| OLD | NEW |
| (Empty) |
| 1 #!/usr/bin/python2.4 | |
| 2 # Copyright 2008, Google Inc. | |
| 3 # All rights reserved. | |
| 4 # | |
| 5 # Redistribution and use in source and binary forms, with or without | |
| 6 # modification, are permitted provided that the following conditions are | |
| 7 # met: | |
| 8 # | |
| 9 # * Redistributions of source code must retain the above copyright | |
| 10 # notice, this list of conditions and the following disclaimer. | |
| 11 # * Redistributions in binary form must reproduce the above | |
| 12 # copyright notice, this list of conditions and the following disclaimer | |
| 13 # in the documentation and/or other materials provided with the | |
| 14 # distribution. | |
| 15 # * Neither the name of Google Inc. nor the names of its | |
| 16 # contributors may be used to endorse or promote products derived from | |
| 17 # this software without specific prior written permission. | |
| 18 # | |
| 19 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
| 20 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
| 21 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
| 22 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
| 23 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
| 24 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
| 25 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
| 26 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
| 27 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
| 28 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
| 29 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| 30 | |
| 31 """Build tool setup for Windows. | |
| 32 | |
| 33 This module is a SCons tool which should be include in the topmost windows | |
| 34 environment. | |
| 35 It is used as follows: | |
| 36 env = base_env.Clone(tools = ['component_setup']) | |
| 37 win_env = base_env.Clone(tools = ['target_platform_windows']) | |
| 38 """ | |
| 39 | |
| 40 | |
| 41 import os | |
| 42 import time | |
| 43 import command_output | |
| 44 import SCons.Script | |
| 45 | |
| 46 | |
| 47 def WaitForWritable(target, source, env): | |
| 48 """Waits for the target to become writable. | |
| 49 | |
| 50 Args: | |
| 51 target: List of target nodes. | |
| 52 source: List of source nodes. | |
| 53 env: Environment context. | |
| 54 | |
| 55 Returns: | |
| 56 Zero if success, nonzero if error. | |
| 57 | |
| 58 This is a necessary hack on Windows, where antivirus software can lock exe | |
| 59 files briefly after they're written. This can cause subsequent reads of the | |
| 60 file by env.Install() to fail. To prevent these failures, wait for the file | |
| 61 to be writable. | |
| 62 """ | |
| 63 target_path = target[0].abspath | |
| 64 if not os.path.exists(target_path): | |
| 65 return 0 # Nothing to wait for | |
| 66 | |
| 67 for retries in range(10): | |
| 68 try: | |
| 69 f = open(target_path, 'a+b') | |
| 70 f.close() | |
| 71 return 0 # Successfully opened file for write, so we're done | |
| 72 except (IOError, OSError): | |
| 73 print 'Waiting for access to %s...' % target_path | |
| 74 time.sleep(1) | |
| 75 | |
| 76 # If we're still here, fail | |
| 77 print 'Timeout waiting for access to %s.' % target_path | |
| 78 return 1 | |
| 79 | |
| 80 | |
| 81 def RunManifest(target, source, env, cmd): | |
| 82 """Run the Microsoft Visual Studio manifest tool (mt.exe). | |
| 83 | |
| 84 Args: | |
| 85 target: List of target nodes. | |
| 86 source: List of source nodes. | |
| 87 env: Environment context. | |
| 88 cmd: Command to run. | |
| 89 | |
| 90 Returns: | |
| 91 Zero if success, nonzero if error. | |
| 92 | |
| 93 The mt.exe tool seems to experience intermittent failures trying to write to | |
| 94 .exe or .dll files. Antivirus software makes this worse, but the problem | |
| 95 can still occur even if antivirus software is disabled. The failures look | |
| 96 like: | |
| 97 | |
| 98 mt.exe : general error c101008d: Failed to write the updated manifest to | |
| 99 the resource of file "(name of exe)". Access is denied. | |
| 100 | |
| 101 with mt.exe returning an errorlevel (return code) of 31. The workaround is | |
| 102 to retry running mt.exe after a short delay. | |
| 103 """ | |
| 104 cmdline = env.subst(cmd, target=target, source=source) | |
| 105 | |
| 106 for retry in range(5): | |
| 107 # If this is a retry, print a message and delay first | |
| 108 if retry: | |
| 109 # mt.exe failed to write to the target file. Print a warning message, | |
| 110 # delay 3 seconds, and retry. | |
| 111 print 'Warning: mt.exe failed to write to %s; retrying.' % target[0] | |
| 112 time.sleep(3) | |
| 113 | |
| 114 return_code, output = command_output.RunCommand( | |
| 115 cmdline, env=env['ENV'], echo_output=False) | |
| 116 if return_code != 31: # Something other than the intermittent error | |
| 117 break | |
| 118 | |
| 119 # Pass through output (if any) and return code from manifest | |
| 120 if output: | |
| 121 print output | |
| 122 return return_code | |
| 123 | |
| 124 | |
| 125 def RunManifestExe(target, source, env): | |
| 126 """Calls RunManifest for updating an executable (resource_num=1).""" | |
| 127 return RunManifest(target, source, env, cmd='$MANIFEST_COM') | |
| 128 | |
| 129 | |
| 130 def RunManifestDll(target, source, env): | |
| 131 """Calls RunManifest for updating a dll (resource_num=2).""" | |
| 132 return RunManifest(target, source, env, cmd='$SHMANIFEST_COM') | |
| 133 | |
| 134 | |
| 135 def ComponentPlatformSetup(env, builder_name): | |
| 136 """Hook to allow platform to modify environment inside a component builder. | |
| 137 | |
| 138 This is called on a clone of the environment passed into the component | |
| 139 builder, and is the last modification done to that environment before using | |
| 140 it to call the underlying SCons builder (env.Program(), env.Library(), etc.) | |
| 141 | |
| 142 Args: | |
| 143 env: Environment to modify | |
| 144 builder_name: Name of the builder | |
| 145 """ | |
| 146 if env.get('ENABLE_EXCEPTIONS'): | |
| 147 env.FilterOut( | |
| 148 CPPDEFINES=['_HAS_EXCEPTIONS=0'], | |
| 149 # There are problems with LTCG when some files are compiled with | |
| 150 # exceptions and some aren't (the v-tables for STL and BOOST classes | |
| 151 # don't match). Therefore, turn off LTCG when exceptions are enabled. | |
| 152 CCFLAGS=['/GL'], | |
| 153 LINKFLAGS=['/LTCG'], | |
| 154 ARFLAGS=['/LTCG'], | |
| 155 ) | |
| 156 env.Append(CCFLAGS=['/EHsc']) | |
| 157 | |
| 158 if builder_name in ('ComponentObject', 'ComponentLibrary'): | |
| 159 if env.get('COMPONENT_STATIC'): | |
| 160 env.Append(CPPDEFINES=['_LIB']) | |
| 161 else: | |
| 162 env.Append(CPPDEFINES=['_USRDLL', '_WINDLL']) | |
| 163 | |
| 164 if builder_name == 'ComponentTestProgram': | |
| 165 env.FilterOut( | |
| 166 CPPDEFINES=['_WINDOWS'], | |
| 167 LINKFLAGS=['/SUBSYSTEM:WINDOWS'], | |
| 168 ) | |
| 169 env.Append( | |
| 170 CPPDEFINES=['_CONSOLE'], | |
| 171 LINKFLAGS=['/SUBSYSTEM:CONSOLE'], | |
| 172 ) | |
| 173 | |
| 174 # Make sure link methods are lists, so we can append to them below | |
| 175 env['LINKCOM'] = [env['LINKCOM']] | |
| 176 env['SHLINKCOM'] = [env['SHLINKCOM']] | |
| 177 | |
| 178 # Support manifest file generation and consumption | |
| 179 if env.get('MANIFEST_FILE'): | |
| 180 env.Append( | |
| 181 LINKCOM=[SCons.Script.Action(RunManifestExe, '$MANIFEST_COMSTR')], | |
| 182 SHLINKCOM=[SCons.Script.Action(RunManifestDll, '$SHMANIFEST_COMSTR')], | |
| 183 ) | |
| 184 | |
| 185 # If manifest file should be autogenerated, add the -manifest link line and | |
| 186 # delete the generated manfest after running mt.exe. | |
| 187 if env.get('MANFEST_FILE_GENERATED_BY_LINK'): | |
| 188 env.Append( | |
| 189 LINKFLAGS=['-manifest'], | |
| 190 LINKCOM=[SCons.Script.Delete('$MANFEST_FILE_GENERATED_BY_LINK')], | |
| 191 SHLINKCOM=[SCons.Script.Delete('$MANFEST_FILE_GENERATED_BY_LINK')], | |
| 192 ) | |
| 193 | |
| 194 # Wait for the output file to be writable before releasing control to | |
| 195 # SCons. Windows virus scanners temporarily lock modified executable files | |
| 196 # for scanning, which causes SCons's env.Install() to fail intermittently. | |
| 197 env.Append( | |
| 198 LINKCOM=[SCons.Script.Action(WaitForWritable, None)], | |
| 199 SHLINKCOM=[SCons.Script.Action(WaitForWritable, None)], | |
| 200 ) | |
| 201 | |
| 202 #------------------------------------------------------------------------------ | |
| 203 | |
| 204 | |
| 205 def generate(env): | |
| 206 # NOTE: SCons requires the use of this name, which fails gpylint. | |
| 207 """SCons entry point for this tool.""" | |
| 208 | |
| 209 # Bring in the outside PATH, INCLUDE, and LIB if not blocked. | |
| 210 if not env.get('MSVC_BLOCK_ENVIRONMENT_CHANGES'): | |
| 211 env.AppendENVPath('PATH', os.environ.get('PATH', '[]')) | |
| 212 env.AppendENVPath('INCLUDE', os.environ.get('INCLUDE', '[]')) | |
| 213 env.AppendENVPath('LIB', os.environ.get('LIB', '[]')) | |
| 214 | |
| 215 # Load various Visual Studio related tools. | |
| 216 env.Tool('as') | |
| 217 env.Tool('msvs') | |
| 218 env.Tool('windows_hard_link') | |
| 219 | |
| 220 pre_msvc_env = env['ENV'].copy() | |
| 221 | |
| 222 env.Tool('msvc') | |
| 223 env.Tool('mslib') | |
| 224 env.Tool('mslink') | |
| 225 | |
| 226 # Find VC80_DIR if it isn't already set. | |
| 227 if not env.get('VC80_DIR'): | |
| 228 # Look in each directory in the path for cl.exe. | |
| 229 for p in env['ENV']['PATH'].split(os.pathsep): | |
| 230 # Use the directory two layers up if it exists. | |
| 231 if os.path.exists(os.path.join(p, 'cl.exe')): | |
| 232 env['VC80_DIR'] = os.path.dirname(os.path.dirname(p)) | |
| 233 | |
| 234 # The msvc, mslink, and mslib tools search the registry for installed copies | |
| 235 # of Visual Studio and prepends them to the PATH, INCLUDE, and LIB | |
| 236 # environment variables. Block these changes if necessary. | |
| 237 if env.get('MSVC_BLOCK_ENVIRONMENT_CHANGES'): | |
| 238 env['ENV'] = pre_msvc_env | |
| 239 | |
| 240 # Declare bits | |
| 241 DeclareBit('windows', 'Target platform is windows.', | |
| 242 exclusive_groups=('target_platform')) | |
| 243 env.SetBits('windows') | |
| 244 | |
| 245 env.Replace( | |
| 246 TARGET_PLATFORM='WINDOWS', | |
| 247 COMPONENT_PLATFORM_SETUP=ComponentPlatformSetup, | |
| 248 | |
| 249 # A better rebuild command (actually cleans, then rebuild) | |
| 250 MSVSREBUILDCOM=''.join(['$MSVSSCONSCOM -c "$MSVSBUILDTARGET" && ', | |
| 251 '$MSVSSCONSCOM "$MSVSBUILDTARGET"']), | |
| 252 ) | |
| 253 | |
| 254 env.SetDefault( | |
| 255 # Command line option to include a header | |
| 256 CCFLAG_INCLUDE='/FI', | |
| 257 | |
| 258 # Generate PDBs matching target name by default. | |
| 259 PDB='${TARGET.base}.pdb', | |
| 260 | |
| 261 # Code coverage related. | |
| 262 COVERAGE_LINKFLAGS='/PROFILE', # Requires vc_80 or higher. | |
| 263 COVERAGE_LINKCOM_EXTRAS='$COVERAGE_VSINSTR /COVERAGE $TARGET', | |
| 264 # NOTE: need to ignore error in return type here, the tool has issues. | |
| 265 # Thus a - is added. | |
| 266 COVERAGE_START_CMD=[ | |
| 267 # If a previous build was cancelled or crashed, VSPerfCmd may still | |
| 268 # be running, which causes future coverage runs to fail. Make sure | |
| 269 # it's shut down before starting coverage up again. | |
| 270 '-$COVERAGE_VSPERFCMD -shutdown', | |
| 271 '$COVERAGE_VSPERFCMD -start:coverage ' | |
| 272 '-output:${COVERAGE_OUTPUT_FILE}.pre'], | |
| 273 COVERAGE_STOP_CMD=[ | |
| 274 '-$COVERAGE_VSPERFCMD -shutdown', | |
| 275 '$COVERAGE_ANALYZER -sym_path=. ${COVERAGE_OUTPUT_FILE}.pre.coverage', | |
| 276 SCons.Script.Copy('$COVERAGE_OUTPUT_FILE', | |
| 277 '${COVERAGE_OUTPUT_FILE}.pre.coverage.lcov'), | |
| 278 ], | |
| 279 COVERAGE_EXTRA_PATHS=['$COVERAGE_ANALYZER_DIR'], | |
| 280 | |
| 281 # Manifest options | |
| 282 # When link.exe is run with '-manifest', it always generated a manifest | |
| 283 # with this name. | |
| 284 MANFEST_FILE_GENERATED_BY_LINK='${TARGET}.manifest', | |
| 285 # Manifest file to use as input to mt.exe. Can be overridden to pass in | |
| 286 # a pregenerated manifest file. | |
| 287 MANIFEST_FILE='$MANFEST_FILE_GENERATED_BY_LINK', | |
| 288 MANIFEST_COM=('mt.exe -nologo -manifest "$MANIFEST_FILE" ' | |
| 289 '-outputresource:"$TARGET";1'), | |
| 290 MANIFEST_COMSTR='$MANIFEST_COM', | |
| 291 SHMANIFEST_COM=('mt.exe -nologo -manifest "$MANIFEST_FILE" ' | |
| 292 '-outputresource:"$TARGET";2'), | |
| 293 SHMANIFEST_COMSTR='$SHMANIFEST_COM', | |
| 294 ) | |
| 295 | |
| 296 env.Append( | |
| 297 HOST_PLATFORMS=['WINDOWS'], | |
| 298 CPPDEFINES=['OS_WINDOWS=OS_WINDOWS'], | |
| 299 | |
| 300 # Turn up the warning level | |
| 301 CCFLAGS=['/W3'], | |
| 302 | |
| 303 # Force x86 platform, generate manifests | |
| 304 LINKFLAGS=['/MACHINE:X86'], | |
| 305 ARFLAGS=['/MACHINE:X86'], | |
| 306 | |
| 307 # Settings for debug | |
| 308 CCFLAGS_DEBUG=[ | |
| 309 '/Od', # disable optimizations | |
| 310 '/RTC1', # enable fast checks | |
| 311 '/MTd', # link with LIBCMTD.LIB debug lib | |
| 312 ], | |
| 313 LINKFLAGS_DEBUG=['/DEBUG'], | |
| 314 | |
| 315 # Settings for optimized | |
| 316 CCFLAGS_OPTIMIZED=[ | |
| 317 '/O1', # optimize for size | |
| 318 '/MT', # link with LIBCMT.LIB (multi-threaded, static linked crt) | |
| 319 '/GS', # enable security checks | |
| 320 ], | |
| 321 LINKFLAGS_OPTIMIZED=['/PDBPATH:none'], | |
| 322 | |
| 323 # Settings for component_builders | |
| 324 COMPONENT_LIBRARY_LINK_SUFFIXES=['.lib'], | |
| 325 COMPONENT_LIBRARY_DEBUG_SUFFIXES=['.pdb'], | |
| 326 ) | |
| 327 | |
| 328 # TODO(sgk): mslink.py creates a shlibLinkAction which doesn't specify | |
| 329 # '$SHLINKCOMSTR' as its command string. This breaks --brief. For now, | |
| 330 # hack into the existing action and override its command string. | |
| 331 env['SHLINKCOM'].list[0].cmdstr = '$SHLINKCOMSTR' | |
| OLD | NEW |