From 9b73d2781acfc322319eb5c59b30f2dfa0fea977 Mon Sep 17 00:00:00 2001 From: Luca Falavigna Date: Sat, 2 Jan 2010 20:31:23 +0100 Subject: Imported Upstream version 1.2.0.d20091224 --- engine/SCons/Tool/msvs.py | 1439 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1439 insertions(+) create mode 100644 engine/SCons/Tool/msvs.py (limited to 'engine/SCons/Tool/msvs.py') diff --git a/engine/SCons/Tool/msvs.py b/engine/SCons/Tool/msvs.py new file mode 100644 index 0000000..c042d68 --- /dev/null +++ b/engine/SCons/Tool/msvs.py @@ -0,0 +1,1439 @@ +"""SCons.Tool.msvs + +Tool-specific initialization for Microsoft Visual Studio project files. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "src/engine/SCons/Tool/msvs.py 4577 2009/12/27 19:43:56 scons" + +import base64 +import hashlib +import ntpath +import os +import pickle +import re +import string +import sys + +import SCons.Builder +import SCons.Node.FS +import SCons.Platform.win32 +import SCons.Script.SConscript +import SCons.Util +import SCons.Warnings + +from MSCommon import msvc_exists, msvc_setup_env_once +from SCons.Defaults import processDefines + +############################################################################## +# Below here are the classes and functions for generation of +# DSP/DSW/SLN/VCPROJ files. +############################################################################## + +def _hexdigest(s): + """Return a string as a string of hex characters. + """ + # NOTE: This routine is a method in the Python 2.0 interface + # of the native md5 module, but we want SCons to operate all + # the way back to at least Python 1.5.2, which doesn't have it. + h = string.hexdigits + r = '' + for c in s: + i = ord(c) + r = r + h[(i >> 4) & 0xF] + h[i & 0xF] + return r + +def xmlify(s): + s = string.replace(s, "&", "&") # do this first + s = string.replace(s, "'", "'") + s = string.replace(s, '"', """) + return s + +external_makefile_guid = '{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}' + +def _generateGUID(slnfile, name): + """This generates a dummy GUID for the sln file to use. It is + based on the MD5 signatures of the sln filename plus the name of + the project. It basically just needs to be unique, and not + change with each invocation.""" + m = hashlib.md5() + # Normalize the slnfile path to a Windows path (\ separators) so + # the generated file has a consistent GUID even if we generate + # it on a non-Windows platform. + m.update(ntpath.normpath(str(slnfile)) + str(name)) + # TODO(1.5) + #solution = m.hexdigest().upper() + solution = string.upper(_hexdigest(m.digest())) + # convert most of the signature to GUID form (discard the rest) + solution = "{" + solution[:8] + "-" + solution[8:12] + "-" + solution[12:16] + "-" + solution[16:20] + "-" + solution[20:32] + "}" + return solution + +version_re = re.compile(r'(\d+\.\d+)(.*)') + +def msvs_parse_version(s): + """ + Split a Visual Studio version, which may in fact be something like + '7.0Exp', into is version number (returned as a float) and trailing + "suite" portion. + """ + num, suite = version_re.match(s).groups() + return float(num), suite + +# This is how we re-invoke SCons from inside MSVS Project files. +# The problem is that we might have been invoked as either scons.bat +# or scons.py. If we were invoked directly as scons.py, then we could +# use sys.argv[0] to find the SCons "executable," but that doesn't work +# if we were invoked as scons.bat, which uses "python -c" to execute +# things and ends up with "-c" as sys.argv[0]. Consequently, we have +# the MSVS Project file invoke SCons the same way that scons.bat does, +# which works regardless of how we were invoked. +def getExecScriptMain(env, xml=None): + scons_home = env.get('SCONS_HOME') + if not scons_home and os.environ.has_key('SCONS_LIB_DIR'): + scons_home = os.environ['SCONS_LIB_DIR'] + if scons_home: + exec_script_main = "from os.path import join; import sys; sys.path = [ r'%s' ] + sys.path; import SCons.Script; SCons.Script.main()" % scons_home + else: + version = SCons.__version__ + exec_script_main = "from os.path import join; import sys; sys.path = [ join(sys.prefix, 'Lib', 'site-packages', 'scons-%(version)s'), join(sys.prefix, 'scons-%(version)s'), join(sys.prefix, 'Lib', 'site-packages', 'scons'), join(sys.prefix, 'scons') ] + sys.path; import SCons.Script; SCons.Script.main()" % locals() + if xml: + exec_script_main = xmlify(exec_script_main) + return exec_script_main + +# The string for the Python executable we tell the Project file to use +# is either sys.executable or, if an external PYTHON_ROOT environment +# variable exists, $(PYTHON)ROOT\\python.exe (generalized a little to +# pluck the actual executable name from sys.executable). +try: + python_root = os.environ['PYTHON_ROOT'] +except KeyError: + python_executable = sys.executable +else: + python_executable = os.path.join('$$(PYTHON_ROOT)', + os.path.split(sys.executable)[1]) + +class Config: + pass + +def splitFully(path): + dir, base = os.path.split(path) + if dir and dir != '' and dir != path: + return splitFully(dir)+[base] + if base == '': + return [] + return [base] + +def makeHierarchy(sources): + '''Break a list of files into a hierarchy; for each value, if it is a string, + then it is a file. If it is a dictionary, it is a folder. The string is + the original path of the file.''' + + hierarchy = {} + for file in sources: + path = splitFully(file) + if len(path): + dict = hierarchy + for part in path[:-1]: + if not dict.has_key(part): + dict[part] = {} + dict = dict[part] + dict[path[-1]] = file + #else: + # print 'Warning: failed to decompose path for '+str(file) + return hierarchy + +class _DSPGenerator: + """ Base class for DSP generators """ + + srcargs = [ + 'srcs', + 'incs', + 'localincs', + 'resources', + 'misc'] + + def __init__(self, dspfile, source, env): + self.dspfile = str(dspfile) + try: + get_abspath = dspfile.get_abspath + except AttributeError: + self.dspabs = os.path.abspath(dspfile) + else: + self.dspabs = get_abspath() + + if not env.has_key('variant'): + raise SCons.Errors.InternalError, \ + "You must specify a 'variant' argument (i.e. 'Debug' or " +\ + "'Release') to create an MSVSProject." + elif SCons.Util.is_String(env['variant']): + variants = [env['variant']] + elif SCons.Util.is_List(env['variant']): + variants = env['variant'] + + if not env.has_key('buildtarget') or env['buildtarget'] == None: + buildtarget = [''] + elif SCons.Util.is_String(env['buildtarget']): + buildtarget = [env['buildtarget']] + elif SCons.Util.is_List(env['buildtarget']): + if len(env['buildtarget']) != len(variants): + raise SCons.Errors.InternalError, \ + "Sizes of 'buildtarget' and 'variant' lists must be the same." + buildtarget = [] + for bt in env['buildtarget']: + if SCons.Util.is_String(bt): + buildtarget.append(bt) + else: + buildtarget.append(bt.get_abspath()) + else: + buildtarget = [env['buildtarget'].get_abspath()] + if len(buildtarget) == 1: + bt = buildtarget[0] + buildtarget = [] + for _ in variants: + buildtarget.append(bt) + + if not env.has_key('outdir') or env['outdir'] == None: + outdir = [''] + elif SCons.Util.is_String(env['outdir']): + outdir = [env['outdir']] + elif SCons.Util.is_List(env['outdir']): + if len(env['outdir']) != len(variants): + raise SCons.Errors.InternalError, \ + "Sizes of 'outdir' and 'variant' lists must be the same." + outdir = [] + for s in env['outdir']: + if SCons.Util.is_String(s): + outdir.append(s) + else: + outdir.append(s.get_abspath()) + else: + outdir = [env['outdir'].get_abspath()] + if len(outdir) == 1: + s = outdir[0] + outdir = [] + for v in variants: + outdir.append(s) + + if not env.has_key('runfile') or env['runfile'] == None: + runfile = buildtarget[-1:] + elif SCons.Util.is_String(env['runfile']): + runfile = [env['runfile']] + elif SCons.Util.is_List(env['runfile']): + if len(env['runfile']) != len(variants): + raise SCons.Errors.InternalError, \ + "Sizes of 'runfile' and 'variant' lists must be the same." + runfile = [] + for s in env['runfile']: + if SCons.Util.is_String(s): + runfile.append(s) + else: + runfile.append(s.get_abspath()) + else: + runfile = [env['runfile'].get_abspath()] + if len(runfile) == 1: + s = runfile[0] + runfile = [] + for v in variants: + runfile.append(s) + + self.sconscript = env['MSVSSCONSCRIPT'] + + cmdargs = env.get('cmdargs', '') + + self.env = env + + if self.env.has_key('name'): + self.name = self.env['name'] + else: + self.name = os.path.basename(SCons.Util.splitext(self.dspfile)[0]) + self.name = self.env.subst(self.name) + + sourcenames = [ + 'Source Files', + 'Header Files', + 'Local Headers', + 'Resource Files', + 'Other Files'] + + self.sources = {} + for n in sourcenames: + self.sources[n] = [] + + self.configs = {} + + self.nokeep = 0 + if env.has_key('nokeep') and env['variant'] != 0: + self.nokeep = 1 + + if self.nokeep == 0 and os.path.exists(self.dspabs): + self.Parse() + + for t in zip(sourcenames,self.srcargs): + if self.env.has_key(t[1]): + if SCons.Util.is_List(self.env[t[1]]): + for i in self.env[t[1]]: + if not i in self.sources[t[0]]: + self.sources[t[0]].append(i) + else: + if not self.env[t[1]] in self.sources[t[0]]: + self.sources[t[0]].append(self.env[t[1]]) + + for n in sourcenames: + # TODO(1.5): + #self.sources[n].sort(lambda a, b: cmp(a.lower(), b.lower())) + self.sources[n].sort(lambda a, b: cmp(string.lower(a), string.lower(b))) + + def AddConfig(self, variant, buildtarget, outdir, runfile, cmdargs, dspfile=dspfile): + config = Config() + config.buildtarget = buildtarget + config.outdir = outdir + config.cmdargs = cmdargs + config.runfile = runfile + + match = re.match('(.*)\|(.*)', variant) + if match: + config.variant = match.group(1) + config.platform = match.group(2) + else: + config.variant = variant + config.platform = 'Win32' + + self.configs[variant] = config + print "Adding '" + self.name + ' - ' + config.variant + '|' + config.platform + "' to '" + str(dspfile) + "'" + + for i in range(len(variants)): + AddConfig(self, variants[i], buildtarget[i], outdir[i], runfile[i], cmdargs) + + self.platforms = [] + for key in self.configs.keys(): + platform = self.configs[key].platform + if not platform in self.platforms: + self.platforms.append(platform) + + def Build(self): + pass + +V6DSPHeader = """\ +# Microsoft Developer Studio Project File - Name="%(name)s" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) External Target" 0x0106 + +CFG=%(name)s - Win32 %(confkey)s +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "%(name)s.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "%(name)s.mak" CFG="%(name)s - Win32 %(confkey)s" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +""" + +class _GenerateV6DSP(_DSPGenerator): + """Generates a Project file for MSVS 6.0""" + + def PrintHeader(self): + # pick a default config + confkeys = self.configs.keys() + confkeys.sort() + + name = self.name + confkey = confkeys[0] + + self.file.write(V6DSPHeader % locals()) + + for kind in confkeys: + self.file.write('!MESSAGE "%s - Win32 %s" (based on "Win32 (x86) External Target")\n' % (name, kind)) + + self.file.write('!MESSAGE \n\n') + + def PrintProject(self): + name = self.name + self.file.write('# Begin Project\n' + '# PROP AllowPerConfigDependencies 0\n' + '# PROP Scc_ProjName ""\n' + '# PROP Scc_LocalPath ""\n\n') + + first = 1 + confkeys = self.configs.keys() + confkeys.sort() + for kind in confkeys: + outdir = self.configs[kind].outdir + buildtarget = self.configs[kind].buildtarget + if first == 1: + self.file.write('!IF "$(CFG)" == "%s - Win32 %s"\n\n' % (name, kind)) + first = 0 + else: + self.file.write('\n!ELSEIF "$(CFG)" == "%s - Win32 %s"\n\n' % (name, kind)) + + env_has_buildtarget = self.env.has_key('MSVSBUILDTARGET') + if not env_has_buildtarget: + self.env['MSVSBUILDTARGET'] = buildtarget + + # have to write this twice, once with the BASE settings, and once without + for base in ("BASE ",""): + self.file.write('# PROP %sUse_MFC 0\n' + '# PROP %sUse_Debug_Libraries ' % (base, base)) + # TODO(1.5): + #if kind.lower().find('debug') < 0: + if string.find(string.lower(kind), 'debug') < 0: + self.file.write('0\n') + else: + self.file.write('1\n') + self.file.write('# PROP %sOutput_Dir "%s"\n' + '# PROP %sIntermediate_Dir "%s"\n' % (base,outdir,base,outdir)) + cmd = 'echo Starting SCons && ' + self.env.subst('$MSVSBUILDCOM', 1) + self.file.write('# PROP %sCmd_Line "%s"\n' + '# PROP %sRebuild_Opt "-c && %s"\n' + '# PROP %sTarget_File "%s"\n' + '# PROP %sBsc_Name ""\n' + '# PROP %sTarget_Dir ""\n'\ + %(base,cmd,base,cmd,base,buildtarget,base,base)) + + if not env_has_buildtarget: + del self.env['MSVSBUILDTARGET'] + + self.file.write('\n!ENDIF\n\n' + '# Begin Target\n\n') + for kind in confkeys: + self.file.write('# Name "%s - Win32 %s"\n' % (name,kind)) + self.file.write('\n') + first = 0 + for kind in confkeys: + if first == 0: + self.file.write('!IF "$(CFG)" == "%s - Win32 %s"\n\n' % (name,kind)) + first = 1 + else: + self.file.write('!ELSEIF "$(CFG)" == "%s - Win32 %s"\n\n' % (name,kind)) + self.file.write('!ENDIF \n\n') + self.PrintSourceFiles() + self.file.write('# End Target\n' + '# End Project\n') + + if self.nokeep == 0: + # now we pickle some data and add it to the file -- MSDEV will ignore it. + pdata = pickle.dumps(self.configs,1) + pdata = base64.encodestring(pdata) + self.file.write(pdata + '\n') + pdata = pickle.dumps(self.sources,1) + pdata = base64.encodestring(pdata) + self.file.write(pdata + '\n') + + def PrintSourceFiles(self): + categories = {'Source Files': 'cpp|c|cxx|l|y|def|odl|idl|hpj|bat', + 'Header Files': 'h|hpp|hxx|hm|inl', + 'Local Headers': 'h|hpp|hxx|hm|inl', + 'Resource Files': 'r|rc|ico|cur|bmp|dlg|rc2|rct|bin|cnt|rtf|gif|jpg|jpeg|jpe', + 'Other Files': ''} + + cats = categories.keys() + # TODO(1.5): + #cats.sort(lambda a, b: cmp(a.lower(), b.lower())) + cats.sort(lambda a, b: cmp(string.lower(a), string.lower(b))) + for kind in cats: + if not self.sources[kind]: + continue # skip empty groups + + self.file.write('# Begin Group "' + kind + '"\n\n') + # TODO(1.5) + #typelist = categories[kind].replace('|', ';') + typelist = string.replace(categories[kind], '|', ';') + self.file.write('# PROP Default_Filter "' + typelist + '"\n') + + for file in self.sources[kind]: + file = os.path.normpath(file) + self.file.write('# Begin Source File\n\n' + 'SOURCE="' + file + '"\n' + '# End Source File\n') + self.file.write('# End Group\n') + + # add the SConscript file outside of the groups + self.file.write('# Begin Source File\n\n' + 'SOURCE="' + str(self.sconscript) + '"\n' + '# End Source File\n') + + def Parse(self): + try: + dspfile = open(self.dspabs,'r') + except IOError: + return # doesn't exist yet, so can't add anything to configs. + + line = dspfile.readline() + while line: + # TODO(1.5): + #if line.find("# End Project") > -1: + if string.find(line, "# End Project") > -1: + break + line = dspfile.readline() + + line = dspfile.readline() + datas = line + while line and line != '\n': + line = dspfile.readline() + datas = datas + line + + # OK, we've found our little pickled cache of data. + try: + datas = base64.decodestring(datas) + data = pickle.loads(datas) + except KeyboardInterrupt: + raise + except: + return # unable to unpickle any data for some reason + + self.configs.update(data) + + data = None + line = dspfile.readline() + datas = line + while line and line != '\n': + line = dspfile.readline() + datas = datas + line + + # OK, we've found our little pickled cache of data. + # it has a "# " in front of it, so we strip that. + try: + datas = base64.decodestring(datas) + data = pickle.loads(datas) + except KeyboardInterrupt: + raise + except: + return # unable to unpickle any data for some reason + + self.sources.update(data) + + def Build(self): + try: + self.file = open(self.dspabs,'w') + except IOError, detail: + raise SCons.Errors.InternalError, 'Unable to open "' + self.dspabs + '" for writing:' + str(detail) + else: + self.PrintHeader() + self.PrintProject() + self.file.close() + +V7DSPHeader = """\ + + +""" + +V7DSPConfiguration = """\ +\t\t +\t\t\t +\t\t +""" + +V8DSPHeader = """\ + + +""" + +V8DSPConfiguration = """\ +\t\t +\t\t\t +\t\t +""" +class _GenerateV7DSP(_DSPGenerator): + """Generates a Project file for MSVS .NET""" + + def __init__(self, dspfile, source, env): + _DSPGenerator.__init__(self, dspfile, source, env) + self.version = env['MSVS_VERSION'] + self.version_num, self.suite = msvs_parse_version(self.version) + if self.version_num >= 8.0: + self.versionstr = '8.00' + self.dspheader = V8DSPHeader + self.dspconfiguration = V8DSPConfiguration + else: + if self.version_num >= 7.1: + self.versionstr = '7.10' + else: + self.versionstr = '7.00' + self.dspheader = V7DSPHeader + self.dspconfiguration = V7DSPConfiguration + self.file = None + + def PrintHeader(self): + env = self.env + versionstr = self.versionstr + name = self.name + encoding = self.env.subst('$MSVSENCODING') + scc_provider = env.get('MSVS_SCC_PROVIDER', '') + scc_project_name = env.get('MSVS_SCC_PROJECT_NAME', '') + scc_aux_path = env.get('MSVS_SCC_AUX_PATH', '') + scc_local_path = env.get('MSVS_SCC_LOCAL_PATH', '') + project_guid = env.get('MSVS_PROJECT_GUID', '') + if self.version_num >= 8.0 and not project_guid: + project_guid = _generateGUID(self.dspfile, '') + if scc_provider != '': + scc_attrs = ('\tProjectGUID="%s"\n' + '\tSccProjectName="%s"\n' + '\tSccAuxPath="%s"\n' + '\tSccLocalPath="%s"\n' + '\tSccProvider="%s"' % (project_guid, scc_project_name, scc_aux_path, scc_local_path, scc_provider)) + else: + scc_attrs = ('\tProjectGUID="%s"\n' + '\tSccProjectName="%s"\n' + '\tSccLocalPath="%s"' % (project_guid, scc_project_name, scc_local_path)) + + self.file.write(self.dspheader % locals()) + + self.file.write('\t\n') + for platform in self.platforms: + self.file.write( + '\t\t\n' % platform) + self.file.write('\t\n') + + if self.version_num >= 8.0: + self.file.write('\t\n' + '\t\n') + + def PrintProject(self): + self.file.write('\t\n') + + confkeys = self.configs.keys() + confkeys.sort() + for kind in confkeys: + variant = self.configs[kind].variant + platform = self.configs[kind].platform + outdir = self.configs[kind].outdir + buildtarget = self.configs[kind].buildtarget + runfile = self.configs[kind].runfile + cmdargs = self.configs[kind].cmdargs + + env_has_buildtarget = self.env.has_key('MSVSBUILDTARGET') + if not env_has_buildtarget: + self.env['MSVSBUILDTARGET'] = buildtarget + + starting = 'echo Starting SCons && ' + if cmdargs: + cmdargs = ' ' + cmdargs + else: + cmdargs = '' + buildcmd = xmlify(starting + self.env.subst('$MSVSBUILDCOM', 1) + cmdargs) + rebuildcmd = xmlify(starting + self.env.subst('$MSVSREBUILDCOM', 1) + cmdargs) + cleancmd = xmlify(starting + self.env.subst('$MSVSCLEANCOM', 1) + cmdargs) + + # TODO(1.5) + #preprocdefs = xmlify(';'.join(self.env.get('CPPDEFINES', []))) + #includepath = xmlify(';'.join(self.env.get('CPPPATH', []))) + preprocdefs = xmlify(string.join(processDefines(self.env.get('CPPDEFINES', [])), ';')) + includepath = xmlify(string.join(self.env.get('CPPPATH', []), ';')) + + if not env_has_buildtarget: + del self.env['MSVSBUILDTARGET'] + + self.file.write(self.dspconfiguration % locals()) + + self.file.write('\t\n') + + if self.version_num >= 7.1: + self.file.write('\t\n' + '\t\n') + + self.PrintSourceFiles() + + self.file.write('\n') + + if self.nokeep == 0: + # now we pickle some data and add it to the file -- MSDEV will ignore it. + pdata = pickle.dumps(self.configs,1) + pdata = base64.encodestring(pdata) + self.file.write('\n') + + def printSources(self, hierarchy, commonprefix): + sorteditems = hierarchy.items() + # TODO(1.5): + #sorteditems.sort(lambda a, b: cmp(a[0].lower(), b[0].lower())) + sorteditems.sort(lambda a, b: cmp(string.lower(a[0]), string.lower(b[0]))) + + # First folders, then files + for key, value in sorteditems: + if SCons.Util.is_Dict(value): + self.file.write('\t\t\t\n' % (key)) + self.printSources(value, commonprefix) + self.file.write('\t\t\t\n') + + for key, value in sorteditems: + if SCons.Util.is_String(value): + file = value + if commonprefix: + file = os.path.join(commonprefix, value) + file = os.path.normpath(file) + self.file.write('\t\t\t\n' + '\t\t\t\n' % (file)) + + def PrintSourceFiles(self): + categories = {'Source Files': 'cpp;c;cxx;l;y;def;odl;idl;hpj;bat', + 'Header Files': 'h;hpp;hxx;hm;inl', + 'Local Headers': 'h;hpp;hxx;hm;inl', + 'Resource Files': 'r;rc;ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe', + 'Other Files': ''} + + self.file.write('\t\n') + + cats = categories.keys() + # TODO(1.5) + #cats.sort(lambda a, b: cmp(a.lower(), b.lower())) + cats.sort(lambda a, b: cmp(string.lower(a), string.lower(b))) + cats = filter(lambda k, s=self: s.sources[k], cats) + for kind in cats: + if len(cats) > 1: + self.file.write('\t\t\n' % (kind, categories[kind])) + + sources = self.sources[kind] + + # First remove any common prefix + commonprefix = None + if len(sources) > 1: + s = map(os.path.normpath, sources) + # take the dirname because the prefix may include parts + # of the filenames (e.g. if you have 'dir\abcd' and + # 'dir\acde' then the cp will be 'dir\a' ) + cp = os.path.dirname( os.path.commonprefix(s) ) + if cp and s[0][len(cp)] == os.sep: + # +1 because the filename starts after the separator + sources = map(lambda s, l=len(cp)+1: s[l:], sources) + commonprefix = cp + elif len(sources) == 1: + commonprefix = os.path.dirname( sources[0] ) + sources[0] = os.path.basename( sources[0] ) + + hierarchy = makeHierarchy(sources) + self.printSources(hierarchy, commonprefix=commonprefix) + + if len(cats)>1: + self.file.write('\t\t\n') + + # add the SConscript file outside of the groups + self.file.write('\t\t\n' + '\t\t\n' % str(self.sconscript)) + + self.file.write('\t\n' + '\t\n' + '\t\n') + + def Parse(self): + try: + dspfile = open(self.dspabs,'r') + except IOError: + return # doesn't exist yet, so can't add anything to configs. + + line = dspfile.readline() + while line: + # TODO(1.5) + #if line.find('