89768528614b5b0730c7efa492f062db26e72598
[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 string
39 import sys
40
41 import SCons.Action
42 import SCons.Builder
43 import SCons.Errors
44 import SCons.Platform.win32
45 import SCons.Tool
46 import SCons.Tool.msvs
47 import SCons.Util
48 import SCons.Warnings
49 import SCons.Scanner.RC
50
51 from MSCommon import msvc_exists, msvc_setup_env
52
53 CSuffixes = ['.c', '.C']
54 CXXSuffixes = ['.cc', '.cpp', '.cxx', '.c++', '.C++']
55
56 def validate_vars(env):
57     """Validate the PCH and PCHSTOP construction variables."""
58     if env.has_key('PCH') and env['PCH']:
59         if not env.has_key('PCHSTOP'):
60             raise SCons.Errors.UserError, "The PCHSTOP construction must be defined if PCH is defined."
61         if not SCons.Util.is_String(env['PCHSTOP']):
62             raise SCons.Errors.UserError, "The PCHSTOP construction variable must be a string: %r"%env['PCHSTOP']
63
64 def pch_emitter(target, source, env):
65     """Adds the object file target."""
66
67     validate_vars(env)
68
69     pch = None
70     obj = None
71
72     for t in target:
73         if SCons.Util.splitext(str(t))[1] == '.pch':
74             pch = t
75         if SCons.Util.splitext(str(t))[1] == '.obj':
76             obj = t
77
78     if not obj:
79         obj = SCons.Util.splitext(str(pch))[0]+'.obj'
80
81     target = [pch, obj] # pch must be first, and obj second for the PCHCOM to work
82
83     return (target, source)
84
85 def object_emitter(target, source, env, parent_emitter):
86     """Sets up the PCH dependencies for an object file."""
87
88     validate_vars(env)
89
90     parent_emitter(target, source, env)
91
92     if env.has_key('PCH') and env['PCH']:
93         env.Depends(target, env['PCH'])
94
95     return (target, source)
96
97 def static_object_emitter(target, source, env):
98     return object_emitter(target, source, env,
99                           SCons.Defaults.StaticObjectEmitter)
100
101 def shared_object_emitter(target, source, env):
102     return object_emitter(target, source, env,
103                           SCons.Defaults.SharedObjectEmitter)
104
105 pch_action = SCons.Action.Action('$PCHCOM', '$PCHCOMSTR')
106 pch_builder = SCons.Builder.Builder(action=pch_action, suffix='.pch',
107                                     emitter=pch_emitter,
108                                     source_scanner=SCons.Tool.SourceFileScanner)
109
110
111 # Logic to build .rc files into .res files (resource files)
112 res_scanner = SCons.Scanner.RC.RCScan()
113 res_action  = SCons.Action.Action('$RCCOM', '$RCCOMSTR')
114 res_builder = SCons.Builder.Builder(action=res_action,
115                                     src_suffix='.rc',
116                                     suffix='.res',
117                                     src_builder=[],
118                                     source_scanner=res_scanner)
119
120 def msvc_batch_key(action, env, target, source):
121     """
122     Returns a key to identify unique batches of sources for compilation.
123
124     If batching is enabled (via the $MSVC_BATCH setting), then all
125     target+source pairs that use the same action, defined by the same
126     environment, and have the same target and source directories, will
127     be batched.
128
129     Returning None specifies that the specified target+source should not
130     be batched with other compilations.
131     """
132     b = env.subst('$MSVC_BATCH')
133     if b in (None, '', '0'):
134         # We're not using batching; return no key.
135         return None
136     t = target[0]
137     s = source[0]
138     if os.path.splitext(t.name)[0] != os.path.splitext(s.name)[0]:
139         # The base names are different, so this *must* be compiled
140         # separately; return no key.
141         return None
142     return (id(action), id(env), t.dir, s.dir)
143
144 def msvc_output_flag(target, source, env, for_signature):
145     """
146     Returns the correct /Fo flag for batching.
147
148     If batching is disabled or there's only one source file, then we
149     return an /Fo string that specifies the target explicitly.  Otherwise,
150     we return an /Fo string that just specifies the first target's
151     directory (where the Visual C/C++ compiler will put the .obj files).
152     """
153     b = env.subst('$MSVC_BATCH')
154     if b in (None, '', '0') or len(source) == 1:
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
164 CAction = SCons.Action.Action("$CCCOM", "$CCCOMSTR",
165                               batch_key=msvc_batch_key,
166                               targets='$CHANGED_TARGETS')
167 ShCAction = SCons.Action.Action("$SHCCCOM", "$SHCCCOMSTR",
168                                 batch_key=msvc_batch_key,
169                                 targets='$CHANGED_TARGETS')
170 CXXAction = SCons.Action.Action("$CXXCOM", "$CXXCOMSTR",
171                                 batch_key=msvc_batch_key,
172                                 targets='$CHANGED_TARGETS')
173 ShCXXAction = SCons.Action.Action("$SHCXXCOM", "$SHCXXCOMSTR",
174                                   batch_key=msvc_batch_key,
175                                   targets='$CHANGED_TARGETS')
176
177 def generate(env):
178     """Add Builders and construction variables for MSVC++ to an Environment."""
179     static_obj, shared_obj = SCons.Tool.createObjBuilders(env)
180
181     # TODO(batch):  shouldn't reach in to cmdgen this way; necessary
182     # for now to bypass the checks in Builder.DictCmdGenerator.__call__()
183     # and allow .cc and .cpp to be compiled in the same command line.
184     static_obj.cmdgen.source_ext_match = False
185     shared_obj.cmdgen.source_ext_match = False
186
187     for suffix in CSuffixes:
188         static_obj.add_action(suffix, CAction)
189         shared_obj.add_action(suffix, ShCAction)
190         static_obj.add_emitter(suffix, static_object_emitter)
191         shared_obj.add_emitter(suffix, shared_object_emitter)
192
193     for suffix in CXXSuffixes:
194         static_obj.add_action(suffix, CXXAction)
195         shared_obj.add_action(suffix, ShCXXAction)
196         static_obj.add_emitter(suffix, static_object_emitter)
197         shared_obj.add_emitter(suffix, shared_object_emitter)
198
199     env['CCPDBFLAGS'] = SCons.Util.CLVar(['${(PDB and "/Z7") or ""}'])
200     env['CCPCHFLAGS'] = SCons.Util.CLVar(['${(PCH and "/Yu%s /Fp%s"%(PCHSTOP or "",File(PCH))) or ""}'])
201     env['_MSVC_OUTPUT_FLAG'] = msvc_output_flag
202     env['_CCCOMCOM']  = '$CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS $CCPCHFLAGS $CCPDBFLAGS'
203     env['CC']         = 'cl'
204     env['CCFLAGS']    = SCons.Util.CLVar('/nologo')
205     env['CFLAGS']     = SCons.Util.CLVar('')
206     env['CCCOM']      = '$CC $_MSVC_OUTPUT_FLAG /c $CHANGED_SOURCES $CFLAGS $CCFLAGS $_CCCOMCOM'
207     env['SHCC']       = '$CC'
208     env['SHCCFLAGS']  = SCons.Util.CLVar('$CCFLAGS')
209     env['SHCFLAGS']   = SCons.Util.CLVar('$CFLAGS')
210     env['SHCCCOM']    = '$SHCC $_MSVC_OUTPUT_FLAG /c $CHANGED_SOURCES $SHCFLAGS $SHCCFLAGS $_CCCOMCOM'
211     env['CXX']        = '$CC'
212     env['CXXFLAGS']   = SCons.Util.CLVar('$( /TP $)')
213     env['CXXCOM']     = '$CXX $_MSVC_OUTPUT_FLAG /c $CHANGED_SOURCES $CXXFLAGS $CCFLAGS $_CCCOMCOM'
214     env['SHCXX']      = '$CXX'
215     env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS')
216     env['SHCXXCOM']   = '$SHCXX $_MSVC_OUTPUT_FLAG /c $CHANGED_SOURCES $SHCXXFLAGS $SHCCFLAGS $_CCCOMCOM'
217     env['CPPDEFPREFIX']  = '/D'
218     env['CPPDEFSUFFIX']  = ''
219     env['INCPREFIX']  = '/I'
220     env['INCSUFFIX']  = ''
221 #    env.Append(OBJEMITTER = [static_object_emitter])
222 #    env.Append(SHOBJEMITTER = [shared_object_emitter])
223     env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 1
224
225     env['RC'] = 'rc'
226     env['RCFLAGS'] = SCons.Util.CLVar('')
227     env['RCSUFFIXES']=['.rc','.rc2']
228     env['RCCOM'] = '$RC $_CPPDEFFLAGS $_CPPINCFLAGS $RCFLAGS /fo$TARGET $SOURCES'
229     env['BUILDERS']['RES'] = res_builder
230     env['OBJPREFIX']      = ''
231     env['OBJSUFFIX']      = '.obj'
232     env['SHOBJPREFIX']    = '$OBJPREFIX'
233     env['SHOBJSUFFIX']    = '$OBJSUFFIX'
234
235     # Set-up ms tools paths for default version
236     msvc_setup_env(env)
237
238     import mssdk
239     mssdk.generate(env)
240
241     env['CFILESUFFIX'] = '.c'
242     env['CXXFILESUFFIX'] = '.cc'
243
244     env['PCHPDBFLAGS'] = SCons.Util.CLVar(['${(PDB and "/Yd") or ""}'])
245     env['PCHCOM'] = '$CXX /Fo${TARGETS[1]} $CXXFLAGS $CCFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS /c $SOURCES /Yc$PCHSTOP /Fp${TARGETS[0]} $CCPDBFLAGS $PCHPDBFLAGS'
246     env['BUILDERS']['PCH'] = pch_builder
247
248     if not env.has_key('ENV'):
249         env['ENV'] = {}
250     if not env['ENV'].has_key('SystemRoot'):    # required for dlls in the winsxs folders
251         env['ENV']['SystemRoot'] = SCons.Platform.win32.get_system_root()
252
253 def exists(env):
254     return msvc_exists('cl')
255
256 # Local Variables:
257 # tab-width:4
258 # indent-tabs-mode:nil
259 # End:
260 # vim: set expandtab tabstop=4 shiftwidth=4: