Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 # Copyright (c) 2012 Google Inc. All rights reserved. | 1 # Copyright (c) 2012 Google Inc. All rights reserved. |
| 2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
| 3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
| 4 | 4 |
| 5 from compiler.ast import Const | 5 from compiler.ast import Const |
| 6 from compiler.ast import Dict | 6 from compiler.ast import Dict |
| 7 from compiler.ast import Discard | 7 from compiler.ast import Discard |
| 8 from compiler.ast import List | 8 from compiler.ast import List |
| 9 from compiler.ast import Module | 9 from compiler.ast import Module |
| 10 from compiler.ast import Node | 10 from compiler.ast import Node |
| 11 from compiler.ast import Stmt | 11 from compiler.ast import Stmt |
| 12 import compiler | 12 import compiler |
| 13 import copy | 13 import copy |
| 14 import gyp.common | 14 import gyp.common |
| 15 import multiprocessing | |
| 15 import optparse | 16 import optparse |
| 16 import os.path | 17 import os.path |
| 17 import re | 18 import re |
| 18 import shlex | 19 import shlex |
| 19 import subprocess | 20 import subprocess |
| 20 import sys | 21 import sys |
| 22 import time | |
| 21 | 23 |
| 22 | 24 |
| 23 # A list of types that are treated as linkable. | 25 # A list of types that are treated as linkable. |
| 24 linkable_types = ['executable', 'shared_library', 'loadable_module'] | 26 linkable_types = ['executable', 'shared_library', 'loadable_module'] |
| 25 | 27 |
| 26 # A list of sections that contain links to other targets. | 28 # A list of sections that contain links to other targets. |
| 27 dependency_sections = ['dependencies', 'export_dependent_settings'] | 29 dependency_sections = ['dependencies', 'export_dependent_settings'] |
| 28 | 30 |
| 29 # base_path_sections is a list of sections defined by GYP that contain | 31 # base_path_sections is a list of sections defined by GYP that contain |
| 30 # pathnames. The generators can provide more keys, the two lists are merged | 32 # pathnames. The generators can provide more keys, the two lists are merged |
| (...skipping 291 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 322 for condition in data['conditions']: | 324 for condition in data['conditions']: |
| 323 if isinstance(condition, list): | 325 if isinstance(condition, list): |
| 324 for condition_dict in condition[1:]: | 326 for condition_dict in condition[1:]: |
| 325 ProcessToolsetsInDict(condition_dict) | 327 ProcessToolsetsInDict(condition_dict) |
| 326 | 328 |
| 327 | 329 |
| 328 # TODO(mark): I don't love this name. It just means that it's going to load | 330 # TODO(mark): I don't love this name. It just means that it's going to load |
| 329 # a build file that contains targets and is expected to provide a targets dict | 331 # a build file that contains targets and is expected to provide a targets dict |
| 330 # that contains the targets... | 332 # that contains the targets... |
| 331 def LoadTargetBuildFile(build_file_path, data, aux_data, variables, includes, | 333 def LoadTargetBuildFile(build_file_path, data, aux_data, variables, includes, |
| 332 depth, check): | 334 depth, check, load_dependencies): |
| 333 # If depth is set, predefine the DEPTH variable to be a relative path from | 335 # If depth is set, predefine the DEPTH variable to be a relative path from |
| 334 # this build file's directory to the directory identified by depth. | 336 # this build file's directory to the directory identified by depth. |
| 335 if depth: | 337 if depth: |
| 336 # TODO(dglazkov) The backslash/forward-slash replacement at the end is a | 338 # TODO(dglazkov) The backslash/forward-slash replacement at the end is a |
| 337 # temporary measure. This should really be addressed by keeping all paths | 339 # temporary measure. This should really be addressed by keeping all paths |
| 338 # in POSIX until actual project generation. | 340 # in POSIX until actual project generation. |
| 339 d = gyp.common.RelativePath(depth, os.path.dirname(build_file_path)) | 341 d = gyp.common.RelativePath(depth, os.path.dirname(build_file_path)) |
| 340 if d == '': | 342 if d == '': |
| 341 variables['DEPTH'] = '.' | 343 variables['DEPTH'] = '.' |
| 342 else: | 344 else: |
| 343 variables['DEPTH'] = d.replace('\\', '/') | 345 variables['DEPTH'] = d.replace('\\', '/') |
| 344 | 346 |
| 345 # If the generator needs absolue paths, then do so. | 347 # If the generator needs absolue paths, then do so. |
| 346 if absolute_build_file_paths: | 348 if absolute_build_file_paths: |
| 347 build_file_path = os.path.abspath(build_file_path) | 349 build_file_path = os.path.abspath(build_file_path) |
| 348 | 350 |
| 349 if build_file_path in data['target_build_files']: | 351 if build_file_path in data['target_build_files']: |
| 350 # Already loaded. | 352 # Already loaded. |
| 351 return | 353 return False |
| 352 data['target_build_files'].add(build_file_path) | 354 data['target_build_files'].add(build_file_path) |
| 353 | 355 |
| 354 gyp.DebugOutput(gyp.DEBUG_INCLUDES, | 356 gyp.DebugOutput(gyp.DEBUG_INCLUDES, |
| 355 "Loading Target Build File '%s'" % build_file_path) | 357 "Loading Target Build File '%s'" % build_file_path) |
| 356 | 358 |
| 357 build_file_data = LoadOneBuildFile(build_file_path, data, aux_data, variables, | 359 build_file_data = LoadOneBuildFile(build_file_path, data, aux_data, variables, |
| 358 includes, True, check) | 360 includes, True, check) |
| 359 | 361 |
| 360 # Store DEPTH for later use in generators. | 362 # Store DEPTH for later use in generators. |
| 361 build_file_data['_DEPTH'] = depth | 363 build_file_data['_DEPTH'] = depth |
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 411 "Unable to find targets in build file %s" % build_file_path | 413 "Unable to find targets in build file %s" % build_file_path |
| 412 | 414 |
| 413 # No longer needed. | 415 # No longer needed. |
| 414 del build_file_data['target_defaults'] | 416 del build_file_data['target_defaults'] |
| 415 | 417 |
| 416 # Look for dependencies. This means that dependency resolution occurs | 418 # Look for dependencies. This means that dependency resolution occurs |
| 417 # after "pre" conditionals and variable expansion, but before "post" - | 419 # after "pre" conditionals and variable expansion, but before "post" - |
| 418 # in other words, you can't put a "dependencies" section inside a "post" | 420 # in other words, you can't put a "dependencies" section inside a "post" |
| 419 # conditional within a target. | 421 # conditional within a target. |
| 420 | 422 |
| 423 dependencies = [] | |
| 421 if 'targets' in build_file_data: | 424 if 'targets' in build_file_data: |
| 422 for target_dict in build_file_data['targets']: | 425 for target_dict in build_file_data['targets']: |
| 423 if 'dependencies' not in target_dict: | 426 if 'dependencies' not in target_dict: |
| 424 continue | 427 continue |
| 425 for dependency in target_dict['dependencies']: | 428 for dependency in target_dict['dependencies']: |
| 426 other_build_file = \ | 429 dependencies.append( |
| 427 gyp.common.ResolveTarget(build_file_path, dependency, None)[0] | 430 gyp.common.ResolveTarget(build_file_path, dependency, None)[0]) |
| 428 try: | |
| 429 LoadTargetBuildFile(other_build_file, data, aux_data, variables, | |
| 430 includes, depth, check) | |
| 431 except Exception, e: | |
| 432 gyp.common.ExceptionAppend( | |
| 433 e, 'while loading dependencies of %s' % build_file_path) | |
| 434 raise | |
| 435 | 431 |
| 436 return data | 432 if load_dependencies: |
| 433 for dependency in dependencies: | |
| 434 try: | |
| 435 LoadTargetBuildFile(dependency, data, aux_data, variables, | |
| 436 includes, depth, check, load_dependencies) | |
| 437 except Exception, e: | |
| 438 gyp.common.ExceptionAppend( | |
| 439 e, 'while loading dependencies of %s' % build_file_path) | |
| 440 raise | |
| 441 else: | |
| 442 return (build_file_path, dependencies) | |
| 443 | |
| 444 | |
| 445 # Wrapper around LoadTargetBuildFile used when LoadTargetBuildFile | |
| 446 # is executed in a worker process. | |
| 447 def CallLoadTargetBuildFile(global_flags, | |
| 448 build_file_path, data, | |
| 449 aux_data, variables, | |
| 450 includes, depth, check): | |
| 451 # Apply globals so that the worker process behaves the same. | |
| 452 for key, value in global_flags.iteritems(): | |
| 453 globals()[key] = value | |
| 454 | |
| 455 # Save the keys so we can return data that changed. | |
| 456 data_keys = set(data) | |
| 457 aux_data_keys = set(aux_data) | |
| 458 | |
| 459 result = LoadTargetBuildFile(build_file_path, data, | |
| 460 aux_data, variables, | |
| 461 includes, depth, check, False) | |
| 462 if not result: | |
| 463 return result | |
| 464 | |
| 465 (build_file_path, dependencies) = result | |
| 466 | |
| 467 data_out = {} | |
| 468 for key in data: | |
| 469 if key == 'target_build_files': | |
| 470 continue | |
| 471 if key not in data_keys: | |
| 472 data_out[key] = data[key] | |
| 473 aux_data_out = {} | |
| 474 for key in aux_data: | |
| 475 if key not in aux_data_keys: | |
| 476 aux_data_out[key] = aux_data[key] | |
| 477 | |
| 478 # This gets serialized and sent back to the main process via a pipe. | |
| 479 # It's handled in LoadTargetBuildFileCallback. | |
| 480 return (build_file_path, | |
| 481 data_out, | |
| 482 aux_data_out, | |
| 483 dependencies) | |
| 484 | |
| 485 | |
| 486 # Handle the results of LoadTargetBuildFile that executed in a separate | |
| 487 # process. | |
| 488 def LoadTargetBuildFileCallback(result): | |
| 489 (build_file_path0, data0, aux_data0, dependencies0) = result | |
| 490 gyp.data['target_build_files'].add(build_file_path0) | |
|
M-A Ruel
2012/09/18 19:42:18
Can you tell me where the "gyp" variable is define
dmazzoni
2012/09/19 21:14:52
gyp is the module object.
I cleaned this up by cr
| |
| 491 for key in data0: | |
| 492 gyp.data[key] = data0[key] | |
| 493 for key in aux_data0: | |
| 494 gyp.aux_data[key] = aux_data0[key] | |
| 495 for new_dependency in dependencies0: | |
| 496 if new_dependency not in gyp.scheduled: | |
| 497 gyp.scheduled.add(new_dependency) | |
| 498 gyp.dependencies.append(new_dependency) | |
| 499 gyp.pending -= 1 | |
| 500 | |
| 501 | |
| 502 def LoadTargetBuildFileParallel(build_file_path, data, aux_data, | |
| 503 variables, includes, depth, check): | |
| 504 gyp.dependencies = [build_file_path] | |
|
M-A Ruel
2012/09/18 19:42:18
I find the code a bit hard to read, the "gyp" vari
| |
| 505 gyp.scheduled = set([build_file_path]) | |
| 506 gyp.pending = 0 | |
| 507 gyp.data = data | |
| 508 gyp.aux_data = aux_data | |
| 509 while gyp.dependencies or gyp.pending: | |
| 510 if not gyp.dependencies: | |
| 511 time.sleep(0.003) | |
| 512 continue | |
| 513 | |
| 514 dependency = gyp.dependencies.pop() | |
| 515 | |
| 516 gyp.pending += 1 | |
| 517 data_in = {} | |
| 518 data_in['target_build_files'] = data['target_build_files'] | |
| 519 aux_data_in = {} | |
| 520 global_flags = { | |
| 521 'path_sections': globals()['path_sections'], | |
| 522 'non_configuration_keys': globals()['non_configuration_keys'], | |
| 523 'absolute_build_file_paths': globals()['absolute_build_file_paths'], | |
| 524 'multiple_toolsets': globals()['multiple_toolsets']} | |
| 525 | |
| 526 if 'pool' not in dir(gyp): | |
| 527 gyp.pool = multiprocessing.Pool(8) | |
| 528 gyp.pool.apply_async( | |
| 529 CallLoadTargetBuildFile, | |
| 530 args = (global_flags, dependency, | |
| 531 data_in, aux_data_in, | |
| 532 variables, includes, depth, check), | |
| 533 callback = LoadTargetBuildFileCallback) | |
| 534 time.sleep(0.003) | |
| 437 | 535 |
| 438 | 536 |
| 439 # Look for the bracket that matches the first bracket seen in a | 537 # Look for the bracket that matches the first bracket seen in a |
| 440 # string, and return the start and end as a tuple. For example, if | 538 # string, and return the start and end as a tuple. For example, if |
| 441 # the input is something like "<(foo <(bar)) blah", then it would | 539 # the input is something like "<(foo <(bar)) blah", then it would |
| 442 # return (1, 13), indicating the entire string except for the leading | 540 # return (1, 13), indicating the entire string except for the leading |
| 443 # "<" and trailing " blah". | 541 # "<" and trailing " blah". |
| 444 def FindEnclosingBracketGroup(input): | 542 def FindEnclosingBracketGroup(input): |
| 445 brackets = { '}': '{', | 543 brackets = { '}': '{', |
| 446 ']': '[', | 544 ']': '[', |
| (...skipping 1879 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2326 # Prepare a key like 'path/to:target_name'. | 2424 # Prepare a key like 'path/to:target_name'. |
| 2327 key = subdir + ':' + name | 2425 key = subdir + ':' + name |
| 2328 if key in used: | 2426 if key in used: |
| 2329 # Complain if this target is already used. | 2427 # Complain if this target is already used. |
| 2330 raise Exception('Duplicate target name "%s" in directory "%s" used both ' | 2428 raise Exception('Duplicate target name "%s" in directory "%s" used both ' |
| 2331 'in "%s" and "%s".' % (name, subdir, gyp, used[key])) | 2429 'in "%s" and "%s".' % (name, subdir, gyp, used[key])) |
| 2332 used[key] = gyp | 2430 used[key] = gyp |
| 2333 | 2431 |
| 2334 | 2432 |
| 2335 def Load(build_files, variables, includes, depth, generator_input_info, check, | 2433 def Load(build_files, variables, includes, depth, generator_input_info, check, |
| 2336 circular_check): | 2434 circular_check, parallel): |
| 2337 # Set up path_sections and non_configuration_keys with the default data plus | 2435 # Set up path_sections and non_configuration_keys with the default data plus |
| 2338 # the generator-specifc data. | 2436 # the generator-specifc data. |
| 2339 global path_sections | 2437 global path_sections |
| 2340 path_sections = base_path_sections[:] | 2438 path_sections = base_path_sections[:] |
| 2341 path_sections.extend(generator_input_info['path_sections']) | 2439 path_sections.extend(generator_input_info['path_sections']) |
| 2342 | 2440 |
| 2343 global non_configuration_keys | 2441 global non_configuration_keys |
| 2344 non_configuration_keys = base_non_configuration_keys[:] | 2442 non_configuration_keys = base_non_configuration_keys[:] |
| 2345 non_configuration_keys.extend(generator_input_info['non_configuration_keys']) | 2443 non_configuration_keys.extend(generator_input_info['non_configuration_keys']) |
| 2346 | 2444 |
| (...skipping 20 matching lines...) Expand all Loading... | |
| 2367 # NOTE: data contains both "target" files (.gyp) and "includes" (.gypi), as | 2465 # NOTE: data contains both "target" files (.gyp) and "includes" (.gypi), as |
| 2368 # well as meta-data (e.g. 'included_files' key). 'target_build_files' keeps | 2466 # well as meta-data (e.g. 'included_files' key). 'target_build_files' keeps |
| 2369 # track of the keys corresponding to "target" files. | 2467 # track of the keys corresponding to "target" files. |
| 2370 data = {'target_build_files': set()} | 2468 data = {'target_build_files': set()} |
| 2371 aux_data = {} | 2469 aux_data = {} |
| 2372 for build_file in build_files: | 2470 for build_file in build_files: |
| 2373 # Normalize paths everywhere. This is important because paths will be | 2471 # Normalize paths everywhere. This is important because paths will be |
| 2374 # used as keys to the data dict and for references between input files. | 2472 # used as keys to the data dict and for references between input files. |
| 2375 build_file = os.path.normpath(build_file) | 2473 build_file = os.path.normpath(build_file) |
| 2376 try: | 2474 try: |
| 2377 LoadTargetBuildFile(build_file, data, aux_data, variables, includes, | 2475 if parallel: |
| 2378 depth, check) | 2476 print >>sys.stderr, 'Using parallel processing (experimental).' |
| 2477 LoadTargetBuildFileParallel(build_file, data, aux_data, | |
| 2478 variables, includes, depth, check) | |
| 2479 else: | |
| 2480 LoadTargetBuildFile(build_file, data, aux_data, | |
| 2481 variables, includes, depth, check, True) | |
| 2379 except Exception, e: | 2482 except Exception, e: |
| 2380 gyp.common.ExceptionAppend(e, 'while trying to load %s' % build_file) | 2483 gyp.common.ExceptionAppend(e, 'while trying to load %s' % build_file) |
| 2381 raise | 2484 raise |
| 2382 | 2485 |
| 2383 # Build a dict to access each target's subdict by qualified name. | 2486 # Build a dict to access each target's subdict by qualified name. |
| 2384 targets = BuildTargetsDict(data) | 2487 targets = BuildTargetsDict(data) |
| 2385 | 2488 |
| 2386 # Fully qualify all dependency links. | 2489 # Fully qualify all dependency links. |
| 2387 QualifyDependencies(targets) | 2490 QualifyDependencies(targets) |
| 2388 | 2491 |
| (...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2476 ValidateRunAsInTarget(target, target_dict, build_file) | 2579 ValidateRunAsInTarget(target, target_dict, build_file) |
| 2477 ValidateActionsInTarget(target, target_dict, build_file) | 2580 ValidateActionsInTarget(target, target_dict, build_file) |
| 2478 | 2581 |
| 2479 # Generators might not expect ints. Turn them into strs. | 2582 # Generators might not expect ints. Turn them into strs. |
| 2480 TurnIntIntoStrInDict(data) | 2583 TurnIntIntoStrInDict(data) |
| 2481 | 2584 |
| 2482 # TODO(mark): Return |data| for now because the generator needs a list of | 2585 # TODO(mark): Return |data| for now because the generator needs a list of |
| 2483 # build files that came in. In the future, maybe it should just accept | 2586 # build files that came in. In the future, maybe it should just accept |
| 2484 # a list, and not the whole data dict. | 2587 # a list, and not the whole data dict. |
| 2485 return [flat_list, targets, data] | 2588 return [flat_list, targets, data] |
| OLD | NEW |