OLD | NEW |
| (Empty) |
1 #!/usr/bin/python2.4 | |
2 # | |
3 # Copyright 2009 Google Inc. | |
4 # | |
5 # Licensed under the Apache License, Version 2.0 (the "License"); | |
6 # you may not use this file except in compliance with the License. | |
7 # You may obtain a copy of the License at | |
8 # | |
9 # http://www.apache.org/licenses/LICENSE-2.0 | |
10 # | |
11 # Unless required by applicable law or agreed to in writing, software | |
12 # distributed under the License is distributed on an "AS IS" BASIS, | |
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
14 # See the License for the specific language governing permissions and | |
15 # limitations under the License. | |
16 # ======================================================================== | |
17 | |
18 """Omaha builders tool for SCons.""" | |
19 | |
20 import os.path | |
21 import SCons.Action | |
22 import SCons.Builder | |
23 import SCons.Tool | |
24 | |
25 | |
26 def EnablePrecompile(env, target_name): | |
27 """Enable use of precompiled headers for target_name. | |
28 | |
29 Args: | |
30 env: The environment. | |
31 target_name: Name of component. | |
32 | |
33 Returns: | |
34 The pch .obj file. | |
35 """ | |
36 if env.Bit('use_precompiled_headers'): | |
37 # We enable all warnings on all levels. The goal is to fix the code that | |
38 # we have written and to programmatically disable the warnings for the | |
39 # code we do not control. This list of warnings should shrink as the code | |
40 # gets fixed. | |
41 env.FilterOut(CCFLAGS=['/W3']) | |
42 env.Append( | |
43 CCFLAGS=[ | |
44 '/W4', | |
45 '/Wall', | |
46 ], | |
47 INCLUDES=[ | |
48 '$MAIN_DIR/precompile/precompile.h' | |
49 ], | |
50 ) | |
51 | |
52 env['PCHSTOP'] = '$MAIN_DIR/precompile/precompile.h' | |
53 | |
54 pch_env = env.Clone() | |
55 # Must manually force-include the header, as the precompilation step does | |
56 # not evaluate $INCLUDES | |
57 pch_env.Append(CCFLAGS=['/FI$MAIN_DIR/precompile/precompile.h']) | |
58 # Append '_pch' to the target base name to prevent target name collisions. | |
59 # One case where this might have occurred is when a .cc file has the same | |
60 # base name as the target program/library. | |
61 pch_output = pch_env.PCH( | |
62 target=target_name.replace('.', '_') + '_pch' + '.pch', | |
63 source='$MAIN_DIR/precompile/precompile.cc', | |
64 ) | |
65 | |
66 env['PCH'] = pch_output[0] | |
67 | |
68 # Return the pch .obj file that is created, so it can be | |
69 # included with the inputs of a module | |
70 return [pch_output[1]] | |
71 | |
72 | |
73 def SignDotNetManifest(env, target, unsigned_manifest): | |
74 """Signs a .NET manifest. | |
75 | |
76 Args: | |
77 env: The environment. | |
78 target: Name of signed manifest. | |
79 unsigned_manifest: Unsigned manifest. | |
80 | |
81 Returns: | |
82 Output node list from env.Command(). | |
83 """ | |
84 sign_manifest_cmd = ('@mage -Sign $SOURCE -ToFile $TARGET -TimestampUri ' | |
85 'http://timestamp.verisign.com/scripts/timstamp.dll ') | |
86 | |
87 if env.Bit('build_server'): | |
88 # If signing fails with the following error, the hash may not match any | |
89 # certificates: "Internal error, please try again. Object reference not set | |
90 # to an instance of an object." | |
91 sign_manifest_cmd += ('-CertHash ' + | |
92 env['build_server_certificate_hash']) | |
93 else: | |
94 sign_manifest_cmd += '-CertFile %s -Password %s' % ( | |
95 env.GetOption('authenticode_file'), | |
96 env.GetOption('authenticode_password')) | |
97 | |
98 signed_manifest = env.Command( | |
99 target=target, | |
100 source=unsigned_manifest, | |
101 action=sign_manifest_cmd | |
102 ) | |
103 | |
104 return signed_manifest | |
105 | |
106 | |
107 # | |
108 # Custom Library and Program builders. | |
109 # | |
110 # These builders have additional cababilities, including enabling precompiled | |
111 # headers when appropriate and signing DLLs and EXEs. | |
112 # | |
113 | |
114 # TODO(omaha): Make all build files use these builders instead of Hammer's. | |
115 # This will eliminate many lines in build.scons files related to enabling | |
116 # precompiled header and signing binaries. | |
117 | |
118 | |
119 def _ConditionallyEnablePrecompile(env, target_name, *args, **kwargs): | |
120 """Enables precompiled headers for target_name when appropriate. | |
121 | |
122 Enables precompiled headers if they are enabled for the build unless | |
123 use_pch_default = False. This requires that the source files are specified in | |
124 sources or in a list as the first argument after target_name. | |
125 | |
126 Args: | |
127 env: Environment in which we were called. | |
128 target_name: Name of the build target. | |
129 args: Positional arguments. | |
130 kwargs: Keyword arguments. | |
131 """ | |
132 use_pch_default = kwargs.get('use_pch_default', True) | |
133 | |
134 if use_pch_default and env.Bit('use_precompiled_headers'): | |
135 pch_output = env.EnablePrecompile(target_name) | |
136 | |
137 # Search the keyworded list first. | |
138 for key in ['source', 'sources', 'input', 'inputs']: | |
139 if key in kwargs: | |
140 kwargs[key] += pch_output | |
141 return | |
142 | |
143 # If the keyword was not found, assume the sources are the first argument in | |
144 # the non-keyworded list. | |
145 if args: | |
146 args[0].append(pch_output[0]) | |
147 | |
148 | |
149 def ComponentStaticLibrary(env, lib_name, *args, **kwargs): | |
150 """Pseudo-builder for static library. | |
151 | |
152 Enables precompiled headers if they are enabled for the build unless | |
153 use_pch_default = False. This requires that the source files are specified in | |
154 sources or in a list as the first argument after lib_name. | |
155 | |
156 Args: | |
157 env: Environment in which we were called. | |
158 lib_name: Static library name. | |
159 args: Positional arguments. | |
160 kwargs: Keyword arguments. | |
161 | |
162 Returns: | |
163 Output node list from env.ComponentLibrary(). | |
164 """ | |
165 _ConditionallyEnablePrecompile(env, lib_name, *args, **kwargs) | |
166 | |
167 return env.ComponentLibrary(lib_name, *args, **kwargs) | |
168 | |
169 | |
170 # TODO(omaha): Add signing. | |
171 def ComponentDll(env, lib_name, *args, **kwargs): | |
172 """Pseudo-builder for DLL. | |
173 | |
174 Enables precompiled headers if they are enabled for the build unless | |
175 use_pch_default = False. This requires that the source files are specified in | |
176 sources or in a list as the first argument after lib_name. | |
177 | |
178 Args: | |
179 env: Environment in which we were called. | |
180 lib_name: DLL name. | |
181 args: Positional arguments. | |
182 kwargs: Keyword arguments. | |
183 | |
184 Returns: | |
185 Output node list from env.ComponentLibrary(). | |
186 """ | |
187 env.Append(COMPONENT_STATIC=False) | |
188 | |
189 _ConditionallyEnablePrecompile(env, lib_name, *args, **kwargs) | |
190 | |
191 return env.ComponentLibrary(lib_name, *args, **kwargs) | |
192 | |
193 | |
194 # TODO(omaha): Add signing. | |
195 def ComponentSignedProgram(env, prog_name, *args, **kwargs): | |
196 """Pseudo-builder for signed EXEs. | |
197 | |
198 Enables precompiled headers if they are enabled for the build unless | |
199 use_pch_default = False. This requires that the source files are specified in | |
200 sources or in a list as the first argument after prog_name. | |
201 | |
202 Args: | |
203 env: Environment in which we were called. | |
204 prog_name: Executable name. | |
205 args: Positional arguments. | |
206 kwargs: Keyword arguments. | |
207 | |
208 Returns: | |
209 Output node list from env.ComponentProgram(). | |
210 """ | |
211 _ConditionallyEnablePrecompile(env, prog_name, *args, **kwargs) | |
212 | |
213 return env.ComponentProgram(prog_name, *args, **kwargs) | |
214 | |
215 | |
216 # TODO(omaha): Put these in a tools/ directory instead of staging. | |
217 def ComponentTool(env, prog_name, *args, **kwargs): | |
218 """Pseudo-builder for utility programs that do not need to be signed. | |
219 | |
220 Enables precompiled headers if they are enabled for the build unless | |
221 use_pch_default = False. This requires that the source files are specified in | |
222 sources or in a list as the first argument after prog_name. | |
223 | |
224 Args: | |
225 env: Environment in which we were called. | |
226 prog_name: Executable name. | |
227 args: Positional arguments. | |
228 kwargs: Keyword arguments. | |
229 | |
230 Returns: | |
231 Output node list from env.ComponentProgram(). | |
232 """ | |
233 _ConditionallyEnablePrecompile(env, prog_name, *args, **kwargs) | |
234 | |
235 return env.ComponentProgram(prog_name, *args, **kwargs) | |
236 | |
237 | |
238 # | |
239 # Unit Test Builders | |
240 # | |
241 | |
242 | |
243 def OmahaUnittest(env, # pylint: disable-msg=C6409 | |
244 name, | |
245 source, | |
246 LIBS=None, | |
247 all_in_one=True, | |
248 COMPONENT_TEST_SIZE='large', | |
249 is_small_tests_using_resources=False): | |
250 """Declares a new unit test. | |
251 | |
252 Args: | |
253 env: The environment. | |
254 name: Name of the unit test. | |
255 source: Sources for the unittest. | |
256 LIBS: Any libs required for the unit test. | |
257 all_in_one: If true, the test will be added to an executable containing | |
258 all tests. | |
259 COMPONENT_TEST_SIZE: small, medium, or large. | |
260 is_small_tests_using_resources: True if COMPONENT_TEST_SIZE='small' and | |
261 the test requires resources, such as strings. | |
262 | |
263 If !all_in_one and COMPONENT_TEST_SIZE is 'small', a main is automatically | |
264 provided. Otherwise, one must be provided in source or LIBS. The small main | |
265 is selected based on is_small_tests_using_resources. | |
266 | |
267 Returns: | |
268 Output node list from env.ComponentTestProgram(). | |
269 | |
270 Raises: | |
271 Exception: Invalid combination of arguments. | |
272 """ | |
273 test_env = env.Clone() | |
274 | |
275 source = test_env.Flatten(source) | |
276 | |
277 if COMPONENT_TEST_SIZE != 'small' and is_small_tests_using_resources: | |
278 raise Exception('is_small_tests_using_resources set for non-small test.') | |
279 | |
280 if all_in_one: | |
281 test_env['all_in_one_unittest_sources'].extend(test_env.File(source)) | |
282 if LIBS: | |
283 test_env['all_in_one_unittest_libs'].update( | |
284 test_env.File(test_env.Flatten(LIBS))) | |
285 # TODO(omaha): Get the node list automatically. | |
286 if 'HAMMER_RUNS_TESTS' in os.environ.keys(): | |
287 test_program_dir = '$TESTS_DIR' | |
288 else: | |
289 test_program_dir = '$STAGING_DIR' | |
290 output = [os.path.join(test_program_dir, 'omaha_unittest.exe'), | |
291 os.path.join(test_program_dir, 'omaha_unittest.pdb')] | |
292 else: | |
293 test_env.FilterOut(LINKFLAGS=['/NODEFAULTLIB', '/SUBSYSTEM:WINDOWS']) | |
294 if LIBS: | |
295 test_env.Append( | |
296 LIBS=test_env.Flatten(LIBS), | |
297 ) | |
298 # TODO(omaha): Let's try to eliminate this giant list of Win32 .libs here. | |
299 # They are generally dependencies of Omaha base, common, or net; it makes | |
300 # more sense for unit test authors to stay aware of dependencies and pass | |
301 # them in as part of the LIBS argument. | |
302 test_env.Append( | |
303 CPPPATH=[ | |
304 '$MAIN_DIR/third_party/gmock/include', | |
305 '$MAIN_DIR/third_party/gtest/include', | |
306 ], | |
307 LIBS=[ | |
308 '$LIB_DIR/base', | |
309 '$LIB_DIR/gmock', | |
310 '$LIB_DIR/gtest', | |
311 ('atls', 'atlsd')[test_env.Bit('debug')], | |
312 | |
313 # Required by base/process.h, which is used by unit_test.cc. | |
314 'psapi', | |
315 | |
316 # Required by omaha_version.h, which is used by omaha_unittest.cc. | |
317 'version', | |
318 | |
319 # Rquired if base/utils.h, which is used by several modules used by | |
320 # omaha_unittest.cc, is used. | |
321 'netapi32', | |
322 'rasapi32', | |
323 'shlwapi', | |
324 'wtsapi32', | |
325 ], | |
326 ) | |
327 | |
328 if COMPONENT_TEST_SIZE == 'small': | |
329 if is_small_tests_using_resources: | |
330 test_env.Append(LIBS=['$LIB_DIR/unittest_base_small_with_resources']) | |
331 else: | |
332 test_env.Append(LIBS=['$LIB_DIR/unittest_base_small']) | |
333 | |
334 if env.Bit('use_precompiled_headers'): | |
335 source += test_env.EnablePrecompile(name) | |
336 | |
337 # Set environment variables specific to the tests. | |
338 for env_var in os.environ: | |
339 if (not env_var in test_env['ENV'] and | |
340 (env_var.startswith('GTEST_') or env_var.startswith('OMAHA_TEST_'))): | |
341 test_env['ENV'][env_var] = os.environ[env_var] | |
342 | |
343 output = test_env.ComponentTestProgram( | |
344 name, | |
345 source + ['$OBJ_ROOT/testing/run_as_invoker.res'], | |
346 COMPONENT_TEST_SIZE=COMPONENT_TEST_SIZE, | |
347 ) | |
348 | |
349 # Add a manual dependency on the resource file used by omaha_unittest.cc to | |
350 # ensure it is always available before the test runs, which could be during | |
351 # the build. | |
352 test_env.Depends(output, '$TESTS_DIR/goopdateres_en.dll') | |
353 | |
354 return output | |
355 | |
356 | |
357 def GetAllInOneUnittestSources(env): | |
358 """Returns a list of source files for the all-in-one unit test. | |
359 | |
360 Args: | |
361 env: The environment. | |
362 | |
363 Returns: | |
364 A list of sources for the all-in-one unit test. | |
365 """ | |
366 return env['all_in_one_unittest_sources'] | |
367 | |
368 | |
369 def GetAllInOneUnittestLibs(env): | |
370 """Returns a list of libs to be linked into the all-in-one unit test. | |
371 | |
372 Args: | |
373 env: The environment. | |
374 | |
375 Returns: | |
376 A list of libs for the all-in-one unit test. | |
377 """ | |
378 # Sort to prevent spurious rebuilds caused by indeterminate ordering of a set. | |
379 return sorted(env['all_in_one_unittest_libs'], | |
380 key=SCons.Node.FS.Base.get_abspath) | |
381 | |
382 | |
383 # If a .idl file does not result in any generated proxy code (no foo_p.c and | |
384 # no foo_data.c), the default TypeLibrary builder will mistakenly believe that | |
385 # the IDL needs to be run through midl.exe again to rebuild the missing files. | |
386 def _MidlEmitter(target, source, env): | |
387 def IsNonProxyGeneratedFile(x): | |
388 """Returns true if x is not generated proxy code, false otherwise.""" | |
389 return not (str(x).endswith('_p.c') or str(x).endswith('_data.c')) | |
390 | |
391 (t, source) = SCons.Tool.midl.midl_emitter(target, source, env) | |
392 return (filter(IsNonProxyGeneratedFile, t), source) | |
393 | |
394 | |
395 def IsCoverageBuild(env): | |
396 """Returns true if this is a coverage build. | |
397 | |
398 Args: | |
399 env: The environment. | |
400 | |
401 Returns: | |
402 whether this is a coverage build. | |
403 """ | |
404 return 'coverage' in env.subst('$BUILD_TYPE') | |
405 | |
406 | |
407 def CopyFileToDirectory(env, target, source): | |
408 """Copies the file to the directory using the DOS copy command. | |
409 | |
410 In general, Replicate() should be used, but there are specific cases where | |
411 an explicit copy is required. | |
412 | |
413 Args: | |
414 env: The environment. | |
415 target: The target directory. | |
416 source: The full path to the source file. | |
417 | |
418 Returns: | |
419 Output node list from env.Command(). | |
420 """ | |
421 (_, source_filename) = os.path.split(source) | |
422 return env.Command(target=os.path.join(target, source_filename), | |
423 source=source, | |
424 action='@copy /y $SOURCE $TARGET') | |
425 | |
426 | |
427 # NOTE: SCons requires the use of this name, which fails gpylint. | |
428 def generate(env): # pylint: disable-msg=C6409 | |
429 """SCons entry point for this tool.""" | |
430 env.AddMethod(EnablePrecompile) | |
431 env.AddMethod(SignDotNetManifest) | |
432 env.AddMethod(ComponentStaticLibrary) | |
433 env.AddMethod(ComponentDll) | |
434 env.AddMethod(ComponentSignedProgram) | |
435 env.AddMethod(ComponentTool) | |
436 env.AddMethod(OmahaUnittest) | |
437 env.AddMethod(GetAllInOneUnittestSources) | |
438 env.AddMethod(GetAllInOneUnittestLibs) | |
439 env.AddMethod(IsCoverageBuild) | |
440 env.AddMethod(CopyFileToDirectory) | |
441 | |
442 env['MIDLNOPROXYCOM'] = ('$MIDL $MIDLFLAGS /tlb ${TARGETS[0]} ' | |
443 '/h ${TARGETS[1]} /iid ${TARGETS[2]} ' | |
444 '$SOURCE 2> NUL') | |
445 env['BUILDERS']['TypeLibraryWithNoProxy'] = SCons.Builder.Builder( | |
446 action=SCons.Action.Action('$MIDLNOPROXYCOM', '$MIDLNOPROXYCOMSTR'), | |
447 src_suffix='.idl', | |
448 suffix='.tlb', | |
449 emitter=_MidlEmitter, | |
450 source_scanner=SCons.Tool.midl.idl_scanner) | |
OLD | NEW |