| 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 |