You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

295 lines
12 KiB

6 years ago
  1. """engine.SCons.Tool.msvc
  2. Tool-specific initialization for Microsoft Visual C/C++.
  3. There normally shouldn't be any need to import this module directly.
  4. It will usually be imported through the generic SCons.Tool.Tool()
  5. selection method.
  6. """
  7. #
  8. # Copyright (c) 2001 - 2017 The SCons Foundation
  9. #
  10. # Permission is hereby granted, free of charge, to any person obtaining
  11. # a copy of this software and associated documentation files (the
  12. # "Software"), to deal in the Software without restriction, including
  13. # without limitation the rights to use, copy, modify, merge, publish,
  14. # distribute, sublicense, and/or sell copies of the Software, and to
  15. # permit persons to whom the Software is furnished to do so, subject to
  16. # the following conditions:
  17. #
  18. # The above copyright notice and this permission notice shall be included
  19. # in all copies or substantial portions of the Software.
  20. #
  21. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
  22. # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  23. # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  24. # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  25. # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  26. # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  27. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  28. #
  29. __revision__ = "src/engine/SCons/Tool/msvc.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog"
  30. import os.path
  31. import re
  32. import sys
  33. import SCons.Action
  34. import SCons.Builder
  35. import SCons.Errors
  36. import SCons.Platform.win32
  37. import SCons.Tool
  38. import SCons.Tool.msvs
  39. import SCons.Util
  40. import SCons.Warnings
  41. import SCons.Scanner.RC
  42. from .MSCommon import msvc_exists, msvc_setup_env_once, msvc_version_to_maj_min
  43. CSuffixes = ['.c', '.C']
  44. CXXSuffixes = ['.cc', '.cpp', '.cxx', '.c++', '.C++']
  45. def validate_vars(env):
  46. """Validate the PCH and PCHSTOP construction variables."""
  47. if 'PCH' in env and env['PCH']:
  48. if 'PCHSTOP' not in env:
  49. raise SCons.Errors.UserError("The PCHSTOP construction must be defined if PCH is defined.")
  50. if not SCons.Util.is_String(env['PCHSTOP']):
  51. raise SCons.Errors.UserError("The PCHSTOP construction variable must be a string: %r"%env['PCHSTOP'])
  52. def msvc_set_PCHPDBFLAGS(env):
  53. """
  54. Set appropriate PCHPDBFLAGS for the MSVC version being used.
  55. """
  56. if env.get('MSVC_VERSION',False):
  57. maj, min = msvc_version_to_maj_min(env['MSVC_VERSION'])
  58. if maj < 8:
  59. env['PCHPDBFLAGS'] = SCons.Util.CLVar(['${(PDB and "/Yd") or ""}'])
  60. else:
  61. env['PCHPDBFLAGS'] = ''
  62. else:
  63. # Default if we can't determine which version of MSVC we're using
  64. env['PCHPDBFLAGS'] = SCons.Util.CLVar(['${(PDB and "/Yd") or ""}'])
  65. def pch_emitter(target, source, env):
  66. """Adds the object file target."""
  67. validate_vars(env)
  68. pch = None
  69. obj = None
  70. for t in target:
  71. if SCons.Util.splitext(str(t))[1] == '.pch':
  72. pch = t
  73. if SCons.Util.splitext(str(t))[1] == '.obj':
  74. obj = t
  75. if not obj:
  76. obj = SCons.Util.splitext(str(pch))[0]+'.obj'
  77. target = [pch, obj] # pch must be first, and obj second for the PCHCOM to work
  78. return (target, source)
  79. def object_emitter(target, source, env, parent_emitter):
  80. """Sets up the PCH dependencies for an object file."""
  81. validate_vars(env)
  82. parent_emitter(target, source, env)
  83. # Add a dependency, but only if the target (e.g. 'Source1.obj')
  84. # doesn't correspond to the pre-compiled header ('Source1.pch').
  85. # If the basenames match, then this was most likely caused by
  86. # someone adding the source file to both the env.PCH() and the
  87. # env.Program() calls, and adding the explicit dependency would
  88. # cause a cycle on the .pch file itself.
  89. #
  90. # See issue #2505 for a discussion of what to do if it turns
  91. # out this assumption causes trouble in the wild:
  92. # http://scons.tigris.org/issues/show_bug.cgi?id=2505
  93. if 'PCH' in env:
  94. pch = env['PCH']
  95. if str(target[0]) != SCons.Util.splitext(str(pch))[0] + '.obj':
  96. env.Depends(target, pch)
  97. return (target, source)
  98. def static_object_emitter(target, source, env):
  99. return object_emitter(target, source, env,
  100. SCons.Defaults.StaticObjectEmitter)
  101. def shared_object_emitter(target, source, env):
  102. return object_emitter(target, source, env,
  103. SCons.Defaults.SharedObjectEmitter)
  104. pch_action = SCons.Action.Action('$PCHCOM', '$PCHCOMSTR')
  105. pch_builder = SCons.Builder.Builder(action=pch_action, suffix='.pch',
  106. emitter=pch_emitter,
  107. source_scanner=SCons.Tool.SourceFileScanner)
  108. # Logic to build .rc files into .res files (resource files)
  109. res_scanner = SCons.Scanner.RC.RCScan()
  110. res_action = SCons.Action.Action('$RCCOM', '$RCCOMSTR')
  111. res_builder = SCons.Builder.Builder(action=res_action,
  112. src_suffix='.rc',
  113. suffix='.res',
  114. src_builder=[],
  115. source_scanner=res_scanner)
  116. def msvc_batch_key(action, env, target, source):
  117. """
  118. Returns a key to identify unique batches of sources for compilation.
  119. If batching is enabled (via the $MSVC_BATCH setting), then all
  120. target+source pairs that use the same action, defined by the same
  121. environment, and have the same target and source directories, will
  122. be batched.
  123. Returning None specifies that the specified target+source should not
  124. be batched with other compilations.
  125. """
  126. # Fixing MSVC_BATCH mode. Previous if did not work when MSVC_BATCH
  127. # was set to False. This new version should work better.
  128. # Note we need to do the env.subst so $MSVC_BATCH can be a reference to
  129. # another construction variable, which is why we test for False and 0
  130. # as strings.
  131. if not 'MSVC_BATCH' in env or env.subst('$MSVC_BATCH') in ('0', 'False', '', None):
  132. # We're not using batching; return no key.
  133. return None
  134. t = target[0]
  135. s = source[0]
  136. if os.path.splitext(t.name)[0] != os.path.splitext(s.name)[0]:
  137. # The base names are different, so this *must* be compiled
  138. # separately; return no key.
  139. return None
  140. return (id(action), id(env), t.dir, s.dir)
  141. def msvc_output_flag(target, source, env, for_signature):
  142. """
  143. Returns the correct /Fo flag for batching.
  144. If batching is disabled or there's only one source file, then we
  145. return an /Fo string that specifies the target explicitly. Otherwise,
  146. we return an /Fo string that just specifies the first target's
  147. directory (where the Visual C/C++ compiler will put the .obj files).
  148. """
  149. # Fixing MSVC_BATCH mode. Previous if did not work when MSVC_BATCH
  150. # was set to False. This new version should work better. Removed
  151. # len(source)==1 as batch mode can compile only one file
  152. # (and it also fixed problem with compiling only one changed file
  153. # with batch mode enabled)
  154. if not 'MSVC_BATCH' in env or env.subst('$MSVC_BATCH') in ('0', 'False', '', None):
  155. return '/Fo$TARGET'
  156. else:
  157. # The Visual C/C++ compiler requires a \ at the end of the /Fo
  158. # option to indicate an output directory. We use os.sep here so
  159. # that the test(s) for this can be run on non-Windows systems
  160. # without having a hard-coded backslash mess up command-line
  161. # argument parsing.
  162. return '/Fo${TARGET.dir}' + os.sep
  163. CAction = SCons.Action.Action("$CCCOM", "$CCCOMSTR",
  164. batch_key=msvc_batch_key,
  165. targets='$CHANGED_TARGETS')
  166. ShCAction = SCons.Action.Action("$SHCCCOM", "$SHCCCOMSTR",
  167. batch_key=msvc_batch_key,
  168. targets='$CHANGED_TARGETS')
  169. CXXAction = SCons.Action.Action("$CXXCOM", "$CXXCOMSTR",
  170. batch_key=msvc_batch_key,
  171. targets='$CHANGED_TARGETS')
  172. ShCXXAction = SCons.Action.Action("$SHCXXCOM", "$SHCXXCOMSTR",
  173. batch_key=msvc_batch_key,
  174. targets='$CHANGED_TARGETS')
  175. def generate(env):
  176. """Add Builders and construction variables for MSVC++ to an Environment."""
  177. static_obj, shared_obj = SCons.Tool.createObjBuilders(env)
  178. # TODO(batch): shouldn't reach in to cmdgen this way; necessary
  179. # for now to bypass the checks in Builder.DictCmdGenerator.__call__()
  180. # and allow .cc and .cpp to be compiled in the same command line.
  181. static_obj.cmdgen.source_ext_match = False
  182. shared_obj.cmdgen.source_ext_match = False
  183. for suffix in CSuffixes:
  184. static_obj.add_action(suffix, CAction)
  185. shared_obj.add_action(suffix, ShCAction)
  186. static_obj.add_emitter(suffix, static_object_emitter)
  187. shared_obj.add_emitter(suffix, shared_object_emitter)
  188. for suffix in CXXSuffixes:
  189. static_obj.add_action(suffix, CXXAction)
  190. shared_obj.add_action(suffix, ShCXXAction)
  191. static_obj.add_emitter(suffix, static_object_emitter)
  192. shared_obj.add_emitter(suffix, shared_object_emitter)
  193. env['CCPDBFLAGS'] = SCons.Util.CLVar(['${(PDB and "/Z7") or ""}'])
  194. env['CCPCHFLAGS'] = SCons.Util.CLVar(['${(PCH and "/Yu%s \\\"/Fp%s\\\""%(PCHSTOP or "",File(PCH))) or ""}'])
  195. env['_MSVC_OUTPUT_FLAG'] = msvc_output_flag
  196. env['_CCCOMCOM'] = '$CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS $CCPCHFLAGS $CCPDBFLAGS'
  197. env['CC'] = 'cl'
  198. env['CCFLAGS'] = SCons.Util.CLVar('/nologo')
  199. env['CFLAGS'] = SCons.Util.CLVar('')
  200. env['CCCOM'] = '${TEMPFILE("$CC $_MSVC_OUTPUT_FLAG /c $CHANGED_SOURCES $CFLAGS $CCFLAGS $_CCCOMCOM","$CCCOMSTR")}'
  201. env['SHCC'] = '$CC'
  202. env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS')
  203. env['SHCFLAGS'] = SCons.Util.CLVar('$CFLAGS')
  204. env['SHCCCOM'] = '${TEMPFILE("$SHCC $_MSVC_OUTPUT_FLAG /c $CHANGED_SOURCES $SHCFLAGS $SHCCFLAGS $_CCCOMCOM","$SHCCCOMSTR")}'
  205. env['CXX'] = '$CC'
  206. env['CXXFLAGS'] = SCons.Util.CLVar('$( /TP $)')
  207. env['CXXCOM'] = '${TEMPFILE("$CXX $_MSVC_OUTPUT_FLAG /c $CHANGED_SOURCES $CXXFLAGS $CCFLAGS $_CCCOMCOM","$CXXCOMSTR")}'
  208. env['SHCXX'] = '$CXX'
  209. env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS')
  210. env['SHCXXCOM'] = '${TEMPFILE("$SHCXX $_MSVC_OUTPUT_FLAG /c $CHANGED_SOURCES $SHCXXFLAGS $SHCCFLAGS $_CCCOMCOM","$SHCXXCOMSTR")}'
  211. env['CPPDEFPREFIX'] = '/D'
  212. env['CPPDEFSUFFIX'] = ''
  213. env['INCPREFIX'] = '/I'
  214. env['INCSUFFIX'] = ''
  215. # env.Append(OBJEMITTER = [static_object_emitter])
  216. # env.Append(SHOBJEMITTER = [shared_object_emitter])
  217. env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 1
  218. env['RC'] = 'rc'
  219. env['RCFLAGS'] = SCons.Util.CLVar('')
  220. env['RCSUFFIXES']=['.rc','.rc2']
  221. env['RCCOM'] = '$RC $_CPPDEFFLAGS $_CPPINCFLAGS $RCFLAGS /fo$TARGET $SOURCES'
  222. env['BUILDERS']['RES'] = res_builder
  223. env['OBJPREFIX'] = ''
  224. env['OBJSUFFIX'] = '.obj'
  225. env['SHOBJPREFIX'] = '$OBJPREFIX'
  226. env['SHOBJSUFFIX'] = '$OBJSUFFIX'
  227. # Set-up ms tools paths
  228. msvc_setup_env_once(env)
  229. env['CFILESUFFIX'] = '.c'
  230. env['CXXFILESUFFIX'] = '.cc'
  231. msvc_set_PCHPDBFLAGS(env)
  232. env['PCHCOM'] = '$CXX /Fo${TARGETS[1]} $CXXFLAGS $CCFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS /c $SOURCES /Yc$PCHSTOP /Fp${TARGETS[0]} $CCPDBFLAGS $PCHPDBFLAGS'
  233. env['BUILDERS']['PCH'] = pch_builder
  234. if 'ENV' not in env:
  235. env['ENV'] = {}
  236. if 'SystemRoot' not in env['ENV']: # required for dlls in the winsxs folders
  237. env['ENV']['SystemRoot'] = SCons.Platform.win32.get_system_root()
  238. def exists(env):
  239. return msvc_exists()
  240. # Local Variables:
  241. # tab-width:4
  242. # indent-tabs-mode:nil
  243. # End:
  244. # vim: set expandtab tabstop=4 shiftwidth=4: