1beab9a2ff9fcbd27d4b1c951d3fc6e6f09230223thakis@chromium.org# Copyright (c) 2012 Google Inc. All rights reserved. 2df8224662e615bd36cf8bebae8e58c017201f998sgk@chromium.org# Use of this source code is governed by a BSD-style license that can be 3df8224662e615bd36cf8bebae8e58c017201f998sgk@chromium.org# found in the LICENSE file. 4df8224662e615bd36cf8bebae8e58c017201f998sgk@chromium.org 542d9b6828d9442eedd144ae9b1937be280b19e01thakis@chromium.orgfrom __future__ import with_statement 642d9b6828d9442eedd144ae9b1937be280b19e01thakis@chromium.org 79f14141afe735cf6ae93532ddd97f98fa9ea2821thakis@chromium.orgimport collections 8c376dab1c44c36bf607de14b8204c27bd6e6ce0bbradnelson@google.comimport errno 9c376dab1c44c36bf607de14b8204c27bd6e6ce0bbradnelson@google.comimport filecmp 10f6d65a99233197a7b2e194d19be63ec85ff635d2mark@chromium.orgimport os.path 110be4796de4a43311feb4cca55725f685f5182581mark@chromium.orgimport re 12c376dab1c44c36bf607de14b8204c27bd6e6ce0bbradnelson@google.comimport tempfile 130954e99802e6c9c911b87c862c7ca179e4a4a114bradnelson@google.comimport sys 14f6d65a99233197a7b2e194d19be63ec85ff635d2mark@chromium.org 15f5a52009064941d0b668717e5cd92945643acb95thakis@chromium.org 16f5a52009064941d0b668717e5cd92945643acb95thakis@chromium.org# A minimal memoizing decorator. It'll blow up if the args aren't immutable, 17f5a52009064941d0b668717e5cd92945643acb95thakis@chromium.org# among other "problems". 18f5a52009064941d0b668717e5cd92945643acb95thakis@chromium.orgclass memoize(object): 19f5a52009064941d0b668717e5cd92945643acb95thakis@chromium.org def __init__(self, func): 20f5a52009064941d0b668717e5cd92945643acb95thakis@chromium.org self.func = func 21f5a52009064941d0b668717e5cd92945643acb95thakis@chromium.org self.cache = {} 22f5a52009064941d0b668717e5cd92945643acb95thakis@chromium.org def __call__(self, *args): 23f5a52009064941d0b668717e5cd92945643acb95thakis@chromium.org try: 24f5a52009064941d0b668717e5cd92945643acb95thakis@chromium.org return self.cache[args] 25f5a52009064941d0b668717e5cd92945643acb95thakis@chromium.org except KeyError: 26f5a52009064941d0b668717e5cd92945643acb95thakis@chromium.org result = self.func(*args) 27f5a52009064941d0b668717e5cd92945643acb95thakis@chromium.org self.cache[args] = result 28f5a52009064941d0b668717e5cd92945643acb95thakis@chromium.org return result 29f5a52009064941d0b668717e5cd92945643acb95thakis@chromium.org 30f5a52009064941d0b668717e5cd92945643acb95thakis@chromium.org 31d3d94fcdfb9745d6784065cade320c2462924df9sbc@chromium.orgclass GypError(Exception): 32d3d94fcdfb9745d6784065cade320c2462924df9sbc@chromium.org """Error class representing an error, which is to be presented 33d3d94fcdfb9745d6784065cade320c2462924df9sbc@chromium.org to the user. The main entry point will catch and display this. 34d3d94fcdfb9745d6784065cade320c2462924df9sbc@chromium.org """ 35d3d94fcdfb9745d6784065cade320c2462924df9sbc@chromium.org pass 36d3d94fcdfb9745d6784065cade320c2462924df9sbc@chromium.org 37d3d94fcdfb9745d6784065cade320c2462924df9sbc@chromium.org 38e162e112f7db7dca52aa668996e828315777bbaagspencer@google.comdef ExceptionAppend(e, msg): 39e162e112f7db7dca52aa668996e828315777bbaagspencer@google.com """Append a message to the given exception's message.""" 40e162e112f7db7dca52aa668996e828315777bbaagspencer@google.com if not e.args: 41e162e112f7db7dca52aa668996e828315777bbaagspencer@google.com e.args = (msg,) 42e162e112f7db7dca52aa668996e828315777bbaagspencer@google.com elif len(e.args) == 1: 43e162e112f7db7dca52aa668996e828315777bbaagspencer@google.com e.args = (str(e.args[0]) + ' ' + msg,) 44e162e112f7db7dca52aa668996e828315777bbaagspencer@google.com else: 45e162e112f7db7dca52aa668996e828315777bbaagspencer@google.com e.args = (str(e.args[0]) + ' ' + msg,) + e.args[1:] 46e162e112f7db7dca52aa668996e828315777bbaagspencer@google.com 47f6d65a99233197a7b2e194d19be63ec85ff635d2mark@chromium.org 48beca12a56c0772fc8263ad7ca12ca022b8671c1csbaigdef FindQualifiedTargets(target, qualified_list): 49beca12a56c0772fc8263ad7ca12ca022b8671c1csbaig """ 50beca12a56c0772fc8263ad7ca12ca022b8671c1csbaig Given a list of qualified targets, return the qualified targets for the 51beca12a56c0772fc8263ad7ca12ca022b8671c1csbaig specified |target|. 52beca12a56c0772fc8263ad7ca12ca022b8671c1csbaig """ 53beca12a56c0772fc8263ad7ca12ca022b8671c1csbaig return [t for t in qualified_list if ParseQualifiedTarget(t)[1] == target] 54beca12a56c0772fc8263ad7ca12ca022b8671c1csbaig 55beca12a56c0772fc8263ad7ca12ca022b8671c1csbaig 56821df584822a40838f71013666f9a9ccd3fa8fcfbradnelson@google.comdef ParseQualifiedTarget(target): 57821df584822a40838f71013666f9a9ccd3fa8fcfbradnelson@google.com # Splits a qualified target into a build file, target name and toolset. 58f6d65a99233197a7b2e194d19be63ec85ff635d2mark@chromium.org 593adee9349f3d710dd2159907072bc20413e82b46bradnelson@google.com # NOTE: rsplit is used to disambiguate the Windows drive letter separator. 603adee9349f3d710dd2159907072bc20413e82b46bradnelson@google.com target_split = target.rsplit(':', 1) 61f6d65a99233197a7b2e194d19be63ec85ff635d2mark@chromium.org if len(target_split) == 2: 62821df584822a40838f71013666f9a9ccd3fa8fcfbradnelson@google.com [build_file, target] = target_split 63821df584822a40838f71013666f9a9ccd3fa8fcfbradnelson@google.com else: 64821df584822a40838f71013666f9a9ccd3fa8fcfbradnelson@google.com build_file = None 65821df584822a40838f71013666f9a9ccd3fa8fcfbradnelson@google.com 66821df584822a40838f71013666f9a9ccd3fa8fcfbradnelson@google.com target_split = target.rsplit('#', 1) 67821df584822a40838f71013666f9a9ccd3fa8fcfbradnelson@google.com if len(target_split) == 2: 68821df584822a40838f71013666f9a9ccd3fa8fcfbradnelson@google.com [target, toolset] = target_split 69821df584822a40838f71013666f9a9ccd3fa8fcfbradnelson@google.com else: 70821df584822a40838f71013666f9a9ccd3fa8fcfbradnelson@google.com toolset = None 71821df584822a40838f71013666f9a9ccd3fa8fcfbradnelson@google.com 72821df584822a40838f71013666f9a9ccd3fa8fcfbradnelson@google.com return [build_file, target, toolset] 73821df584822a40838f71013666f9a9ccd3fa8fcfbradnelson@google.com 74821df584822a40838f71013666f9a9ccd3fa8fcfbradnelson@google.com 75821df584822a40838f71013666f9a9ccd3fa8fcfbradnelson@google.comdef ResolveTarget(build_file, target, toolset): 76821df584822a40838f71013666f9a9ccd3fa8fcfbradnelson@google.com # This function resolves a target into a canonical form: 77821df584822a40838f71013666f9a9ccd3fa8fcfbradnelson@google.com # - a fully defined build file, either absolute or relative to the current 78821df584822a40838f71013666f9a9ccd3fa8fcfbradnelson@google.com # directory 79821df584822a40838f71013666f9a9ccd3fa8fcfbradnelson@google.com # - a target name 80821df584822a40838f71013666f9a9ccd3fa8fcfbradnelson@google.com # - a toolset 81821df584822a40838f71013666f9a9ccd3fa8fcfbradnelson@google.com # 82821df584822a40838f71013666f9a9ccd3fa8fcfbradnelson@google.com # build_file is the file relative to which 'target' is defined. 83821df584822a40838f71013666f9a9ccd3fa8fcfbradnelson@google.com # target is the qualified target. 84821df584822a40838f71013666f9a9ccd3fa8fcfbradnelson@google.com # toolset is the default toolset for that target. 85821df584822a40838f71013666f9a9ccd3fa8fcfbradnelson@google.com [parsed_build_file, target, parsed_toolset] = ParseQualifiedTarget(target) 86821df584822a40838f71013666f9a9ccd3fa8fcfbradnelson@google.com 87821df584822a40838f71013666f9a9ccd3fa8fcfbradnelson@google.com if parsed_build_file: 88821df584822a40838f71013666f9a9ccd3fa8fcfbradnelson@google.com if build_file: 89821df584822a40838f71013666f9a9ccd3fa8fcfbradnelson@google.com # If a relative path, parsed_build_file is relative to the directory 90821df584822a40838f71013666f9a9ccd3fa8fcfbradnelson@google.com # containing build_file. If build_file is not in the current directory, 91821df584822a40838f71013666f9a9ccd3fa8fcfbradnelson@google.com # parsed_build_file is not a usable path as-is. Resolve it by 92821df584822a40838f71013666f9a9ccd3fa8fcfbradnelson@google.com # interpreting it as relative to build_file. If parsed_build_file is 93821df584822a40838f71013666f9a9ccd3fa8fcfbradnelson@google.com # absolute, it is usable as a path regardless of the current directory, 94821df584822a40838f71013666f9a9ccd3fa8fcfbradnelson@google.com # and os.path.join will return it as-is. 95821df584822a40838f71013666f9a9ccd3fa8fcfbradnelson@google.com build_file = os.path.normpath(os.path.join(os.path.dirname(build_file), 96821df584822a40838f71013666f9a9ccd3fa8fcfbradnelson@google.com parsed_build_file)) 97b7b7f31931e287d42e767840249194570d07f975bradnelson@google.com # Further (to handle cases like ../cwd), make it relative to cwd) 9827aaada3227da7f17b70ae773808dc149da70cc4bradnelson@google.com if not os.path.isabs(build_file): 9927aaada3227da7f17b70ae773808dc149da70cc4bradnelson@google.com build_file = RelativePath(build_file, '.') 100821df584822a40838f71013666f9a9ccd3fa8fcfbradnelson@google.com else: 101821df584822a40838f71013666f9a9ccd3fa8fcfbradnelson@google.com build_file = parsed_build_file 102821df584822a40838f71013666f9a9ccd3fa8fcfbradnelson@google.com 103821df584822a40838f71013666f9a9ccd3fa8fcfbradnelson@google.com if parsed_toolset: 104821df584822a40838f71013666f9a9ccd3fa8fcfbradnelson@google.com toolset = parsed_toolset 105821df584822a40838f71013666f9a9ccd3fa8fcfbradnelson@google.com 106821df584822a40838f71013666f9a9ccd3fa8fcfbradnelson@google.com return [build_file, target, toolset] 107f6d65a99233197a7b2e194d19be63ec85ff635d2mark@chromium.org 108f6d65a99233197a7b2e194d19be63ec85ff635d2mark@chromium.org 109821df584822a40838f71013666f9a9ccd3fa8fcfbradnelson@google.comdef BuildFile(fully_qualified_target): 110821df584822a40838f71013666f9a9ccd3fa8fcfbradnelson@google.com # Extracts the build file from the fully qualified target. 111821df584822a40838f71013666f9a9ccd3fa8fcfbradnelson@google.com return ParseQualifiedTarget(fully_qualified_target)[0] 112f6d65a99233197a7b2e194d19be63ec85ff635d2mark@chromium.org 113f6d65a99233197a7b2e194d19be63ec85ff635d2mark@chromium.org 11454d2f6fe6d8a7b9d9786bd1f8540df6b4f46b83fsbc@chromium.orgdef GetEnvironFallback(var_list, default): 11554d2f6fe6d8a7b9d9786bd1f8540df6b4f46b83fsbc@chromium.org """Look up a key in the environment, with fallback to secondary keys 11654d2f6fe6d8a7b9d9786bd1f8540df6b4f46b83fsbc@chromium.org and finally falling back to a default value.""" 11754d2f6fe6d8a7b9d9786bd1f8540df6b4f46b83fsbc@chromium.org for var in var_list: 11854d2f6fe6d8a7b9d9786bd1f8540df6b4f46b83fsbc@chromium.org if var in os.environ: 11954d2f6fe6d8a7b9d9786bd1f8540df6b4f46b83fsbc@chromium.org return os.environ[var] 12054d2f6fe6d8a7b9d9786bd1f8540df6b4f46b83fsbc@chromium.org return default 12154d2f6fe6d8a7b9d9786bd1f8540df6b4f46b83fsbc@chromium.org 12254d2f6fe6d8a7b9d9786bd1f8540df6b4f46b83fsbc@chromium.org 123821df584822a40838f71013666f9a9ccd3fa8fcfbradnelson@google.comdef QualifiedTarget(build_file, target, toolset): 124f6d65a99233197a7b2e194d19be63ec85ff635d2mark@chromium.org # "Qualified" means the file that a target was defined in and the target 125821df584822a40838f71013666f9a9ccd3fa8fcfbradnelson@google.com # name, separated by a colon, suffixed by a # and the toolset name: 126821df584822a40838f71013666f9a9ccd3fa8fcfbradnelson@google.com # /path/to/file.gyp:target_name#toolset 127821df584822a40838f71013666f9a9ccd3fa8fcfbradnelson@google.com fully_qualified = build_file + ':' + target 128821df584822a40838f71013666f9a9ccd3fa8fcfbradnelson@google.com if toolset: 129821df584822a40838f71013666f9a9ccd3fa8fcfbradnelson@google.com fully_qualified = fully_qualified + '#' + toolset 130821df584822a40838f71013666f9a9ccd3fa8fcfbradnelson@google.com return fully_qualified 131f6d65a99233197a7b2e194d19be63ec85ff635d2mark@chromium.org 132f6d65a99233197a7b2e194d19be63ec85ff635d2mark@chromium.org 133f5a52009064941d0b668717e5cd92945643acb95thakis@chromium.org@memoize 134f6d65a99233197a7b2e194d19be63ec85ff635d2mark@chromium.orgdef RelativePath(path, relative_to): 135f6d65a99233197a7b2e194d19be63ec85ff635d2mark@chromium.org # Assuming both |path| and |relative_to| are relative to the current 136f6d65a99233197a7b2e194d19be63ec85ff635d2mark@chromium.org # directory, returns a relative path that identifies path relative to 137f6d65a99233197a7b2e194d19be63ec85ff635d2mark@chromium.org # relative_to. 138f6d65a99233197a7b2e194d19be63ec85ff635d2mark@chromium.org 1392724da9e5f57957af502ff40ed586fd6351a5f71thakis@chromium.org # Convert to normalized (and therefore absolute paths). 1402724da9e5f57957af502ff40ed586fd6351a5f71thakis@chromium.org path = os.path.realpath(path) 1412724da9e5f57957af502ff40ed586fd6351a5f71thakis@chromium.org relative_to = os.path.realpath(relative_to) 142f6d65a99233197a7b2e194d19be63ec85ff635d2mark@chromium.org 143ecf073cd05739a0848ad6b0928b48b4776ef1ff2scottmg@chromium.org # On Windows, we can't create a relative path to a different drive, so just 144ecf073cd05739a0848ad6b0928b48b4776ef1ff2scottmg@chromium.org # use the absolute path. 145ecf073cd05739a0848ad6b0928b48b4776ef1ff2scottmg@chromium.org if sys.platform == 'win32': 146ecf073cd05739a0848ad6b0928b48b4776ef1ff2scottmg@chromium.org if (os.path.splitdrive(path)[0].lower() != 147ecf073cd05739a0848ad6b0928b48b4776ef1ff2scottmg@chromium.org os.path.splitdrive(relative_to)[0].lower()): 148ecf073cd05739a0848ad6b0928b48b4776ef1ff2scottmg@chromium.org return path 149ecf073cd05739a0848ad6b0928b48b4776ef1ff2scottmg@chromium.org 15011b2df7f913292655872b5fda2044ee225780118mmoss@chromium.org # Split the paths into components. 15111b2df7f913292655872b5fda2044ee225780118mmoss@chromium.org path_split = path.split(os.path.sep) 15211b2df7f913292655872b5fda2044ee225780118mmoss@chromium.org relative_to_split = relative_to.split(os.path.sep) 153f6d65a99233197a7b2e194d19be63ec85ff635d2mark@chromium.org 154f6d65a99233197a7b2e194d19be63ec85ff635d2mark@chromium.org # Determine how much of the prefix the two paths share. 155f6d65a99233197a7b2e194d19be63ec85ff635d2mark@chromium.org prefix_len = len(os.path.commonprefix([path_split, relative_to_split])) 156f6d65a99233197a7b2e194d19be63ec85ff635d2mark@chromium.org 157f6d65a99233197a7b2e194d19be63ec85ff635d2mark@chromium.org # Put enough ".." components to back up out of relative_to to the common 158f6d65a99233197a7b2e194d19be63ec85ff635d2mark@chromium.org # prefix, and then append the part of path_split after the common prefix. 159f6d65a99233197a7b2e194d19be63ec85ff635d2mark@chromium.org relative_split = [os.path.pardir] * (len(relative_to_split) - prefix_len) + \ 160f6d65a99233197a7b2e194d19be63ec85ff635d2mark@chromium.org path_split[prefix_len:] 161f6d65a99233197a7b2e194d19be63ec85ff635d2mark@chromium.org 162eeccd9e5d515b046419a3e90115ba4ecafad632fmark@chromium.org if len(relative_split) == 0: 163eeccd9e5d515b046419a3e90115ba4ecafad632fmark@chromium.org # The paths were the same. 164eeccd9e5d515b046419a3e90115ba4ecafad632fmark@chromium.org return '' 165eeccd9e5d515b046419a3e90115ba4ecafad632fmark@chromium.org 166f6d65a99233197a7b2e194d19be63ec85ff635d2mark@chromium.org # Turn it back into a string and we're done. 167f6d65a99233197a7b2e194d19be63ec85ff635d2mark@chromium.org return os.path.join(*relative_split) 1680be4796de4a43311feb4cca55725f685f5182581mark@chromium.org 1690be4796de4a43311feb4cca55725f685f5182581mark@chromium.org 1702724da9e5f57957af502ff40ed586fd6351a5f71thakis@chromium.org@memoize 1712724da9e5f57957af502ff40ed586fd6351a5f71thakis@chromium.orgdef InvertRelativePath(path, toplevel_dir=None): 1722724da9e5f57957af502ff40ed586fd6351a5f71thakis@chromium.org """Given a path like foo/bar that is relative to toplevel_dir, return 1732724da9e5f57957af502ff40ed586fd6351a5f71thakis@chromium.org the inverse relative path back to the toplevel_dir. 1742724da9e5f57957af502ff40ed586fd6351a5f71thakis@chromium.org 1752724da9e5f57957af502ff40ed586fd6351a5f71thakis@chromium.org E.g. os.path.normpath(os.path.join(path, InvertRelativePath(path))) 1762724da9e5f57957af502ff40ed586fd6351a5f71thakis@chromium.org should always produce the empty string, unless the path contains symlinks. 1772724da9e5f57957af502ff40ed586fd6351a5f71thakis@chromium.org """ 1782724da9e5f57957af502ff40ed586fd6351a5f71thakis@chromium.org if not path: 1792724da9e5f57957af502ff40ed586fd6351a5f71thakis@chromium.org return path 1802724da9e5f57957af502ff40ed586fd6351a5f71thakis@chromium.org toplevel_dir = '.' if toplevel_dir is None else toplevel_dir 1812724da9e5f57957af502ff40ed586fd6351a5f71thakis@chromium.org return RelativePath(toplevel_dir, os.path.join(toplevel_dir, path)) 1822724da9e5f57957af502ff40ed586fd6351a5f71thakis@chromium.org 1832724da9e5f57957af502ff40ed586fd6351a5f71thakis@chromium.org 184c01356ff413b71e18e80d081b33e9cc1cb923440evan@chromium.orgdef FixIfRelativePath(path, relative_to): 185c01356ff413b71e18e80d081b33e9cc1cb923440evan@chromium.org # Like RelativePath but returns |path| unchanged if it is absolute. 186c01356ff413b71e18e80d081b33e9cc1cb923440evan@chromium.org if os.path.isabs(path): 187c01356ff413b71e18e80d081b33e9cc1cb923440evan@chromium.org return path 188c01356ff413b71e18e80d081b33e9cc1cb923440evan@chromium.org return RelativePath(path, relative_to) 189c01356ff413b71e18e80d081b33e9cc1cb923440evan@chromium.org 190c01356ff413b71e18e80d081b33e9cc1cb923440evan@chromium.org 191c01356ff413b71e18e80d081b33e9cc1cb923440evan@chromium.orgdef UnrelativePath(path, relative_to): 192c01356ff413b71e18e80d081b33e9cc1cb923440evan@chromium.org # Assuming that |relative_to| is relative to the current directory, and |path| 193c01356ff413b71e18e80d081b33e9cc1cb923440evan@chromium.org # is a path relative to the dirname of |relative_to|, returns a path that 194c01356ff413b71e18e80d081b33e9cc1cb923440evan@chromium.org # identifies |path| relative to the current directory. 195c01356ff413b71e18e80d081b33e9cc1cb923440evan@chromium.org rel_dir = os.path.dirname(relative_to) 196c01356ff413b71e18e80d081b33e9cc1cb923440evan@chromium.org return os.path.normpath(os.path.join(rel_dir, path)) 197c01356ff413b71e18e80d081b33e9cc1cb923440evan@chromium.org 198c01356ff413b71e18e80d081b33e9cc1cb923440evan@chromium.org 199e757c99b5ca060877a8b00f0d3e7723e6462e71dmark@chromium.org# re objects used by EncodePOSIXShellArgument. See IEEE 1003.1 XCU.2.2 at 200e757c99b5ca060877a8b00f0d3e7723e6462e71dmark@chromium.org# http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_02 201e757c99b5ca060877a8b00f0d3e7723e6462e71dmark@chromium.org# and the documentation for various shells. 2020be4796de4a43311feb4cca55725f685f5182581mark@chromium.org 2030be4796de4a43311feb4cca55725f685f5182581mark@chromium.org# _quote is a pattern that should match any argument that needs to be quoted 2040be4796de4a43311feb4cca55725f685f5182581mark@chromium.org# with double-quotes by EncodePOSIXShellArgument. It matches the following 2050be4796de4a43311feb4cca55725f685f5182581mark@chromium.org# characters appearing anywhere in an argument: 206e757c99b5ca060877a8b00f0d3e7723e6462e71dmark@chromium.org# \t, \n, space parameter separators 207e757c99b5ca060877a8b00f0d3e7723e6462e71dmark@chromium.org# # comments 208e757c99b5ca060877a8b00f0d3e7723e6462e71dmark@chromium.org# $ expansions (quoted to always expand within one argument) 209ada32f03ce7c5fdd53398cc0fb112049d252c5edmark@chromium.org# % called out by IEEE 1003.1 XCU.2.2 210e757c99b5ca060877a8b00f0d3e7723e6462e71dmark@chromium.org# & job control 211e757c99b5ca060877a8b00f0d3e7723e6462e71dmark@chromium.org# ' quoting 212e757c99b5ca060877a8b00f0d3e7723e6462e71dmark@chromium.org# (, ) subshell execution 213ada32f03ce7c5fdd53398cc0fb112049d252c5edmark@chromium.org# *, ?, [ pathname expansion 214e757c99b5ca060877a8b00f0d3e7723e6462e71dmark@chromium.org# ; command delimiter 215e757c99b5ca060877a8b00f0d3e7723e6462e71dmark@chromium.org# <, >, | redirection 216ada32f03ce7c5fdd53398cc0fb112049d252c5edmark@chromium.org# = assignment 217ada32f03ce7c5fdd53398cc0fb112049d252c5edmark@chromium.org# {, } brace expansion (bash) 218e757c99b5ca060877a8b00f0d3e7723e6462e71dmark@chromium.org# ~ tilde expansion 2190be4796de4a43311feb4cca55725f685f5182581mark@chromium.org# It also matches the empty string, because "" (or '') is the only way to 2200be4796de4a43311feb4cca55725f685f5182581mark@chromium.org# represent an empty string literal argument to a POSIX shell. 221e757c99b5ca060877a8b00f0d3e7723e6462e71dmark@chromium.org# 222e757c99b5ca060877a8b00f0d3e7723e6462e71dmark@chromium.org# This does not match the characters in _escape, because those need to be 223e757c99b5ca060877a8b00f0d3e7723e6462e71dmark@chromium.org# backslash-escaped regardless of whether they appear in a double-quoted 224e757c99b5ca060877a8b00f0d3e7723e6462e71dmark@chromium.org# string. 225ada32f03ce7c5fdd53398cc0fb112049d252c5edmark@chromium.org_quote = re.compile('[\t\n #$%&\'()*;<=>?[{|}~]|^$') 2260be4796de4a43311feb4cca55725f685f5182581mark@chromium.org 2270be4796de4a43311feb4cca55725f685f5182581mark@chromium.org# _escape is a pattern that should match any character that needs to be 2280be4796de4a43311feb4cca55725f685f5182581mark@chromium.org# escaped with a backslash, whether or not the argument matched the _quote 229e757c99b5ca060877a8b00f0d3e7723e6462e71dmark@chromium.org# pattern. _escape is used with re.sub to backslash anything in _escape's 230e757c99b5ca060877a8b00f0d3e7723e6462e71dmark@chromium.org# first match group, hence the (parentheses) in the regular expression. 231e757c99b5ca060877a8b00f0d3e7723e6462e71dmark@chromium.org# 232e757c99b5ca060877a8b00f0d3e7723e6462e71dmark@chromium.org# _escape matches the following characters appearing anywhere in an argument: 2330be4796de4a43311feb4cca55725f685f5182581mark@chromium.org# " to prevent POSIX shells from interpreting this character for quoting 2340be4796de4a43311feb4cca55725f685f5182581mark@chromium.org# \ to prevent POSIX shells from interpreting this character for escaping 2350be4796de4a43311feb4cca55725f685f5182581mark@chromium.org# ` to prevent POSIX shells from interpreting this character for command 2360be4796de4a43311feb4cca55725f685f5182581mark@chromium.org# substitution 2370be4796de4a43311feb4cca55725f685f5182581mark@chromium.org# Missing from this list is $, because the desired behavior of 2380be4796de4a43311feb4cca55725f685f5182581mark@chromium.org# EncodePOSIXShellArgument is to permit parameter (variable) expansion. 2390be4796de4a43311feb4cca55725f685f5182581mark@chromium.org# 2400be4796de4a43311feb4cca55725f685f5182581mark@chromium.org# Also missing from this list is !, which bash will interpret as the history 2410be4796de4a43311feb4cca55725f685f5182581mark@chromium.org# expansion character when history is enabled. bash does not enable history 2420be4796de4a43311feb4cca55725f685f5182581mark@chromium.org# by default in non-interactive shells, so this is not thought to be a problem. 2430be4796de4a43311feb4cca55725f685f5182581mark@chromium.org# ! was omitted from this list because bash interprets "\!" as a literal string 2440be4796de4a43311feb4cca55725f685f5182581mark@chromium.org# including the backslash character (avoiding history expansion but retaining 2450be4796de4a43311feb4cca55725f685f5182581mark@chromium.org# the backslash), which would not be correct for argument encoding. Handling 2460be4796de4a43311feb4cca55725f685f5182581mark@chromium.org# this case properly would also be problematic because bash allows the history 2470be4796de4a43311feb4cca55725f685f5182581mark@chromium.org# character to be changed with the histchars shell variable. Fortunately, 2480be4796de4a43311feb4cca55725f685f5182581mark@chromium.org# as history is not enabled in non-interactive shells and 2490be4796de4a43311feb4cca55725f685f5182581mark@chromium.org# EncodePOSIXShellArgument is only expected to encode for non-interactive 2500be4796de4a43311feb4cca55725f685f5182581mark@chromium.org# shells, there is no room for error here by ignoring !. 251e757c99b5ca060877a8b00f0d3e7723e6462e71dmark@chromium.org_escape = re.compile(r'(["\\`])') 2520be4796de4a43311feb4cca55725f685f5182581mark@chromium.org 2530be4796de4a43311feb4cca55725f685f5182581mark@chromium.orgdef EncodePOSIXShellArgument(argument): 2540be4796de4a43311feb4cca55725f685f5182581mark@chromium.org """Encodes |argument| suitably for consumption by POSIX shells. 2550be4796de4a43311feb4cca55725f685f5182581mark@chromium.org 2560be4796de4a43311feb4cca55725f685f5182581mark@chromium.org argument may be quoted and escaped as necessary to ensure that POSIX shells 2570be4796de4a43311feb4cca55725f685f5182581mark@chromium.org treat the returned value as a literal representing the argument passed to 2580be4796de4a43311feb4cca55725f685f5182581mark@chromium.org this function. Parameter (variable) expansions beginning with $ are allowed 2590be4796de4a43311feb4cca55725f685f5182581mark@chromium.org to remain intact without escaping the $, to allow the argument to contain 2600be4796de4a43311feb4cca55725f685f5182581mark@chromium.org references to variables to be expanded by the shell. 2610be4796de4a43311feb4cca55725f685f5182581mark@chromium.org """ 2620be4796de4a43311feb4cca55725f685f5182581mark@chromium.org 263955725e2ea029ef03db7fdefff8a3ac0d09c7f6dmark@chromium.org if not isinstance(argument, str): 264955725e2ea029ef03db7fdefff8a3ac0d09c7f6dmark@chromium.org argument = str(argument) 265955725e2ea029ef03db7fdefff8a3ac0d09c7f6dmark@chromium.org 2660be4796de4a43311feb4cca55725f685f5182581mark@chromium.org if _quote.search(argument): 2670be4796de4a43311feb4cca55725f685f5182581mark@chromium.org quote = '"' 2680be4796de4a43311feb4cca55725f685f5182581mark@chromium.org else: 2690be4796de4a43311feb4cca55725f685f5182581mark@chromium.org quote = '' 2700be4796de4a43311feb4cca55725f685f5182581mark@chromium.org 271e757c99b5ca060877a8b00f0d3e7723e6462e71dmark@chromium.org encoded = quote + re.sub(_escape, r'\\\1', argument) + quote 2720be4796de4a43311feb4cca55725f685f5182581mark@chromium.org 2730be4796de4a43311feb4cca55725f685f5182581mark@chromium.org return encoded 2740be4796de4a43311feb4cca55725f685f5182581mark@chromium.org 2750be4796de4a43311feb4cca55725f685f5182581mark@chromium.org 2760be4796de4a43311feb4cca55725f685f5182581mark@chromium.orgdef EncodePOSIXShellList(list): 2770be4796de4a43311feb4cca55725f685f5182581mark@chromium.org """Encodes |list| suitably for consumption by POSIX shells. 2780be4796de4a43311feb4cca55725f685f5182581mark@chromium.org 2790be4796de4a43311feb4cca55725f685f5182581mark@chromium.org Returns EncodePOSIXShellArgument for each item in list, and joins them 2800be4796de4a43311feb4cca55725f685f5182581mark@chromium.org together using the space character as an argument separator. 2810be4796de4a43311feb4cca55725f685f5182581mark@chromium.org """ 2820be4796de4a43311feb4cca55725f685f5182581mark@chromium.org 2830be4796de4a43311feb4cca55725f685f5182581mark@chromium.org encoded_arguments = [] 2840be4796de4a43311feb4cca55725f685f5182581mark@chromium.org for argument in list: 2850be4796de4a43311feb4cca55725f685f5182581mark@chromium.org encoded_arguments.append(EncodePOSIXShellArgument(argument)) 2860be4796de4a43311feb4cca55725f685f5182581mark@chromium.org return ' '.join(encoded_arguments) 2876f97116adcdccf08c5fcd90ecd035023fffcc069sgk@chromium.org 2886f97116adcdccf08c5fcd90ecd035023fffcc069sgk@chromium.org 2896f97116adcdccf08c5fcd90ecd035023fffcc069sgk@chromium.orgdef DeepDependencyTargets(target_dicts, roots): 290714c0d76150ae9ba7ab68cd8d9cf0dc1423186a9bradnelson@google.com """Returns the recursive list of target dependencies.""" 2916f97116adcdccf08c5fcd90ecd035023fffcc069sgk@chromium.org dependencies = set() 292714c0d76150ae9ba7ab68cd8d9cf0dc1423186a9bradnelson@google.com pending = set(roots) 293714c0d76150ae9ba7ab68cd8d9cf0dc1423186a9bradnelson@google.com while pending: 294714c0d76150ae9ba7ab68cd8d9cf0dc1423186a9bradnelson@google.com # Pluck out one. 295714c0d76150ae9ba7ab68cd8d9cf0dc1423186a9bradnelson@google.com r = pending.pop() 296714c0d76150ae9ba7ab68cd8d9cf0dc1423186a9bradnelson@google.com # Skip if visited already. 297714c0d76150ae9ba7ab68cd8d9cf0dc1423186a9bradnelson@google.com if r in dependencies: 298714c0d76150ae9ba7ab68cd8d9cf0dc1423186a9bradnelson@google.com continue 299714c0d76150ae9ba7ab68cd8d9cf0dc1423186a9bradnelson@google.com # Add it. 300714c0d76150ae9ba7ab68cd8d9cf0dc1423186a9bradnelson@google.com dependencies.add(r) 301714c0d76150ae9ba7ab68cd8d9cf0dc1423186a9bradnelson@google.com # Add its children. 3026f97116adcdccf08c5fcd90ecd035023fffcc069sgk@chromium.org spec = target_dicts[r] 303714c0d76150ae9ba7ab68cd8d9cf0dc1423186a9bradnelson@google.com pending.update(set(spec.get('dependencies', []))) 304714c0d76150ae9ba7ab68cd8d9cf0dc1423186a9bradnelson@google.com pending.update(set(spec.get('dependencies_original', []))) 305714c0d76150ae9ba7ab68cd8d9cf0dc1423186a9bradnelson@google.com return list(dependencies - set(roots)) 3066f97116adcdccf08c5fcd90ecd035023fffcc069sgk@chromium.org 3076f97116adcdccf08c5fcd90ecd035023fffcc069sgk@chromium.org 3086f97116adcdccf08c5fcd90ecd035023fffcc069sgk@chromium.orgdef BuildFileTargets(target_list, build_file): 3096f97116adcdccf08c5fcd90ecd035023fffcc069sgk@chromium.org """From a target_list, returns the subset from the specified build_file. 3106f97116adcdccf08c5fcd90ecd035023fffcc069sgk@chromium.org """ 311821df584822a40838f71013666f9a9ccd3fa8fcfbradnelson@google.com return [p for p in target_list if BuildFile(p) == build_file] 3126f97116adcdccf08c5fcd90ecd035023fffcc069sgk@chromium.org 3136f97116adcdccf08c5fcd90ecd035023fffcc069sgk@chromium.org 3146f97116adcdccf08c5fcd90ecd035023fffcc069sgk@chromium.orgdef AllTargets(target_list, target_dicts, build_file): 3156f97116adcdccf08c5fcd90ecd035023fffcc069sgk@chromium.org """Returns all targets (direct and dependencies) for the specified build_file. 3166f97116adcdccf08c5fcd90ecd035023fffcc069sgk@chromium.org """ 3176f97116adcdccf08c5fcd90ecd035023fffcc069sgk@chromium.org bftargets = BuildFileTargets(target_list, build_file) 3186f97116adcdccf08c5fcd90ecd035023fffcc069sgk@chromium.org deptargets = DeepDependencyTargets(target_dicts, bftargets) 3196f97116adcdccf08c5fcd90ecd035023fffcc069sgk@chromium.org return bftargets + deptargets 320c376dab1c44c36bf607de14b8204c27bd6e6ce0bbradnelson@google.com 321c376dab1c44c36bf607de14b8204c27bd6e6ce0bbradnelson@google.com 322c376dab1c44c36bf607de14b8204c27bd6e6ce0bbradnelson@google.comdef WriteOnDiff(filename): 323c376dab1c44c36bf607de14b8204c27bd6e6ce0bbradnelson@google.com """Write to a file only if the new contents differ. 324c376dab1c44c36bf607de14b8204c27bd6e6ce0bbradnelson@google.com 325c376dab1c44c36bf607de14b8204c27bd6e6ce0bbradnelson@google.com Arguments: 326c376dab1c44c36bf607de14b8204c27bd6e6ce0bbradnelson@google.com filename: name of the file to potentially write to. 327c376dab1c44c36bf607de14b8204c27bd6e6ce0bbradnelson@google.com Returns: 328c376dab1c44c36bf607de14b8204c27bd6e6ce0bbradnelson@google.com A file like object which will write to temporary file and only overwrite 329c376dab1c44c36bf607de14b8204c27bd6e6ce0bbradnelson@google.com the target if it differs (on close). 330c376dab1c44c36bf607de14b8204c27bd6e6ce0bbradnelson@google.com """ 331c376dab1c44c36bf607de14b8204c27bd6e6ce0bbradnelson@google.com 332c376dab1c44c36bf607de14b8204c27bd6e6ce0bbradnelson@google.com class Writer: 333c376dab1c44c36bf607de14b8204c27bd6e6ce0bbradnelson@google.com """Wrapper around file which only covers the target if it differs.""" 334c376dab1c44c36bf607de14b8204c27bd6e6ce0bbradnelson@google.com def __init__(self): 335c376dab1c44c36bf607de14b8204c27bd6e6ce0bbradnelson@google.com # Pick temporary file. 336c376dab1c44c36bf607de14b8204c27bd6e6ce0bbradnelson@google.com tmp_fd, self.tmp_path = tempfile.mkstemp( 337c376dab1c44c36bf607de14b8204c27bd6e6ce0bbradnelson@google.com suffix='.tmp', 338c376dab1c44c36bf607de14b8204c27bd6e6ce0bbradnelson@google.com prefix=os.path.split(filename)[1] + '.gyp.', 339c376dab1c44c36bf607de14b8204c27bd6e6ce0bbradnelson@google.com dir=os.path.split(filename)[0]) 340c376dab1c44c36bf607de14b8204c27bd6e6ce0bbradnelson@google.com try: 341c376dab1c44c36bf607de14b8204c27bd6e6ce0bbradnelson@google.com self.tmp_file = os.fdopen(tmp_fd, 'wb') 342c376dab1c44c36bf607de14b8204c27bd6e6ce0bbradnelson@google.com except Exception: 343c376dab1c44c36bf607de14b8204c27bd6e6ce0bbradnelson@google.com # Don't leave turds behind. 344c376dab1c44c36bf607de14b8204c27bd6e6ce0bbradnelson@google.com os.unlink(self.tmp_path) 345c376dab1c44c36bf607de14b8204c27bd6e6ce0bbradnelson@google.com raise 346c376dab1c44c36bf607de14b8204c27bd6e6ce0bbradnelson@google.com 347c376dab1c44c36bf607de14b8204c27bd6e6ce0bbradnelson@google.com def __getattr__(self, attrname): 348c376dab1c44c36bf607de14b8204c27bd6e6ce0bbradnelson@google.com # Delegate everything else to self.tmp_file 349c376dab1c44c36bf607de14b8204c27bd6e6ce0bbradnelson@google.com return getattr(self.tmp_file, attrname) 350c376dab1c44c36bf607de14b8204c27bd6e6ce0bbradnelson@google.com 351c376dab1c44c36bf607de14b8204c27bd6e6ce0bbradnelson@google.com def close(self): 352c376dab1c44c36bf607de14b8204c27bd6e6ce0bbradnelson@google.com try: 353c376dab1c44c36bf607de14b8204c27bd6e6ce0bbradnelson@google.com # Close tmp file. 354c376dab1c44c36bf607de14b8204c27bd6e6ce0bbradnelson@google.com self.tmp_file.close() 355c376dab1c44c36bf607de14b8204c27bd6e6ce0bbradnelson@google.com # Determine if different. 356c376dab1c44c36bf607de14b8204c27bd6e6ce0bbradnelson@google.com same = False 357c376dab1c44c36bf607de14b8204c27bd6e6ce0bbradnelson@google.com try: 358c376dab1c44c36bf607de14b8204c27bd6e6ce0bbradnelson@google.com same = filecmp.cmp(self.tmp_path, filename, False) 359c376dab1c44c36bf607de14b8204c27bd6e6ce0bbradnelson@google.com except OSError, e: 360c376dab1c44c36bf607de14b8204c27bd6e6ce0bbradnelson@google.com if e.errno != errno.ENOENT: 361c376dab1c44c36bf607de14b8204c27bd6e6ce0bbradnelson@google.com raise 362c376dab1c44c36bf607de14b8204c27bd6e6ce0bbradnelson@google.com 363c376dab1c44c36bf607de14b8204c27bd6e6ce0bbradnelson@google.com if same: 364c376dab1c44c36bf607de14b8204c27bd6e6ce0bbradnelson@google.com # The new file is identical to the old one, just get rid of the new 365c376dab1c44c36bf607de14b8204c27bd6e6ce0bbradnelson@google.com # one. 366c376dab1c44c36bf607de14b8204c27bd6e6ce0bbradnelson@google.com os.unlink(self.tmp_path) 367c376dab1c44c36bf607de14b8204c27bd6e6ce0bbradnelson@google.com else: 368c376dab1c44c36bf607de14b8204c27bd6e6ce0bbradnelson@google.com # The new file is different from the old one, or there is no old one. 369c376dab1c44c36bf607de14b8204c27bd6e6ce0bbradnelson@google.com # Rename the new file to the permanent name. 370c376dab1c44c36bf607de14b8204c27bd6e6ce0bbradnelson@google.com # 371c376dab1c44c36bf607de14b8204c27bd6e6ce0bbradnelson@google.com # tempfile.mkstemp uses an overly restrictive mode, resulting in a 372c376dab1c44c36bf607de14b8204c27bd6e6ce0bbradnelson@google.com # file that can only be read by the owner, regardless of the umask. 373c376dab1c44c36bf607de14b8204c27bd6e6ce0bbradnelson@google.com # There's no reason to not respect the umask here, which means that 374c376dab1c44c36bf607de14b8204c27bd6e6ce0bbradnelson@google.com # an extra hoop is required to fetch it and reset the new file's mode. 375c376dab1c44c36bf607de14b8204c27bd6e6ce0bbradnelson@google.com # 376c376dab1c44c36bf607de14b8204c27bd6e6ce0bbradnelson@google.com # No way to get the umask without setting a new one? Set a safe one 377c376dab1c44c36bf607de14b8204c27bd6e6ce0bbradnelson@google.com # and then set it back to the old value. 378c376dab1c44c36bf607de14b8204c27bd6e6ce0bbradnelson@google.com umask = os.umask(077) 379c376dab1c44c36bf607de14b8204c27bd6e6ce0bbradnelson@google.com os.umask(umask) 380c376dab1c44c36bf607de14b8204c27bd6e6ce0bbradnelson@google.com os.chmod(self.tmp_path, 0666 & ~umask) 381ca2d05c4a124c6685179af154f770397ca593bc9sgk@chromium.org if sys.platform == 'win32' and os.path.exists(filename): 3820954e99802e6c9c911b87c862c7ca179e4a4a114bradnelson@google.com # NOTE: on windows (but not cygwin) rename will not replace an 3830954e99802e6c9c911b87c862c7ca179e4a4a114bradnelson@google.com # existing file, so it must be preceded with a remove. Sadly there 3840954e99802e6c9c911b87c862c7ca179e4a4a114bradnelson@google.com # is no way to make the switch atomic. 3850954e99802e6c9c911b87c862c7ca179e4a4a114bradnelson@google.com os.remove(filename) 386c376dab1c44c36bf607de14b8204c27bd6e6ce0bbradnelson@google.com os.rename(self.tmp_path, filename) 387c376dab1c44c36bf607de14b8204c27bd6e6ce0bbradnelson@google.com except Exception: 388c376dab1c44c36bf607de14b8204c27bd6e6ce0bbradnelson@google.com # Don't leave turds behind. 389c376dab1c44c36bf607de14b8204c27bd6e6ce0bbradnelson@google.com os.unlink(self.tmp_path) 390c376dab1c44c36bf607de14b8204c27bd6e6ce0bbradnelson@google.com raise 391c376dab1c44c36bf607de14b8204c27bd6e6ce0bbradnelson@google.com 392c376dab1c44c36bf607de14b8204c27bd6e6ce0bbradnelson@google.com return Writer() 393b06ffce92c3e2a37af13f1c35cb098b735c0f1f7sgk@chromium.org 394b06ffce92c3e2a37af13f1c35cb098b735c0f1f7sgk@chromium.org 39578d5366f3040ddfd2fcdcde6e2e4dde87c102850scottmg@chromium.orgdef EnsureDirExists(path): 39678d5366f3040ddfd2fcdcde6e2e4dde87c102850scottmg@chromium.org """Make sure the directory for |path| exists.""" 39778d5366f3040ddfd2fcdcde6e2e4dde87c102850scottmg@chromium.org try: 39878d5366f3040ddfd2fcdcde6e2e4dde87c102850scottmg@chromium.org os.makedirs(os.path.dirname(path)) 39978d5366f3040ddfd2fcdcde6e2e4dde87c102850scottmg@chromium.org except OSError: 40078d5366f3040ddfd2fcdcde6e2e4dde87c102850scottmg@chromium.org pass 40178d5366f3040ddfd2fcdcde6e2e4dde87c102850scottmg@chromium.org 40278d5366f3040ddfd2fcdcde6e2e4dde87c102850scottmg@chromium.org 4031911d51eee5277f2e1ba9e13a6dbad77b544283cthakis@chromium.orgdef GetFlavor(params): 4041911d51eee5277f2e1ba9e13a6dbad77b544283cthakis@chromium.org """Returns |params.flavor| if it's set, the system's default flavor else.""" 4051911d51eee5277f2e1ba9e13a6dbad77b544283cthakis@chromium.org flavors = { 406315c6d71c86e47204686753a91c10693b0e17d9cthakis@chromium.org 'cygwin': 'win', 407315c6d71c86e47204686753a91c10693b0e17d9cthakis@chromium.org 'win32': 'win', 4081911d51eee5277f2e1ba9e13a6dbad77b544283cthakis@chromium.org 'darwin': 'mac', 4091911d51eee5277f2e1ba9e13a6dbad77b544283cthakis@chromium.org } 4104a9683bce5c77825c369f12f537d6503399cc9e0thakis@chromium.org 4114a9683bce5c77825c369f12f537d6503399cc9e0thakis@chromium.org if 'flavor' in params: 4124a9683bce5c77825c369f12f537d6503399cc9e0thakis@chromium.org return params['flavor'] 4134a9683bce5c77825c369f12f537d6503399cc9e0thakis@chromium.org if sys.platform in flavors: 4144a9683bce5c77825c369f12f537d6503399cc9e0thakis@chromium.org return flavors[sys.platform] 4154a9683bce5c77825c369f12f537d6503399cc9e0thakis@chromium.org if sys.platform.startswith('sunos'): 4164a9683bce5c77825c369f12f537d6503399cc9e0thakis@chromium.org return 'solaris' 4174a9683bce5c77825c369f12f537d6503399cc9e0thakis@chromium.org if sys.platform.startswith('freebsd'): 4184a9683bce5c77825c369f12f537d6503399cc9e0thakis@chromium.org return 'freebsd' 419a0d04986c479a6b48f496d2b377ea2b657ab89fdthakis@chromium.org if sys.platform.startswith('openbsd'): 420a0d04986c479a6b48f496d2b377ea2b657ab89fdthakis@chromium.org return 'openbsd' 4212324c0e93231f814f0022e55bf7e25a8433eb8e9thakis@chromium.org if sys.platform.startswith('aix'): 4222324c0e93231f814f0022e55bf7e25a8433eb8e9thakis@chromium.org return 'aix' 4234a9683bce5c77825c369f12f537d6503399cc9e0thakis@chromium.org 4244a9683bce5c77825c369f12f537d6503399cc9e0thakis@chromium.org return 'linux' 4251911d51eee5277f2e1ba9e13a6dbad77b544283cthakis@chromium.org 4261911d51eee5277f2e1ba9e13a6dbad77b544283cthakis@chromium.org 427beab9a2ff9fcbd27d4b1c951d3fc6e6f09230223thakis@chromium.orgdef CopyTool(flavor, out_path): 428b8e58959f8767948aecbc3d8235927401cf3f93dthakis@chromium.org """Finds (flock|mac|win)_tool.gyp in the gyp directory and copies it 429beab9a2ff9fcbd27d4b1c951d3fc6e6f09230223thakis@chromium.org to |out_path|.""" 430b8e58959f8767948aecbc3d8235927401cf3f93dthakis@chromium.org # aix and solaris just need flock emulation. mac and win use more complicated 431b8e58959f8767948aecbc3d8235927401cf3f93dthakis@chromium.org # support scripts. 432b8e58959f8767948aecbc3d8235927401cf3f93dthakis@chromium.org prefix = { 433b8e58959f8767948aecbc3d8235927401cf3f93dthakis@chromium.org 'aix': 'flock', 434b8e58959f8767948aecbc3d8235927401cf3f93dthakis@chromium.org 'solaris': 'flock', 435b8e58959f8767948aecbc3d8235927401cf3f93dthakis@chromium.org 'mac': 'mac', 436b8e58959f8767948aecbc3d8235927401cf3f93dthakis@chromium.org 'win': 'win' 437b8e58959f8767948aecbc3d8235927401cf3f93dthakis@chromium.org }.get(flavor, None) 438beab9a2ff9fcbd27d4b1c951d3fc6e6f09230223thakis@chromium.org if not prefix: 439beab9a2ff9fcbd27d4b1c951d3fc6e6f09230223thakis@chromium.org return 440beab9a2ff9fcbd27d4b1c951d3fc6e6f09230223thakis@chromium.org 441beab9a2ff9fcbd27d4b1c951d3fc6e6f09230223thakis@chromium.org # Slurp input file. 442beab9a2ff9fcbd27d4b1c951d3fc6e6f09230223thakis@chromium.org source_path = os.path.join( 443beab9a2ff9fcbd27d4b1c951d3fc6e6f09230223thakis@chromium.org os.path.dirname(os.path.abspath(__file__)), '%s_tool.py' % prefix) 444beab9a2ff9fcbd27d4b1c951d3fc6e6f09230223thakis@chromium.org with open(source_path) as source_file: 445beab9a2ff9fcbd27d4b1c951d3fc6e6f09230223thakis@chromium.org source = source_file.readlines() 446beab9a2ff9fcbd27d4b1c951d3fc6e6f09230223thakis@chromium.org 447beab9a2ff9fcbd27d4b1c951d3fc6e6f09230223thakis@chromium.org # Add header and write it out. 448beab9a2ff9fcbd27d4b1c951d3fc6e6f09230223thakis@chromium.org tool_path = os.path.join(out_path, 'gyp-%s-tool' % prefix) 449beab9a2ff9fcbd27d4b1c951d3fc6e6f09230223thakis@chromium.org with open(tool_path, 'w') as tool_file: 450beab9a2ff9fcbd27d4b1c951d3fc6e6f09230223thakis@chromium.org tool_file.write( 451beab9a2ff9fcbd27d4b1c951d3fc6e6f09230223thakis@chromium.org ''.join([source[0], '# Generated by gyp. Do not edit.\n'] + source[1:])) 452beab9a2ff9fcbd27d4b1c951d3fc6e6f09230223thakis@chromium.org 453beab9a2ff9fcbd27d4b1c951d3fc6e6f09230223thakis@chromium.org # Make file executable. 454beab9a2ff9fcbd27d4b1c951d3fc6e6f09230223thakis@chromium.org os.chmod(tool_path, 0755) 455beab9a2ff9fcbd27d4b1c951d3fc6e6f09230223thakis@chromium.org 456beab9a2ff9fcbd27d4b1c951d3fc6e6f09230223thakis@chromium.org 457b06ffce92c3e2a37af13f1c35cb098b735c0f1f7sgk@chromium.org# From Alex Martelli, 458b06ffce92c3e2a37af13f1c35cb098b735c0f1f7sgk@chromium.org# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52560 459b06ffce92c3e2a37af13f1c35cb098b735c0f1f7sgk@chromium.org# ASPN: Python Cookbook: Remove duplicates from a sequence 460b06ffce92c3e2a37af13f1c35cb098b735c0f1f7sgk@chromium.org# First comment, dated 2001/10/13. 461b06ffce92c3e2a37af13f1c35cb098b735c0f1f7sgk@chromium.org# (Also in the printed Python Cookbook.) 462b06ffce92c3e2a37af13f1c35cb098b735c0f1f7sgk@chromium.org 463b06ffce92c3e2a37af13f1c35cb098b735c0f1f7sgk@chromium.orgdef uniquer(seq, idfun=None): 464b06ffce92c3e2a37af13f1c35cb098b735c0f1f7sgk@chromium.org if idfun is None: 46569c5522a3dc3d0906ab836350f53dae34a9897d3bradnelson@google.com idfun = lambda x: x 466b06ffce92c3e2a37af13f1c35cb098b735c0f1f7sgk@chromium.org seen = {} 467b06ffce92c3e2a37af13f1c35cb098b735c0f1f7sgk@chromium.org result = [] 468b06ffce92c3e2a37af13f1c35cb098b735c0f1f7sgk@chromium.org for item in seq: 469b06ffce92c3e2a37af13f1c35cb098b735c0f1f7sgk@chromium.org marker = idfun(item) 470b06ffce92c3e2a37af13f1c35cb098b735c0f1f7sgk@chromium.org if marker in seen: continue 471b06ffce92c3e2a37af13f1c35cb098b735c0f1f7sgk@chromium.org seen[marker] = 1 472b06ffce92c3e2a37af13f1c35cb098b735c0f1f7sgk@chromium.org result.append(item) 473b06ffce92c3e2a37af13f1c35cb098b735c0f1f7sgk@chromium.org return result 4745c770284c79f04c043b706f14e464d5ccf74569fbradnelson@google.com 4755c770284c79f04c043b706f14e464d5ccf74569fbradnelson@google.com 4769f14141afe735cf6ae93532ddd97f98fa9ea2821thakis@chromium.org# Based on http://code.activestate.com/recipes/576694/. 4779f14141afe735cf6ae93532ddd97f98fa9ea2821thakis@chromium.orgclass OrderedSet(collections.MutableSet): 4789f14141afe735cf6ae93532ddd97f98fa9ea2821thakis@chromium.org def __init__(self, iterable=None): 4799f14141afe735cf6ae93532ddd97f98fa9ea2821thakis@chromium.org self.end = end = [] 4809f14141afe735cf6ae93532ddd97f98fa9ea2821thakis@chromium.org end += [None, end, end] # sentinel node for doubly linked list 4819f14141afe735cf6ae93532ddd97f98fa9ea2821thakis@chromium.org self.map = {} # key --> [key, prev, next] 4829f14141afe735cf6ae93532ddd97f98fa9ea2821thakis@chromium.org if iterable is not None: 4839f14141afe735cf6ae93532ddd97f98fa9ea2821thakis@chromium.org self |= iterable 4849f14141afe735cf6ae93532ddd97f98fa9ea2821thakis@chromium.org 4859f14141afe735cf6ae93532ddd97f98fa9ea2821thakis@chromium.org def __len__(self): 4869f14141afe735cf6ae93532ddd97f98fa9ea2821thakis@chromium.org return len(self.map) 4879f14141afe735cf6ae93532ddd97f98fa9ea2821thakis@chromium.org 4889f14141afe735cf6ae93532ddd97f98fa9ea2821thakis@chromium.org def __contains__(self, key): 4899f14141afe735cf6ae93532ddd97f98fa9ea2821thakis@chromium.org return key in self.map 4909f14141afe735cf6ae93532ddd97f98fa9ea2821thakis@chromium.org 4919f14141afe735cf6ae93532ddd97f98fa9ea2821thakis@chromium.org def add(self, key): 4929f14141afe735cf6ae93532ddd97f98fa9ea2821thakis@chromium.org if key not in self.map: 4939f14141afe735cf6ae93532ddd97f98fa9ea2821thakis@chromium.org end = self.end 4949f14141afe735cf6ae93532ddd97f98fa9ea2821thakis@chromium.org curr = end[1] 4959f14141afe735cf6ae93532ddd97f98fa9ea2821thakis@chromium.org curr[2] = end[1] = self.map[key] = [key, curr, end] 4969f14141afe735cf6ae93532ddd97f98fa9ea2821thakis@chromium.org 4979f14141afe735cf6ae93532ddd97f98fa9ea2821thakis@chromium.org def discard(self, key): 4989f14141afe735cf6ae93532ddd97f98fa9ea2821thakis@chromium.org if key in self.map: 49963de31e166cc41c3a5d8b9ee4fde53585f5e8d80mark@chromium.org key, prev_item, next_item = self.map.pop(key) 50063de31e166cc41c3a5d8b9ee4fde53585f5e8d80mark@chromium.org prev_item[2] = next_item 50163de31e166cc41c3a5d8b9ee4fde53585f5e8d80mark@chromium.org next_item[1] = prev_item 5029f14141afe735cf6ae93532ddd97f98fa9ea2821thakis@chromium.org 5039f14141afe735cf6ae93532ddd97f98fa9ea2821thakis@chromium.org def __iter__(self): 5049f14141afe735cf6ae93532ddd97f98fa9ea2821thakis@chromium.org end = self.end 5059f14141afe735cf6ae93532ddd97f98fa9ea2821thakis@chromium.org curr = end[2] 5069f14141afe735cf6ae93532ddd97f98fa9ea2821thakis@chromium.org while curr is not end: 5079f14141afe735cf6ae93532ddd97f98fa9ea2821thakis@chromium.org yield curr[0] 5089f14141afe735cf6ae93532ddd97f98fa9ea2821thakis@chromium.org curr = curr[2] 5099f14141afe735cf6ae93532ddd97f98fa9ea2821thakis@chromium.org 5109f14141afe735cf6ae93532ddd97f98fa9ea2821thakis@chromium.org def __reversed__(self): 5119f14141afe735cf6ae93532ddd97f98fa9ea2821thakis@chromium.org end = self.end 5129f14141afe735cf6ae93532ddd97f98fa9ea2821thakis@chromium.org curr = end[1] 5139f14141afe735cf6ae93532ddd97f98fa9ea2821thakis@chromium.org while curr is not end: 5149f14141afe735cf6ae93532ddd97f98fa9ea2821thakis@chromium.org yield curr[0] 5159f14141afe735cf6ae93532ddd97f98fa9ea2821thakis@chromium.org curr = curr[1] 5169f14141afe735cf6ae93532ddd97f98fa9ea2821thakis@chromium.org 51763de31e166cc41c3a5d8b9ee4fde53585f5e8d80mark@chromium.org # The second argument is an addition that causes a pylint warning. 51863de31e166cc41c3a5d8b9ee4fde53585f5e8d80mark@chromium.org def pop(self, last=True): # pylint: disable=W0221 5199f14141afe735cf6ae93532ddd97f98fa9ea2821thakis@chromium.org if not self: 5209f14141afe735cf6ae93532ddd97f98fa9ea2821thakis@chromium.org raise KeyError('set is empty') 5219f14141afe735cf6ae93532ddd97f98fa9ea2821thakis@chromium.org key = self.end[1][0] if last else self.end[2][0] 5229f14141afe735cf6ae93532ddd97f98fa9ea2821thakis@chromium.org self.discard(key) 5239f14141afe735cf6ae93532ddd97f98fa9ea2821thakis@chromium.org return key 5249f14141afe735cf6ae93532ddd97f98fa9ea2821thakis@chromium.org 5259f14141afe735cf6ae93532ddd97f98fa9ea2821thakis@chromium.org def __repr__(self): 5269f14141afe735cf6ae93532ddd97f98fa9ea2821thakis@chromium.org if not self: 5279f14141afe735cf6ae93532ddd97f98fa9ea2821thakis@chromium.org return '%s()' % (self.__class__.__name__,) 5289f14141afe735cf6ae93532ddd97f98fa9ea2821thakis@chromium.org return '%s(%r)' % (self.__class__.__name__, list(self)) 5299f14141afe735cf6ae93532ddd97f98fa9ea2821thakis@chromium.org 5309f14141afe735cf6ae93532ddd97f98fa9ea2821thakis@chromium.org def __eq__(self, other): 5319f14141afe735cf6ae93532ddd97f98fa9ea2821thakis@chromium.org if isinstance(other, OrderedSet): 5329f14141afe735cf6ae93532ddd97f98fa9ea2821thakis@chromium.org return len(self) == len(other) and list(self) == list(other) 5339f14141afe735cf6ae93532ddd97f98fa9ea2821thakis@chromium.org return set(self) == set(other) 5349f14141afe735cf6ae93532ddd97f98fa9ea2821thakis@chromium.org 5359f14141afe735cf6ae93532ddd97f98fa9ea2821thakis@chromium.org # Extensions to the recipe. 5369f14141afe735cf6ae93532ddd97f98fa9ea2821thakis@chromium.org def update(self, iterable): 5379f14141afe735cf6ae93532ddd97f98fa9ea2821thakis@chromium.org for i in iterable: 5389f14141afe735cf6ae93532ddd97f98fa9ea2821thakis@chromium.org if i not in self: 5399f14141afe735cf6ae93532ddd97f98fa9ea2821thakis@chromium.org self.add(i) 5409f14141afe735cf6ae93532ddd97f98fa9ea2821thakis@chromium.org 5419f14141afe735cf6ae93532ddd97f98fa9ea2821thakis@chromium.org 5425c770284c79f04c043b706f14e464d5ccf74569fbradnelson@google.comclass CycleError(Exception): 5435c770284c79f04c043b706f14e464d5ccf74569fbradnelson@google.com """An exception raised when an unexpected cycle is detected.""" 5445c770284c79f04c043b706f14e464d5ccf74569fbradnelson@google.com def __init__(self, nodes): 5455c770284c79f04c043b706f14e464d5ccf74569fbradnelson@google.com self.nodes = nodes 5465c770284c79f04c043b706f14e464d5ccf74569fbradnelson@google.com def __str__(self): 5475c770284c79f04c043b706f14e464d5ccf74569fbradnelson@google.com return 'CycleError: cycle involving: ' + str(self.nodes) 5485c770284c79f04c043b706f14e464d5ccf74569fbradnelson@google.com 5495c770284c79f04c043b706f14e464d5ccf74569fbradnelson@google.com 5505c770284c79f04c043b706f14e464d5ccf74569fbradnelson@google.comdef TopologicallySorted(graph, get_edges): 5515c770284c79f04c043b706f14e464d5ccf74569fbradnelson@google.com """Topologically sort based on a user provided edge definition. 5525c770284c79f04c043b706f14e464d5ccf74569fbradnelson@google.com 5535c770284c79f04c043b706f14e464d5ccf74569fbradnelson@google.com Args: 5545c770284c79f04c043b706f14e464d5ccf74569fbradnelson@google.com graph: A list of node names. 5555c770284c79f04c043b706f14e464d5ccf74569fbradnelson@google.com get_edges: A function mapping from node name to a hashable collection 5565c770284c79f04c043b706f14e464d5ccf74569fbradnelson@google.com of node names which this node has outgoing edges to. 5575c770284c79f04c043b706f14e464d5ccf74569fbradnelson@google.com Returns: 5585c770284c79f04c043b706f14e464d5ccf74569fbradnelson@google.com A list containing all of the node in graph in topological order. 5595c770284c79f04c043b706f14e464d5ccf74569fbradnelson@google.com It is assumed that calling get_edges once for each node and caching is 5605c770284c79f04c043b706f14e464d5ccf74569fbradnelson@google.com cheaper than repeatedly calling get_edges. 5615c770284c79f04c043b706f14e464d5ccf74569fbradnelson@google.com Raises: 5625c770284c79f04c043b706f14e464d5ccf74569fbradnelson@google.com CycleError in the event of a cycle. 5635c770284c79f04c043b706f14e464d5ccf74569fbradnelson@google.com Example: 5645c770284c79f04c043b706f14e464d5ccf74569fbradnelson@google.com graph = {'a': '$(b) $(c)', 'b': 'hi', 'c': '$(b)'} 5655c770284c79f04c043b706f14e464d5ccf74569fbradnelson@google.com def GetEdges(node): 5665c770284c79f04c043b706f14e464d5ccf74569fbradnelson@google.com return re.findall(r'\$\(([^))]\)', graph[node]) 5675c770284c79f04c043b706f14e464d5ccf74569fbradnelson@google.com print TopologicallySorted(graph.keys(), GetEdges) 5685c770284c79f04c043b706f14e464d5ccf74569fbradnelson@google.com ==> 5695c770284c79f04c043b706f14e464d5ccf74569fbradnelson@google.com ['a', 'c', b'] 5705c770284c79f04c043b706f14e464d5ccf74569fbradnelson@google.com """ 5715c770284c79f04c043b706f14e464d5ccf74569fbradnelson@google.com get_edges = memoize(get_edges) 5725c770284c79f04c043b706f14e464d5ccf74569fbradnelson@google.com visited = set() 5735c770284c79f04c043b706f14e464d5ccf74569fbradnelson@google.com visiting = set() 5745c770284c79f04c043b706f14e464d5ccf74569fbradnelson@google.com ordered_nodes = [] 5755c770284c79f04c043b706f14e464d5ccf74569fbradnelson@google.com def Visit(node): 5765c770284c79f04c043b706f14e464d5ccf74569fbradnelson@google.com if node in visiting: 5775c770284c79f04c043b706f14e464d5ccf74569fbradnelson@google.com raise CycleError(visiting) 5785c770284c79f04c043b706f14e464d5ccf74569fbradnelson@google.com if node in visited: 5795c770284c79f04c043b706f14e464d5ccf74569fbradnelson@google.com return 5805c770284c79f04c043b706f14e464d5ccf74569fbradnelson@google.com visited.add(node) 5815c770284c79f04c043b706f14e464d5ccf74569fbradnelson@google.com visiting.add(node) 5825c770284c79f04c043b706f14e464d5ccf74569fbradnelson@google.com for neighbor in get_edges(node): 5835c770284c79f04c043b706f14e464d5ccf74569fbradnelson@google.com Visit(neighbor) 5845c770284c79f04c043b706f14e464d5ccf74569fbradnelson@google.com visiting.remove(node) 5855c770284c79f04c043b706f14e464d5ccf74569fbradnelson@google.com ordered_nodes.insert(0, node) 5865c770284c79f04c043b706f14e464d5ccf74569fbradnelson@google.com for node in sorted(graph): 5875c770284c79f04c043b706f14e464d5ccf74569fbradnelson@google.com Visit(node) 5885c770284c79f04c043b706f14e464d5ccf74569fbradnelson@google.com return ordered_nodes 589