| OLD | NEW |
| (Empty) |
| 1 #!/usr/bin/env python | |
| 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
| 3 # Use of this source code is governed by a BSD-style license that can be | |
| 4 # found in the LICENSE file. | |
| 5 | |
| 6 ''' A tool to setup the NaCl build env and invoke a command such as make ''' | |
| 7 | |
| 8 __author__ = 'gwink@google.com (Georges Winkenbach)' | |
| 9 | |
| 10 import optparse | |
| 11 import os | |
| 12 import subprocess | |
| 13 import sys | |
| 14 | |
| 15 # The default sdk platform to use if the user doesn't specify one. | |
| 16 __DEFAULT_SDK_PLATFORM = 'pepper_15' | |
| 17 | |
| 18 # Usage info. | |
| 19 __GLOBAL_HELP = '''%prog options [command] | |
| 20 | |
| 21 set-nacl-env is a utility that sets up the environment required to build | |
| 22 NaCl modules and invokes an optional command in a shell. If no command | |
| 23 is specified, set-nacl-env spawns a new shell instead. Optionally, the user | |
| 24 can request that the settings are printed to stdout. | |
| 25 ''' | |
| 26 | |
| 27 # Map the string stored in |sys.platform| into a toolchain host specifier. | |
| 28 __PLATFORM_TO_HOST_MAP = { | |
| 29 'win32': 'windows', | |
| 30 'cygwin': 'windows', | |
| 31 'linux2': 'linux', | |
| 32 'darwin': 'mac', | |
| 33 } | |
| 34 | |
| 35 # Map key triplet of (host, arch, variant) keys to the corresponding subdir in | |
| 36 # the toolchain path. For instance (mac, x86-32, newlib) maps to mac_x86_newlib. | |
| 37 # Note to NaCl eng.: this map is duplicated in nack_utils.py; you must keep them | |
| 38 # synched. | |
| 39 __HOST_TO_TOOLCHAIN_MAP = { | |
| 40 'mac': { # Host arch variant | |
| 41 'x86-32': { | |
| 42 'newlib': 'mac_x86_newlib', # Mac x86-32 newlib | |
| 43 'glibc' : 'mac_x86'}, # Mac x86-32 glibc | |
| 44 'x86-64': { | |
| 45 'newlib': 'mac_x86_newlib', # Mac x86-64 newlib | |
| 46 'glibc' : 'mac_x86'}, # Mac x86-64 glibc | |
| 47 }, | |
| 48 'windows': { | |
| 49 'x86-32': { | |
| 50 'newlib': 'win_x86_newlib', # Windows x86-32 newlib | |
| 51 'glibc' : 'win_x86'}, # Windows x86-32 glibc | |
| 52 'x86-64': { | |
| 53 'newlib': 'win_x86_newlib', # Windows x86-64 newlib | |
| 54 'glibc' : 'win_x86'}, # Windows x86-64 glibc | |
| 55 }, | |
| 56 'linux': { | |
| 57 'x86-32': { | |
| 58 'newlib': 'linux_x86_newlib', # Windows x86-32 newlib | |
| 59 'glibc' : 'linux_x86'}, # Windows x86-32 glibc | |
| 60 'x86-64': { | |
| 61 'newlib': 'linux_x86_newlib', # Windows x86-64 newlib | |
| 62 'glibc' : 'linux_x86'}, # Windows x86-64 glibc | |
| 63 }, | |
| 64 } | |
| 65 | |
| 66 # Map architecture specification to the corresponding tool-name prefix. | |
| 67 # @private | |
| 68 __VALID_ARCH_SPECS = { | |
| 69 'x86-32': 'i686', | |
| 70 'x86-64': 'x86_64', | |
| 71 } | |
| 72 | |
| 73 # Valid lib variants. | |
| 74 __VALID_VARIANT = ['glibc', 'newlib'] | |
| 75 | |
| 76 # Lists of env keys for build tools. Note: Each matching value is actually a | |
| 77 # format template with fields for 'prefix' such as 'i686-nacl-' and 'extras' | |
| 78 # such as ' -m64'. | |
| 79 __BUILD_TOOLS = { | |
| 80 'CC': '{prefix}gcc{extras}', | |
| 81 'CXX': '{prefix}g++{extras}', | |
| 82 'AR': '{prefix}ar{extras}', | |
| 83 'LINK': '{prefix}g++{extras}', | |
| 84 'STRIP': '{prefix}strip', | |
| 85 'RANLIB': '{prefix}ranlib', | |
| 86 } | |
| 87 | |
| 88 # List of env keys for build options with corresponding settings that are | |
| 89 # common to all build configurations. | |
| 90 __BUILD_OPTIONS = { | |
| 91 'CFLAGS': ['-std=gnu99', '-Wall', '-Wswitch-enum', '-g'], | |
| 92 'CXXFLAGS': ['-std=gnu++98', '-Wswitch-enum', '-g', '-pthread'], | |
| 93 'CPPFLAGS': ['-D_GNU_SOURCE=1', '-D__STDC_FORMAT_MACROS=1'], | |
| 94 'LDFLAGS': [], | |
| 95 } | |
| 96 | |
| 97 # All the build-flags env keys in one list. | |
| 98 __ALL_ENV_KEYS = __BUILD_TOOLS.keys() + __BUILD_OPTIONS.keys() | |
| 99 | |
| 100 # Map build types to the corresponding build flags. | |
| 101 __BUILD_TYPES = { | |
| 102 'debug': ['-O0'], | |
| 103 'release': ['-O3'], | |
| 104 } | |
| 105 | |
| 106 | |
| 107 def FormatOptionList(option_list, prefix='', separator=' '): | |
| 108 ''' Format a list of build-option items into a string. | |
| 109 | |
| 110 Format a list of build-option items into a string suitable for output. | |
| 111 | |
| 112 Args: | |
| 113 prefix: a prefix string to prepend to each list item. For instance, | |
| 114 prefix='-D' with item='__DEBUG' generates '-D__DEBUG'. | |
| 115 separator: a separator string to insert between items. | |
| 116 | |
| 117 Returns: | |
| 118 A formatted string. An empty string if list is empty. | |
| 119 ''' | |
| 120 return separator.join([prefix + item for item in option_list]) | |
| 121 | |
| 122 | |
| 123 def GetNaclSdkRoot(): | |
| 124 ''' Produce a string with the full path to the NaCl SDK root. | |
| 125 | |
| 126 The path to nacl-sdk root is derived from one of two sources. First, if | |
| 127 NACL_SDK_ROOT is defined in env it is assumed to contain the desired sdk | |
| 128 root. That makes it possible for this tool to run from any location. If | |
| 129 NACL_SDK_ROOT is not defined or is empty, the sdk root is taken as the | |
| 130 parent directory to this script's file. This works well when this script | |
| 131 is ran from the sdk_tools directory in the standard SDK installation. | |
| 132 | |
| 133 Returns: | |
| 134 A string with the path to the NaCl SDK root. | |
| 135 ''' | |
| 136 if 'NACL_SDK_ROOT' in os.environ: | |
| 137 return os.environ['NACL_SDK_ROOT'] | |
| 138 else: | |
| 139 SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) | |
| 140 SRC_DIR = os.path.dirname(os.path.dirname(os.path.dirname(SCRIPT_DIR))) | |
| 141 return os.path.join(SRC_DIR, 'native_client') | |
| 142 | |
| 143 | |
| 144 def GetToolchainPath(options): | |
| 145 ''' Build the path to the toolchain directory. | |
| 146 | |
| 147 Given the host, sdk-root directory, sdk platform, architecture and library | |
| 148 variant, this function builds the path to the toolchain directory. | |
| 149 | |
| 150 Examples: | |
| 151 For | |
| 152 sdk_root == 'c:/cool_code/nacl_sdk' | |
| 153 arch == 'x86-32' | |
| 154 lib variant == 'newlib' | |
| 155 nacl platform = 'pepper_17' | |
| 156 host == 'mac' | |
| 157 this function returns : | |
| 158 toolchain_path == /cool_code/nacl_sdk/pepper_17/toolchain/mac_x86_newlib | |
| 159 | |
| 160 Args: | |
| 161 options: the options instances containing attributes options.host, | |
| 162 options.arch, options.lib_variant, options.sdk_root and | |
| 163 options.sdk_platform. | |
| 164 | |
| 165 Returns: | |
| 166 A string containing the absolute path to the base directory for the | |
| 167 toolchain. | |
| 168 ''' | |
| 169 host = options.host | |
| 170 arch = options.arch | |
| 171 variant = options.lib_variant | |
| 172 toolchain_dir = __HOST_TO_TOOLCHAIN_MAP[host][arch][variant] | |
| 173 base_dir = os.path.abspath(options.sdk_root) | |
| 174 return os.path.join(base_dir, options.sdk_platform, 'toolchain', | |
| 175 toolchain_dir) | |
| 176 | |
| 177 | |
| 178 def ConfigureBaseEnv(merge): | |
| 179 ''' Configure and return a new env instance with the essential options. | |
| 180 | |
| 181 Create and return a new env instance with the base configuration. That env | |
| 182 contains at least an empty entry for each key defined in __ALL_ENV_KEYS. | |
| 183 However, if merge is True, a copy of the current os.environ is used to seed | |
| 184 env. | |
| 185 | |
| 186 Argument: | |
| 187 merge: True ==> merge build configuration with os.environ.. | |
| 188 | |
| 189 Returns: | |
| 190 A base env map. | |
| 191 ''' | |
| 192 env = {} | |
| 193 if merge: | |
| 194 for key, value in os.environ.items(): | |
| 195 env[key] = [value] | |
| 196 # Ensure that every env key has a default definition. | |
| 197 for key in __ALL_ENV_KEYS: | |
| 198 env.setdefault(key, []) | |
| 199 return env | |
| 200 | |
| 201 | |
| 202 def SetBuildTools(env, tool_bin_path, tool_prefix, extras_flags=''): | |
| 203 ''' Configure the build tools build flags in env. | |
| 204 | |
| 205 Given the absolute path to the toolchain's bin directory, tool_prefix and | |
| 206 optional extra_flags build flags, set the entries for the build tools | |
| 207 in env. For instance, using the sample path from GetToolchainPath above and | |
| 208 tool_prefix = 'i686-nacl-' we would get | |
| 209 | |
| 210 env['CC'] = | |
| 211 '/cool_code/nacl_sdk/pepper_17/toolchain/mac_x86_newlib/bin/i686-nacl-gcc' | |
| 212 | |
| 213 Args: | |
| 214 env: the env map to setup. | |
| 215 tool_bin_path: the absolute path to the toolchain's bin directory. | |
| 216 tool_prefix: a string with the tool's prefix, such as 'i686-nacl-'. | |
| 217 extra_flags: optional extra flags, such as ' -m64'. | |
| 218 ''' | |
| 219 for key, val in __BUILD_TOOLS.iteritems(): | |
| 220 tool_name = val.format(prefix=tool_prefix, extras=extras_flags) | |
| 221 env[key] = os.path.join(tool_bin_path, tool_name) | |
| 222 | |
| 223 | |
| 224 def SetRuntimeTools(env, tool_runtime_path): | |
| 225 ''' Setup the runtime tools in env. | |
| 226 | |
| 227 Given an absolute path to the toolchain's runtime directory, setup the | |
| 228 entries for the runtime tools in env. | |
| 229 | |
| 230 Args: | |
| 231 env: the env map to setup. | |
| 232 tool_runtime_path: the absolute path to the toolchain's runtime directory. | |
| 233 ''' | |
| 234 env['NACL_IRT_CORE32'] = os.path.join(tool_runtime_path, | |
| 235 'irt_core_x86_32.nexe') | |
| 236 env['NACL_IRT_CORE64'] = os.path.join(tool_runtime_path, | |
| 237 'irt_core_x86_64.nexe') | |
| 238 | |
| 239 | |
| 240 def SetCommonBuildOptions(env, options): | |
| 241 ''' Set the common build options, such as CFLAGS. | |
| 242 | |
| 243 Set the build options, such as CFLAGS that are common to all build | |
| 244 configurations, given the built type. | |
| 245 | |
| 246 Args: | |
| 247 env: the env map to set. | |
| 248 build_type: one of 'debug' or 'release'. | |
| 249 ''' | |
| 250 # Set the build flags from __BUILD_OPTIONS. | |
| 251 for key, val in __BUILD_OPTIONS.iteritems(): | |
| 252 env[key].extend(val) | |
| 253 # Add the build-type specific flags. | |
| 254 env['CFLAGS'].extend(__BUILD_TYPES[options.build_type]) | |
| 255 env['CXXFLAGS'].extend(__BUILD_TYPES[options.build_type]) | |
| 256 if not options.no_ppapi: | |
| 257 env['LDFLAGS'].extend(['-lppapi']) | |
| 258 | |
| 259 | |
| 260 def SetupX86Env(options): | |
| 261 ''' Generate a new env map for X86 builds. | |
| 262 | |
| 263 Generate and return a new env map for x86-NN architecture. The NN bit | |
| 264 size is derived from options.arch. | |
| 265 | |
| 266 Argument: | |
| 267 options: the cmd-line options. | |
| 268 | |
| 269 Returns: | |
| 270 A new env map with the build configuration flags set. | |
| 271 ''' | |
| 272 env = ConfigureBaseEnv(options.merge) | |
| 273 | |
| 274 # Where to find tools and libraries within the toolchain directory. | |
| 275 tool_bin_path = os.path.join(options.toolchain_path, 'bin') | |
| 276 tool_runtime_path = os.path.join(options.toolchain_path, 'runtime') | |
| 277 | |
| 278 # Store the bin paths into env. This isn't really part of the build | |
| 279 # environment. But it's nice to have there for reference. | |
| 280 env['NACL_TOOL_BIN_PATH'] = tool_bin_path | |
| 281 env['NACL_TOOL_RUNTIME_PATH'] = tool_runtime_path | |
| 282 | |
| 283 if options.arch == 'x86-32': | |
| 284 SetBuildTools(env, tool_bin_path, 'i686-nacl-', extras_flags=' -m32') | |
| 285 else: | |
| 286 assert(options.arch == 'x86-64') | |
| 287 SetBuildTools(env, tool_bin_path, 'x86_64-nacl-', extras_flags=' -m64') | |
| 288 SetRuntimeTools(env, tool_runtime_path) | |
| 289 SetCommonBuildOptions(env, options) | |
| 290 return env | |
| 291 | |
| 292 | |
| 293 def dump(options, env, template): | |
| 294 ''' Dump the build settings in env to stdout. | |
| 295 | |
| 296 Args: | |
| 297 options: the cmd-line options, used to output the target buid configuartion. | |
| 298 env: the env map with the build flags. | |
| 299 template: a fiormatting template used to format options output. It must | |
| 300 contain format fields 'option' and 'value'. | |
| 301 ''' | |
| 302 if options.pretty_print: | |
| 303 print '\nConfiguration:' | |
| 304 print '-------------' | |
| 305 print ' Host = %s' % options.host | |
| 306 print ' NaCl SDK root = %s' % options.sdk_root | |
| 307 print ' SDK platform = %s' % options.sdk_platform | |
| 308 print ' Target architecture = %s' % options.arch | |
| 309 print ' Lib variant = %s' % options.lib_variant | |
| 310 | |
| 311 if options.pretty_print: | |
| 312 print '\nNaCl toolchain paths:' | |
| 313 print '-------------------------' | |
| 314 print ' toolchain = %s' % options.toolchain_path | |
| 315 print ' toolchain bin = %s' % env['NACL_TOOL_BIN_PATH'] | |
| 316 print ' toolchain runtime = %s' % env['NACL_TOOL_RUNTIME_PATH'] | |
| 317 | |
| 318 if options.pretty_print: | |
| 319 print '\nBuild tools:' | |
| 320 print '-----------' | |
| 321 print template.format(option='CC', value=env['CC']) | |
| 322 print template.format(option='CXX', value=env['CXX']) | |
| 323 print template.format(option='AR', value=env['AR']) | |
| 324 print template.format(option='LINK', value=env['LINK']) | |
| 325 print template.format(option='STRIP', value=env['STRIP']) | |
| 326 print template.format(option='RANLIB', value=env['RANLIB']) | |
| 327 | |
| 328 if options.pretty_print: | |
| 329 print '\nBuild settings:' | |
| 330 print '--------------' | |
| 331 print template.format(option='CFLAGS', | |
| 332 value=FormatOptionList(option_list=env['CFLAGS'])) | |
| 333 print template.format(option='CXXFLAGS', | |
| 334 value=FormatOptionList(option_list=env['CXXFLAGS'])) | |
| 335 print template.format(option='LDFLAGS', | |
| 336 value=FormatOptionList(option_list=env['LDFLAGS'])) | |
| 337 print template.format(option='CPPFLAGS', | |
| 338 value=FormatOptionList(option_list=env['CPPFLAGS'])) | |
| 339 if options.pretty_print: | |
| 340 print '\nRuntime tools:' | |
| 341 print '-------------' | |
| 342 print template.format(option='NACL_IRT_CORE32', value=env['NACL_IRT_CORE32']) | |
| 343 print template.format(option='NACL_IRT_CORE64', value=env['NACL_IRT_CORE64']) | |
| 344 print '' | |
| 345 | |
| 346 | |
| 347 def NormalizeEnv(env): | |
| 348 ''' Returns a copy of env normalized. | |
| 349 | |
| 350 Internally, this script uses lists to keep track of build settings in env. | |
| 351 This function converts these list to space-separated strings of items, | |
| 352 suitable for use as a subprocess env. | |
| 353 | |
| 354 Argument: | |
| 355 env: env map that must be normalized. | |
| 356 | |
| 357 Returns: | |
| 358 A copy of env with lists converted to strings. | |
| 359 ''' | |
| 360 norm_env = {} | |
| 361 for key, value in env.iteritems(): | |
| 362 if isinstance(value, list): | |
| 363 norm_env[key] = ' '.join(value) | |
| 364 else: | |
| 365 norm_env[key] = value | |
| 366 return norm_env | |
| 367 | |
| 368 | |
| 369 def RunCommandOrShell(cmd_list, env): | |
| 370 ''' Run the command in cmd_list or a shell if cmd_list is empty. | |
| 371 | |
| 372 Run the command in cmd_list using a normalized copy of env. For instance, | |
| 373 cmd_list might contain the items ['make', 'application'], which would | |
| 374 cause command 'make application' to run in the current directory. If cmd_list | |
| 375 is empty, this function will spawn a new sbushell instead. | |
| 376 | |
| 377 Args: | |
| 378 cmd_list: the command list to run. | |
| 379 env: the environment to use. | |
| 380 ''' | |
| 381 # If cmd_list is empty, set it up to spawn a shell instead. If cmd_list | |
| 382 # isn't empty, it will run in the current shell (for security and so that the | |
| 383 # user can see the output). | |
| 384 new_shell = False | |
| 385 if cmd_list: | |
| 386 # Normalize cmd_list by building a list of individual arguments. | |
| 387 new_cmd_list = [] | |
| 388 for item in cmd_list: | |
| 389 new_cmd_list += item.split() | |
| 390 cmd_list = new_cmd_list | |
| 391 else: | |
| 392 # Build a shell command. | |
| 393 new_shell = True | |
| 394 if sys.platform == 'win32': | |
| 395 cmd_list = ['cmd'] | |
| 396 else: | |
| 397 cmd_list = ['/bin/bash', '-s'] | |
| 398 return subprocess.call(cmd_list, env=NormalizeEnv(env), shell=new_shell) | |
| 399 | |
| 400 | |
| 401 def GenerateBuildSettings(options, args): | |
| 402 ''' Generate the build settings and dump them or invoke a command. | |
| 403 | |
| 404 Given the cmd-line options and remaining cmd-line arguments, generate the | |
| 405 required build settings and either dump them to stdout or invoke a shell | |
| 406 command. | |
| 407 | |
| 408 Args: | |
| 409 options: cmd-line options. | |
| 410 args: unconsumed cmd-line arguments. | |
| 411 | |
| 412 Returns: | |
| 413 0 in case of success or a command result code otherwise. | |
| 414 ''' | |
| 415 # A few generated options, which we store in options for convenience. | |
| 416 options.host = __PLATFORM_TO_HOST_MAP[sys.platform] | |
| 417 options.sdk_root = GetNaclSdkRoot() | |
| 418 options.toolchain_path = GetToolchainPath(options) | |
| 419 | |
| 420 env = SetupX86Env(options) | |
| 421 if options.dump: | |
| 422 dump(options, env, template=options.format_template) | |
| 423 return 0 | |
| 424 else: | |
| 425 return RunCommandOrShell(args, env) | |
| 426 | |
| 427 | |
| 428 def main(argv): | |
| 429 ''' Do main stuff, mainly. | |
| 430 ''' | |
| 431 parser = optparse.OptionParser(usage=__GLOBAL_HELP) | |
| 432 parser.add_option( | |
| 433 '-a', '--arch', dest='arch', | |
| 434 choices=['x86-32', 'x86-64'], | |
| 435 default='x86-64', | |
| 436 help='The target architecture; one of x86-32 or x86-64. ' | |
| 437 '[default = %default.]') | |
| 438 parser.add_option( | |
| 439 '-A', '--no_ppapi', dest='no_ppapi', | |
| 440 default=False, | |
| 441 action='store_true', | |
| 442 help='Do not add -lppapi to the link settings.') | |
| 443 parser.add_option( | |
| 444 '-d', '--dump', dest='dump', | |
| 445 default=False, | |
| 446 action='store_true', | |
| 447 help='Dump the build settings to stdout') | |
| 448 parser.add_option( | |
| 449 '-D', '--pretty_print', dest='pretty_print', | |
| 450 default=False, | |
| 451 action='store_true', | |
| 452 help='Print section headers when dumping to stdout') | |
| 453 parser.add_option( | |
| 454 '-f', '--format_template', dest='format_template', | |
| 455 default=' {option}={value}', | |
| 456 help="The formatting template used to output (option, value) pairs." | |
| 457 "[default='%default.']") | |
| 458 parser.add_option( | |
| 459 '-n', '--no_merge', dest='merge', | |
| 460 default=True, | |
| 461 action='store_false', | |
| 462 help='Do not merge the build options with current environment. By default' | |
| 463 ' %prog merges the build flags with the current environment vars.' | |
| 464 ' This option turns that off.') | |
| 465 parser.add_option( | |
| 466 '-p', '--platform', dest='sdk_platform', | |
| 467 default=__DEFAULT_SDK_PLATFORM, | |
| 468 help='The SDK platform to use; e.g. pepper_16. [default = %default.]') | |
| 469 parser.add_option( | |
| 470 '-t', '--build_type', dest='build_type', | |
| 471 choices=__BUILD_TYPES.keys(), | |
| 472 default='debug', | |
| 473 help='The desired build type; one of debug or release.' | |
| 474 ' [default = %default.]') | |
| 475 parser.add_option( | |
| 476 '-v', '--variant', dest='lib_variant', | |
| 477 choices=['glibc', 'newlib'], default='newlib', | |
| 478 help='The lib variant to use; one of glibc or newlib. ' | |
| 479 '[default = %default.]') | |
| 480 | |
| 481 (options, args) = parser.parse_args(argv) | |
| 482 | |
| 483 # Verify that we're running on a supported host. | |
| 484 if sys.platform not in __PLATFORM_TO_HOST_MAP: | |
| 485 sys.stderr.write('Platform %s is not supported.' % sys.platform) | |
| 486 return 1 | |
| 487 | |
| 488 return GenerateBuildSettings(options, args) | |
| 489 | |
| 490 | |
| 491 if __name__ == '__main__': | |
| 492 sys.exit(main(sys.argv[1:])) | |
| OLD | NEW |