Implement RC support.
authorDavid Anderson <dvander@alliedmods.net>
Fri Nov 15 22:20:02 2013 -0800 (2013-11-15)
changeset 190ac8f25935d84
parent 189 5cbcf2a49ddf
child 191 591aacecffa3
Implement RC support.
ambuild2/frontend/amb2/gen.py
ambuild2/frontend/cpp.py
ambuild2/nodetypes.py
ambuild2/task.py
     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):