Implement RC support.
Implement RC support.
1.1 --- a/ambuild2/frontend/amb2/gen.py Fri Nov 15 20:15:05 2013 -0800
1.2 +++ b/ambuild2/frontend/amb2/gen.py Fri Nov 15 22:20:02 2013 -0800
1.3 @@ -211,13 +211,9 @@
1.4 # We let the same folder be generated twice, so use discard, not remove.
1.5 self.old_folders_.discard(entry)
1.6 else:
1.7 - util.con_err(
1.8 - util.ConsoleRed,
1.9 - 'Folder has the same node signature as: ',
1.10 - util.ConsoleBlue,
1.11 - entry.format(),
1.12 - util.ConsoleNormal
1.13 - )
1.14 + util.con_err(util.ConsoleRed, 'Folder has the same node signature as: ',
1.15 + util.ConsoleBlue, entry.format(),
1.16 + util.ConsoleNormal)
1.17 raise Exception('Output has been duplicated: {0}'.format(entry.path))
1.18
1.19 parent = entry
1.20 @@ -481,7 +477,7 @@
1.21 self.parseCxxDeps(cx, binary, inputs, binary.compiler.linkflags)
1.22 self.parseCxxDeps(cx, binary, inputs, binary.compiler.postlink)
1.23
1.24 - for objfile in binary.objfiles:
1.25 + for objfile in binary.objects:
1.26 cxxData = {
1.27 'argv': objfile.argv,
1.28 'type': binary.linker.behavior
1.29 @@ -496,6 +492,21 @@
1.30 data = cxxData
1.31 )
1.32 inputs.append(cxxNode)
1.33 + for rcfile in binary.resources:
1.34 + rcData = {
1.35 + 'cl_argv': rcfile.cl_argv,
1.36 + 'rc_argv': rcfile.rc_argv,
1.37 + }
1.38 + rcCmd, (rcNode,) = self.addCommand(
1.39 + context = cx,
1.40 + weak_inputs = binary.compiler.sourcedeps,
1.41 + inputs = [rcfile.sourceFile],
1.42 + outputs = [rcfile.outputFile],
1.43 + node_type = nodetypes.Rc,
1.44 + folder = folder_node,
1.45 + data = rcData
1.46 + )
1.47 + inputs.append(rcNode)
1.48
1.49 outputs = [binary.outputFile]
1.50 if binary.pdbFile:
2.1 --- a/ambuild2/frontend/cpp.py Fri Nov 15 20:15:05 2013 -0800
2.2 +++ b/ambuild2/frontend/cpp.py Fri Nov 15 22:20:02 2013 -0800
2.3 @@ -33,7 +33,8 @@
2.4 self.definePrefix = '/D'
2.5 self.pdbSuffix = '.pdb'
2.6
2.7 - def formatInclude(self, outputPath, includePath):
2.8 + @staticmethod
2.9 + def IncludePath(outputPath, includePath):
2.10 #Hack - try and get a relative path because CL, with either
2.11 #/Zi or /ZI, combined with subprocess, apparently tries and
2.12 #looks for paths like c:\bleh\"c:\bleh" <-- wtf
2.13 @@ -43,8 +44,14 @@
2.14 outputDrive = os.path.splitdrive(outputPath)[0]
2.15 includeDrive = os.path.splitdrive(includePath)[0]
2.16 if outputDrive == includeDrive:
2.17 - return ['/I', os.path.relpath(includePath, outputPath)]
2.18 - return ['/I', includePath]
2.19 + return os.path.relpath(includePath, outputPath)
2.20 + return includePath
2.21 +
2.22 + def formatInclude(self, outputPath, includePath):
2.23 + return ['/I', self.IncludePath(outputPath, includePath)]
2.24 +
2.25 + def preprocessArgs(self, sourceFile):
2.26 + return ['/showIncludes', '/nologo', '/E', '/c', sourceFile]
2.27
2.28 def objectArgs(self, sourceFile, objFile):
2.29 return ['/showIncludes', '/nologo', '/c', sourceFile, '/Fo' + objFile]
2.30 @@ -246,6 +253,8 @@
2.31 'defines', # C and C++ #defines
2.32 'cxxdefines', # C++-only #defines
2.33
2.34 + 'rcdefines', # Resource Compiler (RC) defines
2.35 +
2.36 # Link flags. If any members are not strings, they will be interpreted as
2.37 # Dep entries created from BinaryBuilder.
2.38 'linkflags',
2.39 @@ -300,12 +309,9 @@
2.40 if compiler == config.cxx:
2.41 for include in config.cxxincludes:
2.42 args += compiler.formatInclude(outputPath, include)
2.43 - self.argv_ = args
2.44 + self.argv = args
2.45 self.compiler = compiler
2.46
2.47 - def argv(self, sourceFile, objPath):
2.48 - return self.argv_ + self.compiler.objectArgs(sourceFile, objPath)
2.49 -
2.50 def NameForObjectFile(file):
2.51 return re.sub('[^a-zA-Z0-9_]+', '_', os.path.splitext(file)[0]);
2.52
2.53 @@ -315,6 +321,13 @@
2.54 self.outputFile = outputFile
2.55 self.argv = argv
2.56
2.57 +class RCFile(object):
2.58 + def __init__(self, sourceFile, outputFile, cl_argv, rc_argv):
2.59 + self.sourceFile = sourceFile
2.60 + self.outputFile = outputFile
2.61 + self.cl_argv = cl_argv
2.62 + self.rc_argv = rc_argv
2.63 +
2.64 def LinkFlags(compiler):
2.65 argv = []
2.66 for item in compiler.linkflags:
2.67 @@ -368,7 +381,50 @@
2.68 self.default_c_env = CCommandEnv(self.outputPath, self.compiler, self.compiler.cc)
2.69 self.default_cxx_env = CCommandEnv(self.outputPath, self.compiler, self.compiler.cxx)
2.70
2.71 - self.objfiles = [self.generateItem(cx, item) for item in self.sources]
2.72 + self.objects = []
2.73 + self.resources = []
2.74 + for item in self.sources:
2.75 + filename, extension = os.path.splitext(item)
2.76 + if extension == '.rc':
2.77 + cenv = self.default_c_env
2.78 + suffix = '.res'
2.79 + else:
2.80 + if extension == '.c':
2.81 + cenv = self.default_c_env
2.82 + else:
2.83 + cenv = self.default_cxx_env
2.84 + self.used_cxx_ = True
2.85 + suffix = cenv.compiler.objSuffix
2.86 +
2.87 + if os.path.isabs(item):
2.88 + sourceFile = item
2.89 + else:
2.90 + sourceFile = os.path.join(cx.currentSourcePath, item)
2.91 + sourceFile = os.path.normpath(sourceFile)
2.92 + objectFile = NameForObjectFile(filename) + suffix
2.93 +
2.94 + if extension == '.rc':
2.95 + # This is only relevant on Windows.
2.96 + vendor = cenv.compiler
2.97 + defines = self.compiler.defines + self.compiler.cxxdefines + self.compiler.rcdefines
2.98 + cl_argv = vendor.command.split(' ')
2.99 + cl_argv += [vendor.definePrefix + define for define in defines]
2.100 + for include in (self.compiler.includes + self.compiler.cxxincludes):
2.101 + cl_argv += vendor.formatInclude(objectFile, include)
2.102 + cl_argv += vendor.preprocessArgs(sourceFile)
2.103 +
2.104 + rc_argv = ['rc', '/nologo']
2.105 + for define in defines:
2.106 + rc_argv.extend(['/d', define])
2.107 + for include in (self.compiler.includes + self.compiler.cxxincludes):
2.108 + rc_argv.extend(['/i', MSVC.IncludePath(objectFile, include)])
2.109 + rc_argv.append('/fo' + objectFile)
2.110 + rc_argv.append(sourceFile)
2.111 +
2.112 + self.resources.append(RCFile(sourceFile, objectFile, cl_argv, rc_argv))
2.113 + else:
2.114 + argv = cenv.argv + cenv.compiler.objectArgs(sourceFile, objectFile)
2.115 + self.objects.append(ObjectFile(sourceFile, objectFile, argv))
2.116
2.117 if not self.linker_:
2.118 if self.used_cxx_:
2.119 @@ -377,69 +433,49 @@
2.120 self.linker_ = self.compiler.cc
2.121
2.122 argv = self.linker_.command.split(' ')
2.123 - for objfile in self.objfiles:
2.124 + for objfile in self.objects:
2.125 argv.append(objfile.outputFile)
2.126 + for rcfile in self.resources:
2.127 + argv.append(rcfile.outputFile)
2.128
2.129 - name, argv = self.generateBinary(cx, argv)
2.130 + argv = self.generateBinary(cx, argv)
2.131
2.132 - self.outputFile = name
2.133 self.argv = argv
2.134 if self.linker_.pdbSuffix:
2.135 self.pdbFile = self.name_ + self.linker_.pdbSuffix
2.136 else:
2.137 self.pdbFile = None
2.138
2.139 - def generateItem(self, cx, item):
2.140 - fparts = os.path.splitext(item)
2.141 -
2.142 - if fparts[1] == 'c':
2.143 - cenv = self.default_c_env
2.144 - else:
2.145 - cenv = self.default_cxx_env
2.146 - self.used_cxx_ = True
2.147 -
2.148 - # Find or add node for the source input file.
2.149 - if os.path.isabs(item):
2.150 - sourceFile = item
2.151 - else:
2.152 - sourceFile = os.path.join(cx.currentSourcePath, item)
2.153 - sourceFile = os.path.normpath(sourceFile)
2.154 - objName = NameForObjectFile(fparts[0]) + cenv.compiler.objSuffix
2.155 - argv = cenv.argv(sourceFile, objName)
2.156 - return ObjectFile(sourceFile, objName, argv)
2.157 -
2.158 class Program(BinaryBuilder):
2.159 def __init__(self, compiler, name):
2.160 super(Program, self).__init__(compiler, name)
2.161 + self.outputFile = name + util.ExecutableSuffix()
2.162
2.163 def generateBinary(self, cx, argv):
2.164 - name = self.name_ + util.ExecutableSuffix()
2.165 -
2.166 if isinstance(self.linker_, MSVC):
2.167 argv.append('/link')
2.168 argv.extend(LinkFlags(self.compiler))
2.169 if isinstance(self.linker_, MSVC):
2.170 - argv.append('/OUT:' + name)
2.171 + argv.append('/OUT:' + self.outputFile)
2.172 argv.append('/DEBUG')
2.173 argv.append('/nologo')
2.174 argv.append('/PDB:"' + self.name_ + '.pdb"')
2.175 else:
2.176 argv.extend(['-o', name])
2.177
2.178 - return name, argv
2.179 + return argv
2.180
2.181 class Library(BinaryBuilder):
2.182 def __init__(self, compiler, name):
2.183 super(Library, self).__init__(compiler, name)
2.184 + self.outputFile = name + util.SharedLibSuffix()
2.185
2.186 def generateBinary(self, cx, argv):
2.187 - name = self.name_ + util.SharedLibSuffix()
2.188 -
2.189 if isinstance(self.linker_, MSVC):
2.190 argv.append('/link')
2.191 argv.extend(LinkFlags(self.compiler))
2.192 if isinstance(self.linker_, MSVC):
2.193 - argv.append('/OUT:' + name)
2.194 + argv.append('/OUT:' + self.outputFile)
2.195 argv.append('/DEBUG')
2.196 argv.append('/nologo')
2.197 argv.append('/DLL')
2.198 @@ -451,4 +487,4 @@
2.199 argv.append('-shared')
2.200 argv.extend(['-o', name])
2.201
2.202 - return name, argv
2.203 + return argv
3.1 --- a/ambuild2/nodetypes.py Fri Nov 15 20:15:05 2013 -0800
3.2 +++ b/ambuild2/nodetypes.py Fri Nov 15 22:20:02 2013 -0800
3.3 @@ -58,6 +58,10 @@
3.4 # are used when using AMBuild2's automated C++ builders.
3.5 Cxx = 'cxx'
3.6
3.7 +# RC nodes are similar to C++ nodes, but are specific for .rc files built with
3.8 +# rc.exe on Windows.
3.9 +Rc = 'rc'
3.10 +
3.11 NodeNames = {
3.12 Source: 'source',
3.13 Command: 'command',
3.14 @@ -67,7 +71,8 @@
3.15 Copy: 'copy',
3.16 CopyFolder: 'copy -R',
3.17 Symlink: 'symlink',
3.18 - Cxx: 'c++'
3.19 + Cxx: 'c++',
3.20 + Rc: 'rc'
3.21 }
3.22
3.23 def IsFile(type):
3.24 @@ -157,6 +162,8 @@
3.25 return 'cp "{0}" "{1}"'.format(self.blob[0], os.path.join(self.folder_name, self.blob[1]))
3.26 if self.type == Cxx:
3.27 return '[' + self.blob['type'] + ']' + ' -> ' + (' '.join([arg for arg in self.blob['argv']]))
3.28 + if self.type == Rc:
3.29 + return ' '.join([arg for arg in self.blob['cl_argv']]) + ' && ' + ' '.join([arg for arg in self.blob['rc_argv']])
3.30 if self.type == Group:
3.31 return 'group "{0}"'.format(self.path)
3.32 return (' '.join([arg for arg in self.blob]))
4.1 --- a/ambuild2/task.py Fri Nov 15 20:15:05 2013 -0800
4.2 +++ b/ambuild2/task.py Fri Nov 15 22:20:02 2013 -0800
4.3 @@ -56,6 +56,7 @@
4.4 'cmd': lambda message: self.doCommand(message),
4.5 'ln': lambda message: self.doSymlink(message),
4.6 'cp': lambda message: self.doCopy(message),
4.7 + 'rc': lambda message: self.doResource(message),
4.8 }
4.9
4.10 self.channel.send({
4.11 @@ -167,6 +168,27 @@
4.12 }
4.13 return reply
4.14
4.15 + # Adjusts any dependencies relative to the current folder, to be relative to
4.16 + # the output folder instead.
4.17 + def rewriteDeps(self, deps):
4.18 + paths = []
4.19 + for inc_path in deps:
4.20 + if not os.path.isabs(inc_path):
4.21 + inc_path = os.path.abspath(inc_path)
4.22 +
4.23 + # Detect whether the include is within the build folder or not.
4.24 + build_path = self.buildPath
4.25 + if build_path[-1] != '/':
4.26 + build_path += '/'
4.27 + prefix = os.path.commonprefix([build_path, inc_path])
4.28 + if prefix == build_path:
4.29 + # The include is not a system include, i.e. it was generated, so
4.30 + # rewrite the path to be relative to the build folder.
4.31 + inc_path = os.path.relpath(inc_path, self.buildPath)
4.32 +
4.33 + paths.append(inc_path)
4.34 + return paths
4.35 +
4.36 def doCompile(self, message):
4.37 task_folder = message['task_folder']
4.38 task_data = message['task_data']
4.39 @@ -183,25 +205,8 @@
4.40 err, deps = util.ParseSunDeps(err)
4.41 else:
4.42 raise Exception('unknown compiler type')
4.43 -
4.44 - # Adjust any dependencies relative to the current folder, to be relative
4.45 - # to the output folder instead.
4.46 - paths = []
4.47 - for inc_path in deps:
4.48 - if not os.path.isabs(inc_path):
4.49 - inc_path = os.path.abspath(inc_path)
4.50
4.51 - # Detect whether the include is within the build folder or not.
4.52 - build_path = self.buildPath
4.53 - if build_path[-1] != '/':
4.54 - build_path += '/'
4.55 - prefix = os.path.commonprefix([build_path, inc_path])
4.56 - if prefix == build_path:
4.57 - # The include is not a system include, i.e. it was generated, so
4.58 - # rewrite the path to be relative to the build folder.
4.59 - inc_path = os.path.relpath(inc_path, self.buildPath)
4.60 -
4.61 - paths.append(inc_path)
4.62 + paths = self.rewriteDeps(deps)
4.63
4.64 reply = {
4.65 'ok': p.returncode == 0,
4.66 @@ -212,6 +217,31 @@
4.67 }
4.68 return reply
4.69
4.70 + def doResource(self, message):
4.71 + task_folder = message['task_folder']
4.72 + task_data = message['task_data']
4.73 + cl_argv = task_data['cl_argv']
4.74 + rc_argv = task_data['rc_argv']
4.75 +
4.76 + with util.FolderChanger(task_folder):
4.77 + # Includes go to stderr when we preprocess to stdout.
4.78 + p, out, err = util.Execute(cl_argv)
4.79 + ignore, deps = util.ParseMSVCDeps(err)
4.80 + paths = self.rewriteDeps(deps)
4.81 +
4.82 + if p.returncode == 0:
4.83 + p, out, err = util.Execute(rc_argv)
4.84 +
4.85 + reply = {
4.86 + 'ok': p.returncode == 0,
4.87 + 'cmdline': (' '.join([arg for arg in cl_argv]) + ' '.join([arg for arg in rc_argv])),
4.88 + 'stdout': out,
4.89 + 'stderr': err,
4.90 + 'deps': paths,
4.91 + }
4.92 + return reply
4.93 +
4.94 +
4.95 # The WorkerParent is in the same process as the TaskMasterChild.
4.96 class WorkerParent(ParentProcessListener):
4.97 def __init__(self, taskMaster):