Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(756)

Unified Diff: pylib/gyp/generator/analyzer.py

Issue 330053003: First crack at adding a GYP_GENERATOR for determining various things (Closed) Base URL: http://gyp.googlecode.com/svn/trunk
Patch Set: sim 90 Created 6 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | test/analyzer/gyptest-analyzer.py » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: pylib/gyp/generator/analyzer.py
diff --git a/pylib/gyp/generator/analyzer.py b/pylib/gyp/generator/analyzer.py
new file mode 100644
index 0000000000000000000000000000000000000000..007d17d17e183f470664b1d3f1fcf1fce13b9ced
--- /dev/null
+++ b/pylib/gyp/generator/analyzer.py
@@ -0,0 +1,192 @@
+# Copyright (c) 2014 Google Inc. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""
+This script is intended for use as a GYP_GENERATOR. It takes as input (by way of
+the generator flag file_path) the list of relative file paths to consider. If
+any target has at least one of the paths as a source (or input to an action or
+rule) then 'Found dependency' is output, otherwise 'No dependencies' is output.
+"""
+
+import gyp.common
+import gyp.ninja_syntax as ninja_syntax
+import os
+import posixpath
+
+generator_supports_multiple_toolsets = True
+
+generator_wants_static_library_dependencies_adjusted = False
+
+generator_default_variables = {
+}
+for dirname in ['INTERMEDIATE_DIR', 'SHARED_INTERMEDIATE_DIR', 'PRODUCT_DIR',
+ 'LIB_DIR', 'SHARED_LIB_DIR']:
+ generator_default_variables[dirname] = '!!!'
+
+for unused in ['RULE_INPUT_PATH', 'RULE_INPUT_ROOT', 'RULE_INPUT_NAME',
+ 'RULE_INPUT_DIRNAME', 'RULE_INPUT_EXT',
+ 'EXECUTABLE_PREFIX', 'EXECUTABLE_SUFFIX',
+ 'STATIC_LIB_PREFIX', 'STATIC_LIB_SUFFIX',
+ 'SHARED_LIB_PREFIX', 'SHARED_LIB_SUFFIX',
+ 'CONFIGURATION_NAME']:
+ generator_default_variables[unused] = ''
+
+def __MakeRelativeTargetName(path):
+ """Converts a gyp target name into a relative name. For example, the path to a
+ gyp file may be something like c:\foo\bar.gyp:target, this converts it to
+ bar.gyp.
+ """
+ prune_path = os.getcwd()
+ if path.startswith(prune_path):
+ path = path[len(prune_path):]
+ # Gyp paths are always posix style.
+ path = path.replace('\\', '/')
+ if path.endswith('#target'):
+ path = path[0:len(path) - len('#target')]
+ return path
+
+def __ExtractBasePath(target):
+ """Extracts the path components of the specified gyp target path."""
+ last_index = target.rfind('/')
+ if last_index == -1:
+ return ''
+ return target[0:(last_index + 1)]
+
+def __AddSources(sources, base_path, base_path_components, result):
+ """Extracts valid sources from |sources| and adds them to |result|. Each
+ source file is relative to |base_path|, but may contain '..'. To make
+ resolving '..' easier |base_path_components| contains each of the
+ directories in |base_path|. Additionally each source may contain variables.
+ Such sources are ignored as it is assumed dependencies on them are expressed
+ and tracked in some other means."""
+ # NOTE: gyp paths are always posix style.
+ for source in sources:
+ if not len(source) or source.startswith('!!!') or source.startswith('$'):
+ continue
+ # variable expansion may lead to //.
+ source = source[0] + source[1:].replace('//', '/')
+ if source.startswith('../'):
+ path_components = base_path_components[:]
+ # Resolve relative paths.
+ while source.startswith('../'):
+ path_components.pop(len(path_components) - 1)
+ source = source[3:]
+ result.append('/'.join(path_components) + source)
+ continue
+ result.append(base_path + source)
+
+def __ExtractSourcesFromAction(action, base_path, base_path_components,
+ results):
+ if 'inputs' in action:
+ __AddSources(action['inputs'], base_path, base_path_components, results)
+
+def __ExtractSources(target, target_dict):
+ base_path = posixpath.dirname(target)
+ base_path_components = base_path.split('/')
+ # Add a trailing '/' so that __AddSources() can easily build paths.
+ if len(base_path):
+ base_path += '/'
+ results = []
+ if 'sources' in target_dict:
+ __AddSources(target_dict['sources'], base_path, base_path_components,
+ results)
+ # Include the inputs from any actions. Any changes to these effect the
+ # resulting output.
+ if 'actions' in target_dict:
+ for action in target_dict['actions']:
+ __ExtractSourcesFromAction(action, base_path, base_path_components,
+ results)
+ if 'rules' in target_dict:
+ for rule in target_dict['rules']:
+ __ExtractSourcesFromAction(rule, base_path, base_path_components, results)
+
+ return results
+
+class Target(object):
+ """Holds information about a particular target:
+ sources: set of source files defined by this target. This includes inputs to
+ actions and rules.
+ deps: list of direct dependencies."""
+ def __init__(self):
+ self.sources = []
+ self.deps = []
+
+def __GenerateTargets(target_list, target_dicts):
+ """Generates a dictionary with the key the name of a target and the value a
+ Target."""
+ targets = {}
+
+ # Queue of targets to visit.
+ targets_to_visit = target_list[:]
+
+ while len(targets_to_visit) > 0:
+ absolute_target_name = targets_to_visit.pop()
+ # |absolute_target| may be an absolute path and may include #target.
+ # References to targets are relative, so we need to clean the name.
+ relative_target_name = __MakeRelativeTargetName(absolute_target_name)
+ if relative_target_name in targets:
+ continue
+
+ target = Target()
+ targets[relative_target_name] = target
+ target.sources.extend(__ExtractSources(relative_target_name,
+ target_dicts[absolute_target_name]))
+
+ for dep in target_dicts[absolute_target_name].get('dependencies', []):
+ targets[relative_target_name].deps.append(__MakeRelativeTargetName(dep))
+ targets_to_visit.append(dep)
+
+ return targets
+
+def __GetFiles(params):
+ """Returns the list of files to analyze, or None if none specified."""
+ generator_flags = params.get('generator_flags', {})
+ file_path = generator_flags.get('file_path', None)
+ if not file_path:
+ return None
+ try:
+ f = open(file_path, 'r')
+ result = []
+ for file_name in f:
+ if file_name.endswith('\n'):
+ file_name = file_name[0:len(file_name) - 1]
+ if len(file_name):
+ result.append(file_name)
+ f.close()
+ return result
+ except IOError:
+ print 'Unable to open file', file_path
+ return None
+
+def CalculateVariables(default_variables, params):
+ """Calculate additional variables for use in the build (called by gyp)."""
+ flavor = gyp.common.GetFlavor(params)
+ if flavor == 'mac':
+ default_variables.setdefault('OS', 'mac')
+ elif flavor == 'win':
+ default_variables.setdefault('OS', 'win')
+ else:
+ operating_system = flavor
+ if flavor == 'android':
+ operating_system = 'linux' # Keep this legacy behavior for now.
+ default_variables.setdefault('OS', operating_system)
+
+def GenerateOutput(target_list, target_dicts, data, params):
+ """Called by gyp as the final stage. Outputs results."""
+ files = __GetFiles(params)
+ if not files:
+ print 'Must specify files to analyze via file_path generator flag'
+ return
+
+ targets = __GenerateTargets(target_list, target_dicts)
+
+ files_set = frozenset(files)
+ found_in_all_sources = 0
+ for target_name, target in targets.iteritems():
+ sources = files_set.intersection(target.sources)
+ if len(sources):
+ print 'Found dependency'
+ return
+
+ print 'No dependencies'
« no previous file with comments | « no previous file | test/analyzer/gyptest-analyzer.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698