| # |
| # gen_win.py -- base class for generating windows projects |
| # |
| |
| import os |
| import sys |
| import string |
| |
| try: |
| from cStringIO import StringIO |
| except ImportError: |
| from StringIO import StringIO |
| |
| import gen_base |
| import ezt |
| |
| |
| class WinGeneratorBase(gen_base.GeneratorBase): |
| "Base class for all Windows project files generators" |
| |
| _extension_map = { |
| ('exe', 'target'): '.exe', |
| ('exe', 'object'): '.obj', |
| ('lib', 'target'): '.dll', |
| ('lib', 'object'): '.obj', |
| ('script', 'target'): '', |
| ('script', 'object'): '', |
| } |
| |
| envvars={ |
| "$(SVN_APR_LIBS)": ["apr"], |
| "$(SVN_APRUTIL_LIBS)": ["aprutil", "apriconv"], |
| "$(NEON_LIBS)": ["neon"], |
| "$(SVN_DB_LIBS)": [], |
| "$(SVN_XMLRPC_LIBS)": [], |
| "$(SVN_RA_LIB_LINK)": ["libsvn_ra_dav", "libsvn_ra_local", "libsvn_ra_svn"], |
| } |
| |
| def copyfile(self, dest, src): |
| "Copy file to dest from src" |
| |
| open(dest, 'wb').write(open(src, 'rb').read()) |
| |
| def movefile(self, dest, src): |
| "Move file to dest from src if src exists" |
| |
| if os.path.exists(src): |
| open(dest,'wb').write(open(src, 'rb').read()) |
| os.unlink(src) |
| |
| def parse_options(self, options): |
| self.httpd_path = None |
| self.zlib_path = None |
| self.openssl_path = None |
| self.skip_targets = { 'mod_dav_svn': None, |
| 'mod_authz_svn': None } |
| |
| # Instrumentation options |
| self.instrument_apr_pools = None |
| self.instrument_purify_quantify = None |
| |
| for opt, val in options: |
| if opt == '--with-httpd': |
| self.httpd_path = os.path.abspath(val) |
| del self.skip_targets['mod_dav_svn'] |
| del self.skip_targets['mod_authz_svn'] |
| elif opt == '--with-zlib': |
| self.zlib_path = os.path.abspath(val) |
| elif opt == '--with-openssl': |
| self.openssl_path = os.path.abspath(val) |
| elif opt == '--enable-purify': |
| self.instrument_purify_quantify = 1 |
| self.instrument_apr_pools = 1 |
| elif opt == '--enable-quantify': |
| self.instrument_purify_quantify = 1 |
| elif opt == '--enable-pool-debug': |
| self.instrument_apr_pools = 1 |
| |
| def __init__(self, fname, verfname, options, subdir): |
| """ |
| Do some Windows specific setup |
| |
| Copy getdate.c from getdate.cw |
| |
| To avoid some compiler issues, |
| move mod_dav_svn/log.c to mod_dav_svn/davlog.c & |
| move mod_dav_svn/repos.c to mod_dav_svn/davrepos.c |
| |
| Find db-4.0.x or db-4.1.x |
| |
| Configure inno-setup |
| TODO Revisit this, it may not be needed |
| |
| Build the list of Platforms & Configurations & |
| create the necessary paths |
| |
| """ |
| |
| # parse (and save) the options that were passed to us |
| self.parse_options(options) |
| |
| #Find db-4.0.x or db-4.1.x |
| #We translate all slashes to windows format later on |
| db41 = self.search_for("libdb41.lib", ["db4-win32/lib"]) |
| if db41: |
| sys.stderr.write("Found libdb41.lib in %s\n" % db41) |
| self.dblibname = "libdb41" |
| self.dblibpath = db41 |
| else: |
| db40 = self.search_for("libdb40.lib", ["db4-win32/lib"]) |
| if db40: |
| sys.stderr.write("Found libdb40.lib in %s\n" % db40) |
| self.dblibname = "libdb40" |
| self.dblibpath = db40 |
| else: |
| sys.stderr.write("DB not found; assuming db-4.0.X in db4-win32 " |
| "by default\n") |
| self.dblibname = "libdb40" |
| self.dblibpath = os.path.join("db4-win32","lib") |
| self.dbincpath = string.replace(self.dblibpath, "lib", "include") |
| self.dbbindll = "%s//%s.dll" % (string.replace(self.dblibpath, |
| "lib", "bin"), |
| self.dblibname) |
| self.envvars["$(SVN_DB_LIBS)"] = [self.dblibname] |
| |
| # Find the right perl library name to link swig bindings with |
| fp = os.popen('perl -MConfig -e ' + escape_shell_arg( |
| 'print "$Config{revision}$Config{patchlevel}"'), 'r') |
| try: |
| num = fp.readline() |
| if num: |
| self.perl_lib = 'perl' + string.rstrip(num) + '.lib' |
| sys.stderr.write('Found installed perl version number. Perl bindings\n' |
| ' will be linked with %s\n' % self.perl_lib) |
| else: |
| self.perl_lib = 'perl56.lib' |
| sys.stderr.write('Could not detect perl version. Perl bindings will\n' |
| ' be linked with %s\n' % self.perl_lib) |
| finally: |
| fp.close() |
| |
| #Make some files for the installer so that we don't need to require sed or some other command to do it |
| ### GJS: don't do this right now |
| if 0: |
| buf = open(os.path.join("packages","win32-innosetup","svn.iss.in"), 'rb').read() |
| buf = buf.replace("@VERSION@", "0.16.1+").replace("@RELEASE@", "4365") |
| buf = buf.replace("@DBBINDLL@", self.dbbindll) |
| svnissrel = os.path.join("packages","win32-innosetup","svn.iss.release") |
| svnissdeb = os.path.join("packages","win32-innosetup","svn.iss.debug") |
| if self.write_file_if_changed(svnissrel, buf.replace("@CONFIG@", "Release")): |
| print 'Wrote %s' % svnissrel |
| if self.write_file_if_changed(svnissdeb, buf.replace("@CONFIG@", "Debug")): |
| print 'Wrote %s' % svnissdeb |
| |
| # Generate the build_neon.bat file |
| data = {'zlib_path': self.zlib_path, |
| 'openssl_path': self.openssl_path} |
| self.write_with_template(os.path.join('build', 'win32', 'build_neon.bat'), |
| 'build_neon.ezt', data) |
| |
| # gstein wrote: |
| # > we don't want to munge the working copy since we might be |
| # > generating the Windows build files on a Unix box prior to |
| # > release. this copy already occurs in svn_config.dsp. (is that |
| # > broken or something?) |
| # No, but if getdate.c doesn't exist, it won't get pulled into the |
| # libsvn_subr.dsp (or .vcproj or whatever), so it won't get built. |
| getdate_c = os.path.join('subversion', 'libsvn_subr', 'getdate.c') |
| if not os.path.exists(getdate_c): |
| getdate_cw = getdate_c + 'w' |
| print 'Copied', getdate_cw, 'to', getdate_c |
| self.copyfile(getdate_c, getdate_cw) |
| |
| #Initialize parent |
| gen_base.GeneratorBase.__init__(self, fname, verfname) |
| |
| #Make the project files directory if it doesn't exist |
| #TODO win32 might not be the best path as win64 stuff will go here too |
| self.projfilesdir=os.path.join("build","win32",subdir) |
| if not os.path.exists(self.projfilesdir): |
| os.makedirs(self.projfilesdir) |
| |
| #Here we can add additional platforms to compile for |
| self.platforms = ['Win32'] |
| |
| #Here we can add additional modes to compile for |
| self.configs = ['Debug','Release'] |
| |
| #Here we could enable shared libraries |
| self.shared = 0 |
| |
| def search_for(self, name, paths): |
| "Search for the existence of name in paths & return the first path it was found under" |
| for x in paths: |
| x = string.replace(x, "/", os.sep) |
| if os.path.exists(os.path.join(x, name)): |
| return x |
| |
| def subst_win_env(self, s): |
| "Substitute s with a value from envvars if a match was found" |
| |
| if not self.envvars.has_key(s): |
| return [s] |
| |
| a=self.envvars[s] |
| ret=[] |
| for b in a: |
| ret.append(b) |
| return ret |
| |
| def map_rootpath(self, list, rootpath): |
| "Return a list with rootpath prepended" |
| |
| result = [ ] |
| for item in list: |
| result.append(rootpath + '\\' + item) |
| return result |
| |
| def make_windirs(self, list): |
| "Return a list with all the current os slashes replaced with windows slashes" |
| |
| return map(lambda x:string.replace(x, os.sep, '\\'), list) |
| |
| def _find_libs(self, libs_option): |
| "Override the parents _find_libs function so that environment substitution happens first" |
| |
| libs = [ ] |
| for x in string.split(libs_option): |
| for libname in self.subst_win_env(x): |
| if self.targets.has_key(libname): |
| libs.append(self.targets[libname]) |
| else: |
| libs.append(gen_base.ExternalLibrary(libname)) |
| return libs |
| |
| def get_install_targets(self): |
| "Generate the list of targets" |
| # Generate a fake depaprutil project |
| self.targets['depsubr'] = gen_base.TargetUtility('depsubr', None, |
| 'build/win32', |
| None, None, self.cfg, |
| None) |
| self.targets['depdelta'] = gen_base.TargetUtility('depdelta', None, |
| 'build/win32', |
| None, None, self.cfg, |
| None) |
| |
| install_targets = self.targets.values() \ |
| + self.graph.get_all_sources(gen_base.DT_INSTALL) |
| install_targets = gen_base.unique(install_targets) |
| |
| # sort these for output stability, to watch out for regressions. |
| install_targets.sort() |
| return install_targets |
| |
| def get_configs(self, target, rootpath): |
| "Get the list of configurations for the project" |
| configs = [ ] |
| for cfg in self.configs: |
| configs.append( |
| ProjectItem(name=cfg, |
| lower=string.lower(cfg), |
| defines=self.get_win_defines(target, cfg), |
| libdirs=self.get_win_lib_dirs(target,rootpath, cfg), |
| libs=self.get_win_libs(target, cfg), |
| )) |
| return configs |
| |
| def get_proj_sources(self, quote_path, target, rootpath): |
| "Get the list of source files for each project" |
| sources = [ ] |
| if not isinstance(target, gen_base.TargetUtility): |
| for src, reldir in self.get_win_sources(target): |
| rsrc = string.replace(os.path.join(rootpath, src), os.sep, '\\') |
| if quote_path and '-' in rsrc: |
| rsrc = '"%s"' % rsrc |
| sources.append(ProjectItem(path=rsrc, reldir=reldir, user_deps=[], |
| swig_language=None)) |
| |
| if isinstance(target, gen_base.SWIGLibrary): |
| for obj in self.graph.get_sources(gen_base.DT_LINK, target.name): |
| if isinstance(obj, gen_base.SWIGObject): |
| for cobj in self.graph.get_sources(gen_base.DT_OBJECT, obj): |
| if isinstance(cobj, gen_base.SWIGObject): |
| csrc = rootpath + '\\' + string.replace(cobj.fname, '/', '\\') |
| |
| if isinstance(target, gen_base.SWIGRuntimeLibrary): |
| bsrc = rootpath + "\\build\\win32\\gen_swig_runtime.py" |
| sources.append(ProjectItem(path=bsrc, reldir=None, user_deps=[], |
| swig_language=target.lang, |
| swig_target=csrc, swig_output=None)) |
| continue |
| |
| # output path passed to swig has to use forward slashes, |
| # otherwise the generated python files (for shadow |
| # classes) will be saved to the wrong directory |
| cout = string.replace(os.path.join(rootpath, cobj.fname), |
| os.sep, '/') |
| |
| # included header files that the generated c file depends on |
| user_deps = [] |
| |
| for iobj in self.graph.get_sources(gen_base.DT_SWIG_C, cobj): |
| isrc = rootpath + '\\' + string.replace(str(iobj), '/', '\\') |
| |
| if not isinstance(iobj, gen_base.SWIGSource): |
| user_deps.append(isrc) |
| continue |
| |
| sources.append(ProjectItem(path=isrc, reldir=None, |
| user_deps=user_deps, |
| swig_language=target.lang, |
| swig_target=csrc, swig_output=cout)) |
| |
| sources.sort(lambda x, y: cmp(x.path, y.path)) |
| return sources |
| |
| def gen_proj_names(self, install_targets): |
| "Generate project file names for the targets" |
| # Generate project file names for the targets: replace dashes with |
| # underscores and replace *-test with test_* (so that the test |
| # programs are visually separare from the rest of the projects) |
| for target in install_targets: |
| name = target.name |
| pos = string.find(name, '-test') |
| if pos >= 0: |
| proj_name = 'test_' + string.replace(name[:pos], '-', '_') |
| elif isinstance(target, gen_base.SWIGLibrary): |
| proj_name = 'swig_' + string.replace(name, '-', '_') |
| else: |
| proj_name = string.replace(name, '-', '_') |
| target.proj_name = proj_name |
| |
| def adjust_win_depends(self, target, name): |
| "Handle special dependencies if needed" |
| |
| # For MSVC we need to hack around Apache modules & |
| # libsvn_ra because dependencies implies linking |
| # and there is no way around that |
| if name == '__CONFIG__': |
| depends = [] |
| else: |
| depends = [self.targets['__CONFIG__']] |
| |
| if target.is_apache_mod: |
| if target.name == 'mod_authz_svn': |
| depends.append(self.targets['mod_dav_svn']) |
| pass |
| elif name == 'depdelta': |
| depends.append(self.targets['libsvn_delta']) |
| elif name == 'libsvn_wc': |
| depends.append(self.targets['depdelta']) |
| elif name == 'depsubr': |
| depends.append(self.targets['libsvn_subr']) |
| elif name == 'libsvn_ra_svn': |
| depends.append(self.targets['depsubr']) |
| elif name == 'libsvn_ra_dav': |
| depends.append(self.targets['depsubr']) |
| depends.append(self.targets['neon']) |
| elif isinstance(target, gen_base.Target): |
| if isinstance(target, gen_base.TargetExe): |
| deps = { } |
| for obj in self.get_win_depends(target, 0): |
| deps[obj] = None |
| for obj in self.get_win_depends(target, 2): |
| if isinstance(obj, gen_base.TargetLib): |
| deps[obj] = None |
| deps = deps.keys() |
| deps.sort() |
| depends.extend(deps) |
| else: |
| depends.extend(self.get_unique_win_depends(target)) |
| elif isinstance(target, gen_base.SWIGLibrary): |
| for lib in self.graph.get_sources(gen_base.DT_LINK, target.name): |
| if hasattr(lib, 'proj_name'): |
| depends.append(lib) |
| depends.extend(self.get_win_depends(lib, 0)) |
| if not isinstance(target, gen_base.SWIGRuntimeLibrary): |
| runtime = self.targets['swig_runtime'].get_library(target.lang) |
| if runtime: depends.append(runtime) |
| else: |
| assert 0 |
| |
| return depends |
| |
| |
| def get_win_depends(self, target, recurse=0): |
| """ |
| Return the list of dependencies for target not including external libraries |
| If recurse is 0, return just target's dependencies |
| If recurse is 1, return a list of dependencies plus dependencies of dependencies |
| If recurse is 2, only return the dependencies of target's dependencies |
| |
| """ |
| deps = { } |
| |
| for obj in self.graph.get_sources(gen_base.DT_LINK, target.name): |
| if not isinstance(obj, gen_base.Target): |
| continue |
| |
| if recurse != 2: |
| deps[obj] = None |
| |
| if recurse: |
| for dep in self.get_win_depends(obj, 1): |
| deps[dep] = None |
| |
| deps = deps.keys() |
| deps.sort() |
| return deps |
| |
| def get_unique_win_depends(self, target): |
| "Return the list of dependencies for target that are not already depended upon by a child" |
| |
| deps = { } |
| |
| sub = self.get_win_depends(target, 2) |
| |
| for obj in self.graph.get_sources(gen_base.DT_LINK, target.name): |
| if not isinstance(obj, gen_base.Target): |
| continue |
| |
| if isinstance(obj, gen_base.TargetSWIG): |
| tname = obj.install + '-' + gen_base.lang_abbrev[target.language] |
| for dep in self.graph.get_sources(gen_base.DT_INSTALL, tname): |
| deps[dep] = None |
| continue |
| |
| # if the object is in 'sub', then skip it |
| if obj not in sub: |
| deps[obj] = None |
| |
| deps = deps.keys() |
| deps.sort() |
| return deps |
| |
| def get_win_defines(self, target, cfg): |
| "Return the list of defines for target" |
| |
| fakedefines = ["WIN32","_WINDOWS","alloca=_alloca"] |
| if target.is_apache_mod: |
| if target.name == 'mod_dav_svn': |
| fakedefines.extend(["AP_DECLARE_EXPORT"]) |
| pass |
| else: |
| fakedefines.extend(["APR_DECLARE_STATIC","APU_DECLARE_STATIC"]) |
| |
| if isinstance(target, gen_base.SWIGLibrary): |
| fakedefines.append("SWIG_GLOBAL") |
| |
| if cfg == 'Debug': |
| fakedefines.extend(["_DEBUG","SVN_DEBUG"]) |
| elif cfg == 'Release': |
| fakedefines.append("NDEBUG") |
| return fakedefines |
| |
| def get_win_includes(self, target, rootpath): |
| "Return the list of include directories for target" |
| |
| if target.is_apache_mod: |
| fakeincludes = self.map_rootpath(["subversion/include", |
| self.dbincpath, |
| ""], |
| rootpath) |
| fakeincludes.extend([ |
| self.httpd_path + "/srclib/apr/include", |
| self.httpd_path + "/srclib/apr-util/include", |
| self.httpd_path + "/srclib/apr-util/xml/expat/lib", |
| self.httpd_path + "/include" |
| ]) |
| elif isinstance(target, gen_base.SWIGLibrary): |
| fakeincludes = self.map_rootpath(["subversion/bindings/swig", |
| "subversion/include", |
| "apr/include"], rootpath) |
| else: |
| fakeincludes = self.map_rootpath(["subversion/include", |
| "apr/include", |
| "apr-util/include", |
| "apr-util/xml/expat/lib", |
| "neon/src", |
| self.dbincpath, |
| ""], |
| rootpath) |
| |
| return self.make_windirs(fakeincludes) |
| |
| def get_win_lib_dirs(self, target, rootpath, cfg): |
| "Return the list of library directories for target" |
| |
| libcfg = string.replace(string.replace(cfg, "Debug", "LibD"), |
| "Release", "LibR") |
| |
| fakelibdirs = self.map_rootpath([self.dblibpath], rootpath) |
| if target.is_apache_mod: |
| fakelibdirs.extend([ |
| self.httpd_path + "/%s" % cfg, |
| self.httpd_path + "/srclib/apr/%s" % cfg, |
| self.httpd_path + "/srclib/apr-util/%s" % cfg, |
| self.httpd_path + "/srclib/apr-util/xml/expat/lib/%s" % libcfg |
| ]) |
| if target.name == 'mod_dav_svn': |
| fakelibdirs.extend([self.httpd_path + "/modules/dav/main/%s" % cfg]) |
| |
| return self.make_windirs(fakelibdirs) |
| |
| def get_win_libs(self, target, cfg): |
| "Return the list of external libraries needed for target" |
| |
| if target.is_apache_mod: |
| if target.name == 'mod_dav_svn': |
| libs = [ self.dblibname+(cfg == 'Debug' and 'd.lib' or '.lib'), |
| 'mod_dav.lib' ] |
| else: |
| libs = [] |
| libs.extend([ 'xml.lib', |
| 'libapr.lib', |
| 'libaprutil.lib', |
| 'libhttpd.lib', |
| 'mswsock.lib', |
| 'ws2_32.lib', |
| 'advapi32.lib', |
| 'rpcrt4.lib', |
| 'shfolder.lib' ]) |
| return libs |
| |
| if isinstance(target, gen_base.SWIGLibrary): |
| libs = [ self.dblibname+(cfg == 'Debug' and 'd.lib' or '.lib'), |
| 'mswsock.lib', |
| 'ws2_32.lib', |
| 'advapi32.lib', |
| 'rpcrt4.lib', |
| 'shfolder.lib' ] |
| if target.lang == 'perl': |
| libs.append(self.perl_lib) |
| return libs |
| |
| if not isinstance(target, gen_base.TargetExe): |
| return [] |
| |
| nondeplibs = ['setargv.obj'] |
| depends = [target] + self.get_win_depends(target, 1) |
| for dep in depends: |
| for lib in self.graph.get_sources(gen_base.DT_LINK, dep.name): |
| if not isinstance(lib, gen_base.ExternalLibrary): |
| continue |
| |
| if cfg == 'Debug' and lib.fname == self.dblibname: |
| nondeplibs.append(lib.fname+'d.lib') |
| else: |
| nondeplibs.append(lib.fname+'.lib') |
| |
| return nondeplibs |
| |
| def get_win_sources(self, target, reldir_prefix=''): |
| "Return the list of source files that need to be compliled for target" |
| |
| sources = { } |
| |
| if target.is_apache_mod: |
| # get (fname, reldir) pairs for dependent libs |
| for dep_tgt in self.get_win_depends(target, 1): |
| if not isinstance(dep_tgt, gen_base.TargetLib): |
| continue |
| subdir = string.replace(dep_tgt.name, 'libsvn_', '') |
| for src in self.get_win_sources(dep_tgt, subdir): |
| sources[src] = None |
| |
| for obj in self.graph.get_sources(gen_base.DT_LINK, target.name): |
| if isinstance(obj, gen_base.Target): |
| continue |
| |
| for src in self.graph.get_sources(gen_base.DT_OBJECT, obj): |
| if isinstance(src, gen_base.SourceFile): |
| if reldir_prefix: |
| if src.reldir: |
| reldir = reldir_prefix + '\\' + src.reldir |
| else: |
| reldir = reldir_prefix |
| else: |
| reldir = src.reldir |
| else: |
| reldir = '' |
| sources[str(src), reldir] = None |
| |
| return sources.keys() |
| |
| def write_file_if_changed(self, fname, new_contents): |
| """Rewrite the file if new_contents are different than its current content. |
| |
| If you have your windows projects open and generate the projects |
| it's not a small thing for windows to re-read all projects so |
| only update those that have changed. |
| """ |
| |
| try: |
| old_contents = open(fname, 'rb').read() |
| except IOError: |
| old_contents = None |
| if old_contents != new_contents: |
| open(fname, 'wb').write(new_contents) |
| print "Wrote:", fname |
| |
| def write_with_template(self, fname, tname, data): |
| fout = StringIO() |
| |
| template = ezt.Template(compress_whitespace = 0) |
| template.parse_file(os.path.join('build', 'generator', tname)) |
| template.generate(fout, data) |
| |
| self.write_file_if_changed(fname, fout.getvalue()) |
| |
| def write(self): |
| "Override me when creating a new project type" |
| |
| raise NotImplementedError |
| |
| class ProjectItem: |
| "A generic item class for holding sources info, config info, etc for a project" |
| def __init__(self, **kw): |
| vars(self).update(kw) |
| |
| if sys.platform == "win32": |
| def escape_shell_arg(str): |
| return '"' + string.replace(str, '"', '"^""') + '"' |
| else: |
| def escape_shell_arg(str): |
| return "'" + string.replace(str, "'", "'\\''") + "'" |
| |