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