X-Git-Url: http://git.tremily.us/?a=blobdiff_plain;f=src%2Fengine%2FSCons%2FTool%2Fmsvs.py;h=304c35ea863bc78f9dd3eb40c106076837156ec1;hb=6a218d30e5fa1a14835a31129881b4288db7dc1d;hp=a173c3efa9bb2242343a3611efd2b0a63d762c25;hpb=1ccc0f51dfc96da27704cdc2685c51ab11f42355;p=scons.git diff --git a/src/engine/SCons/Tool/msvs.py b/src/engine/SCons/Tool/msvs.py index a173c3ef..304c35ea 100644 --- a/src/engine/SCons/Tool/msvs.py +++ b/src/engine/SCons/Tool/msvs.py @@ -30,17 +30,20 @@ selection method. # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # +from __future__ import generators ### KEEP FOR COMPATIBILITY FIXERS __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" +import SCons.compat + import base64 -import md5 -import os.path +import hashlib +import ntpath +import os +# compat layer imports "cPickle" for us if it's available. import pickle import re -import string import sys -import types import SCons.Builder import SCons.Node.FS @@ -49,34 +52,48 @@ 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 = s.replace("&", "&") # do this first + s = s.replace("'", "'") + s = s.replace('"', """) + 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.""" - solution = _hexdigest(md5.new(str(slnfile)+str(name)).digest()).upper() + 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)) + solution = m.hexdigest().upper() # 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 @@ -85,8 +102,18 @@ def _generateGUID(slnfile, name): # 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. -exec_script_main = "from os.path import join; import sys; sys.path = [ join(sys.prefix, 'Lib', 'site-packages', 'scons-__VERSION__'), join(sys.prefix, 'scons-__VERSION__'), join(sys.prefix, 'Lib', 'site-packages', 'scons'), join(sys.prefix, 'scons') ] + sys.path; import SCons.Script; SCons.Script.main()" -exec_script_main_xml = string.replace(exec_script_main, "'", "'") +def getExecScriptMain(env, xml=None): + scons_home = env.get('SCONS_HOME') + if not scons_home and 'SCONS_LIB_DIR' in os.environ: + 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 @@ -97,88 +124,164 @@ try: except KeyError: python_executable = sys.executable else: - python_executable = os.path.join('$(PYTHON_ROOT)', + 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 part not in dict: + 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 """ - def __init__(self, dspfile, source, env): - if type(dspfile) == types.StringType: - self.dspfile = os.path.abspath(dspfile) - else: - self.dspfile = dspfile.get_abspath() + srcargs = [ + 'srcs', + 'incs', + 'localincs', + 'resources', + 'misc'] + + def __init__(self, dspfile, source, env): + self.dspfile = str(dspfile) try: - self.conspath = source[0].attributes.sconstruct.get_abspath() - except KeyError: - raise SCons.Errors.InternalError, \ - "Unable to determine where the SConstruct is" - - self.config = Config() - if env.has_key('variant'): - self.config.variant = env['variant'].capitalize() + get_abspath = dspfile.get_abspath + except AttributeError: + self.dspabs = os.path.abspath(dspfile) else: - raise SCons.Errors.InternalError, \ - "You must specify a 'variant' argument (i.e. 'Debug' or " +\ - "'Release') to create an MSVSProject." - - if env.has_key('buildtarget'): - if type(env['buildtarget']) == types.StringType: - self.config.buildtarget = os.path.abspath(env['buildtarget']) - elif type(env['buildtarget']) == types.ListType: - self.config.buildtarget = env['buildtarget'][0].get_abspath() - else: - self.config.buildtarget = env['buildtarget'].get_abspath() + self.dspabs = get_abspath() + + if 'variant' not in env: + 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 'buildtarget' not in env 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 'outdir' not in env 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 'runfile' not in env 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: - raise SCons.Errors.InternalError, \ - "You must specify a target 'buildtarget' file argument (such as the target" +\ - " executable) to create an MSVSProject." + runfile = [env['runfile'].get_abspath()] + if len(runfile) == 1: + s = runfile[0] + runfile = [] + for v in variants: + runfile.append(s) - self.config.outdir = os.path.dirname(self.config.buildtarget) + self.sconscript = env['MSVSSCONSCRIPT'] + + cmdargs = env.get('cmdargs', '') - if type(source[0]) == types.StringType: - self.source = os.path.abspath(source[0]) - else: - self.source = source[0].get_abspath() - self.env = env - if self.env.has_key('name'): + if 'name' in self.env: self.name = self.env['name'] else: self.name = os.path.basename(SCons.Util.splitext(self.dspfile)[0]) - - print "Adding '" + self.name + ' - ' + self.config.variant + "' to Visual Studio Project '" + str(dspfile) + "'" + self.name = self.env.subst(self.name) sourcenames = [ - ' Source Files', + 'Source Files', 'Header Files', 'Local Headers', 'Resource Files', 'Other Files'] - srcargs = [ - 'srcs', - 'incs', - 'localincs', - 'resources', - 'misc'] - self.sources = {} for n in sourcenames: self.sources[n] = [] self.configs = {} - if os.path.exists(self.dspfile): + self.nokeep = 0 + if 'nokeep' in env and env['variant'] != 0: + self.nokeep = 1 + + if self.nokeep == 0 and os.path.exists(self.dspabs): self.Parse() - for t in zip(sourcenames,srcargs): - if self.env.has_key(t[1]): - if type(self.env[t[1]]) == types.ListType: + for t in zip(sourcenames,self.srcargs): + if t[1] in self.env: + 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) @@ -187,43 +290,75 @@ class _DSPGenerator: self.sources[t[0]].append(self.env[t[1]]) for n in sourcenames: - self.sources[n].sort() + self.sources[n].sort(lambda a, b: cmp(a.lower(), b.lower())) + + 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) + "'" - self.configs[self.config.variant] = self.config + 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): - name = self.name # pick a default config - confkeys = self.configs.keys() - confkeys.sort() - - self.file.write('# Microsoft Developer Studio Project File - Name="%s" - Package Owner=<4>\n' - '# Microsoft Developer Studio Generated Build File, Format Version 6.00\n' - '# ** DO NOT EDIT **\n\n' - '# TARGTYPE "Win32 (x86) External Target" 0x0106\n\n' - 'CFG=%s - Win32 %s\n' - '!MESSAGE This is not a valid makefile. To build this project using NMAKE,\n' - '!MESSAGE use the Export Makefile command and run\n' - '!MESSAGE \n' - '!MESSAGE NMAKE /f "%s.mak".\n' - '!MESSAGE \n' - '!MESSAGE You can specify a configuration when running NMAKE\n' - '!MESSAGE by defining the macro CFG on the command line. For example:\n' - '!MESSAGE \n' - '!MESSAGE NMAKE /f "%s.mak" CFG="%s - Win32 %s"\n' - '!MESSAGE \n' - '!MESSAGE Possible choices for configuration are:\n' - '!MESSAGE \n' % (name,name,confkeys[0],name,name,name,confkeys[0])) + confkeys = sorted(self.configs.keys()) + + 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): @@ -234,8 +369,7 @@ class _GenerateV6DSP(_DSPGenerator): '# PROP Scc_LocalPath ""\n\n') first = 1 - confkeys = self.configs.keys() - confkeys.sort() + confkeys = sorted(self.configs.keys()) for kind in confkeys: outdir = self.configs[kind].outdir buildtarget = self.configs[kind].buildtarget @@ -245,27 +379,33 @@ class _GenerateV6DSP(_DSPGenerator): else: self.file.write('\n!ELSEIF "$(CFG)" == "%s - Win32 %s"\n\n' % (name, kind)) + env_has_buildtarget = 'MSVSBUILDTARGET' in self.env + 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 kind.lower().find('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)) - (d,c) = os.path.split(str(self.conspath)) - cmd = '"%s" -c "%s" -C %s -f %s %s' % (python_executable, - exec_script_main, - d, c, buildtarget) - self.file.write('# PROP %sCmd_Line "%s"\n' + 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: @@ -282,32 +422,35 @@ class _GenerateV6DSP(_DSPGenerator): self.PrintSourceFiles() self.file.write('# End Target\n' '# End Project\n') - - # 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') - + + 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', + 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() - cats.sort() + cats.sort(lambda a, b: cmp(a.lower(), b.lower())) for kind in cats: if not self.sources[kind]: continue # skip empty groups - + self.file.write('# Begin Group "' + kind + '"\n\n') - typelist = categories[kind].replace('|',';') + # TODO(1.5) + #typelist = categories[kind].replace('|', ';') + typelist = categories[kind].replace('|', ';') 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' @@ -315,19 +458,21 @@ class _GenerateV6DSP(_DSPGenerator): '# End Source File\n') self.file.write('# End Group\n') - # add the Conscript file outside of the groups + # add the SConscript file outside of the groups self.file.write('# Begin Source File\n\n' - 'SOURCE="' + str(self.source) + '"\n' + 'SOURCE="' + str(self.sconscript) + '"\n' '# End Source File\n') def Parse(self): try: - dspfile = open(self.dspfile,'r') + 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 line.find("# End Project") > -1: break line = dspfile.readline() @@ -367,137 +512,272 @@ class _GenerateV6DSP(_DSPGenerator): return # unable to unpickle any data for some reason self.sources.update(data) - + def Build(self): try: - self.file = open(self.dspfile,'w') + self.file = open(self.dspabs,'w') except IOError, detail: - raise SCons.Errors.InternalError, 'Unable to open "' + self.dspfile + '" for writing:' + str(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 = float(env['MSVS_VERSION']) - self.versionstr = '7.00' - if self.version >= 7.1: - self.versionstr = '7.10' + 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): - self.file.write('\n' - '\n' - ' \n' - ' \n' - ' \n' % (self.versionstr, self.name)) + 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)) - def PrintProject(self): - + self.file.write(self.dspheader % locals()) - self.file.write(' \n') + 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() + confkeys = sorted(self.configs.keys()) 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 = 'MSVSBUILDTARGET' in self.env + 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(';'.join(processDefines(self.env.get('CPPDEFINES', [])))) + includepath = xmlify(';'.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') - (d,c) = os.path.split(str(self.conspath)) - cmd = '"%s" -c "%s" -C %s -f %s %s' % (python_executable, - exec_script_main_xml, - d, c, buildtarget) - - cleancmd = '"%s" -c "%s" -C %s -f %s -c %s' % (python_executable, - exec_script_main_xml, - d, c, buildtarget) - - self.file.write(' \n' - ' \n' - ' \n' % (kind.capitalize(),outdir,outdir,\ - cmd,cleancmd,cmd,buildtarget)) - - self.file.write(' \n') - if self.version >= 7.1: - self.file.write(' \n') - self.file.write(' \n') + if self.version_num >= 7.1: + self.file.write('\t\n' + '\t\n') self.PrintSourceFiles() self.file.write('\n') - # 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') - + 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() + sorteditems.sort(lambda a, b: cmp(a[0].lower(), b[0].lower())) + + # 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', + 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(' \n') - + self.file.write('\t\n') + cats = categories.keys() - cats.sort() + cats.sort(lambda a, b: cmp(a.lower(), b.lower())) + cats = [k for k in cats if self.sources[k]] for kind in cats: - if not self.sources[kind]: - continue # skip empty groups - - self.file.write(' \n' % (kind, categories[kind])) - - for file in self.sources[kind]: - file = os.path.normpath(file) - self.file.write(' \n' - ' \n' % file) - - self.file.write(' \n') - - # add the Conscript file outside of the groups - self.file.write(' \n' - ' \n' - ' \n' - ' \n' - ' \n' % str(self.source)) + 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 = list(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 = [s[len(cp)+1:] for s in 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.dspfile,'r') + 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('