Re-land r1745: "Add an option to prune targets"
BUG=
R=mark@chromium.org
Review URL: https://codereview.chromium.org/25721002
git-svn-id: http://gyp.googlecode.com/svn/trunk@1772 78cadc50-ecff-11dd-a971-7dbc132099af
diff --git a/pylib/gyp/__init__.py b/pylib/gyp/__init__.py
index 8e82fb4..f3de408 100755
--- a/pylib/gyp/__init__.py
+++ b/pylib/gyp/__init__.py
@@ -126,7 +126,7 @@
# Process the input specific to this generator.
result = gyp.input.Load(build_files, default_variables, includes[:],
depth, generator_input_info, check, circular_check,
- params['parallel'])
+ params['parallel'], params['root_targets'])
return [generator] + result
def NameValueListToDict(name_value_list):
@@ -332,6 +332,9 @@
parser.add_option('--toplevel-dir', dest='toplevel_dir', action='store',
default=None, metavar='DIR', type='path',
help='directory to use as the root of the source tree')
+ parser.add_option('-R', '--root-target', dest='root_targets',
+ action='append', metavar='TARGET',
+ help='include only TARGET and its deep dependencies')
options, build_files_arg = parser.parse_args(args)
build_files = build_files_arg
@@ -492,7 +495,8 @@
'build_files_arg': build_files_arg,
'gyp_binary': sys.argv[0],
'home_dot_gyp': home_dot_gyp,
- 'parallel': options.parallel}
+ 'parallel': options.parallel,
+ 'root_targets': options.root_targets}
# Start with the default variables from the command line.
[generator, flat_list, targets, data] = Load(build_files, format,
diff --git a/pylib/gyp/common.py b/pylib/gyp/common.py
index 880bc68..b9d2abe 100644
--- a/pylib/gyp/common.py
+++ b/pylib/gyp/common.py
@@ -44,6 +44,14 @@
e.args = (str(e.args[0]) + ' ' + msg,) + e.args[1:]
+def FindQualifiedTargets(target, qualified_list):
+ """
+ Given a list of qualified targets, return the qualified targets for the
+ specified |target|.
+ """
+ return [t for t in qualified_list if ParseQualifiedTarget(t)[1] == target]
+
+
def ParseQualifiedTarget(target):
# Splits a qualified target into a build file, target name and toolset.
diff --git a/pylib/gyp/input.py b/pylib/gyp/input.py
index d371b27..a08cce9 100644
--- a/pylib/gyp/input.py
+++ b/pylib/gyp/input.py
@@ -2560,6 +2560,41 @@
TurnIntIntoStrInList(item)
+def PruneUnwantedTargets(targets, flat_list, dependency_nodes, root_targets,
+ data):
+ """Return only the targets that are deep dependencies of |root_targets|."""
+ qualified_root_targets = []
+ for target in root_targets:
+ target = target.strip()
+ qualified_targets = gyp.common.FindQualifiedTargets(target, flat_list)
+ if not qualified_targets:
+ raise GypError("Could not find target %s" % target)
+ qualified_root_targets.extend(qualified_targets)
+
+ wanted_targets = {}
+ for target in qualified_root_targets:
+ wanted_targets[target] = targets[target]
+ for dependency in dependency_nodes[target].DeepDependencies():
+ wanted_targets[dependency] = targets[dependency]
+
+ wanted_flat_list = [t for t in flat_list if t in wanted_targets]
+
+ # Prune unwanted targets from each build_file's data dict.
+ for build_file in data['target_build_files']:
+ if not 'targets' in data[build_file]:
+ continue
+ new_targets = []
+ for target in data[build_file]['targets']:
+ qualified_name = gyp.common.QualifiedTarget(build_file,
+ target['target_name'],
+ target['toolset'])
+ if qualified_name in wanted_targets:
+ new_targets.append(target)
+ data[build_file]['targets'] = new_targets
+
+ return wanted_targets, wanted_flat_list
+
+
def VerifyNoCollidingTargets(targets):
"""Verify that no two targets in the same directory share the same name.
@@ -2588,7 +2623,7 @@
def Load(build_files, variables, includes, depth, generator_input_info, check,
- circular_check, parallel):
+ circular_check, parallel, root_targets):
# Set up path_sections and non_configuration_keys with the default data plus
# the generator-specifc data.
global path_sections
@@ -2673,6 +2708,12 @@
[dependency_nodes, flat_list] = BuildDependencyList(targets)
+ if root_targets:
+ # Remove, from |targets| and |flat_list|, the targets that are not deep
+ # dependencies of the targets specified in |root_targets|.
+ targets, flat_list = PruneUnwantedTargets(
+ targets, flat_list, dependency_nodes, root_targets, data)
+
# Check that no two targets in the same directory have the same name.
VerifyNoCollidingTargets(flat_list)
diff --git a/test/prune_targets/gyptest-prune-targets.py b/test/prune_targets/gyptest-prune-targets.py
new file mode 100644
index 0000000..99d7a9a
--- /dev/null
+++ b/test/prune_targets/gyptest-prune-targets.py
@@ -0,0 +1,63 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2013 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.
+
+"""
+Verifies --root-target removes the unnecessary targets.
+"""
+
+import TestGyp
+
+test = TestGyp.TestGyp()
+
+build_error_code = {
+ 'android': 2,
+ 'make': 2,
+ 'msvs': 1,
+ 'ninja': 1,
+ 'xcode': 65,
+}[test.format]
+
+# By default, everything will be included.
+test.run_gyp('test1.gyp')
+test.build('test2.gyp', 'lib1')
+test.build('test2.gyp', 'lib2')
+test.build('test2.gyp', 'lib3')
+test.build('test2.gyp', 'lib_indirect')
+test.build('test1.gyp', 'program1')
+test.build('test1.gyp', 'program2')
+test.build('test1.gyp', 'program3')
+
+# With deep dependencies of program1 only.
+test.run_gyp('test1.gyp', '--root-target=program1')
+test.build('test2.gyp', 'lib1')
+test.build('test2.gyp', 'lib2', status=build_error_code, stderr=None)
+test.build('test2.gyp', 'lib3', status=build_error_code, stderr=None)
+test.build('test2.gyp', 'lib_indirect')
+test.build('test1.gyp', 'program1')
+test.build('test1.gyp', 'program2', status=build_error_code, stderr=None)
+test.build('test1.gyp', 'program3', status=build_error_code, stderr=None)
+
+# With deep dependencies of program2 only.
+test.run_gyp('test1.gyp', '--root-target=program2')
+test.build('test2.gyp', 'lib1', status=build_error_code, stderr=None)
+test.build('test2.gyp', 'lib2')
+test.build('test2.gyp', 'lib3', status=build_error_code, stderr=None)
+test.build('test2.gyp', 'lib_indirect')
+test.build('test1.gyp', 'program1', status=build_error_code, stderr=None)
+test.build('test1.gyp', 'program2')
+test.build('test1.gyp', 'program3', status=build_error_code, stderr=None)
+
+# With deep dependencies of program1 and program2.
+test.run_gyp('test1.gyp', '--root-target=program1', '--root-target=program2')
+test.build('test2.gyp', 'lib1')
+test.build('test2.gyp', 'lib2')
+test.build('test2.gyp', 'lib3', status=build_error_code, stderr=None)
+test.build('test2.gyp', 'lib_indirect')
+test.build('test1.gyp', 'program1')
+test.build('test1.gyp', 'program2')
+test.build('test1.gyp', 'program3', status=build_error_code, stderr=None)
+
+test.pass_test()
diff --git a/test/prune_targets/lib1.cc b/test/prune_targets/lib1.cc
new file mode 100644
index 0000000..692b7de
--- /dev/null
+++ b/test/prune_targets/lib1.cc
@@ -0,0 +1,6 @@
+// Copyright (c) 2013 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.
+
+void libfunc1() {
+}
diff --git a/test/prune_targets/lib2.cc b/test/prune_targets/lib2.cc
new file mode 100644
index 0000000..aed394a
--- /dev/null
+++ b/test/prune_targets/lib2.cc
@@ -0,0 +1,6 @@
+// Copyright (c) 2013 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.
+
+void libfunc2() {
+}
diff --git a/test/prune_targets/lib3.cc b/test/prune_targets/lib3.cc
new file mode 100644
index 0000000..af0f717
--- /dev/null
+++ b/test/prune_targets/lib3.cc
@@ -0,0 +1,6 @@
+// Copyright (c) 2013 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.
+
+void libfunc3() {
+}
diff --git a/test/prune_targets/lib_indirect.cc b/test/prune_targets/lib_indirect.cc
new file mode 100644
index 0000000..92d9ea4
--- /dev/null
+++ b/test/prune_targets/lib_indirect.cc
@@ -0,0 +1,6 @@
+// Copyright (c) 2013 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.
+
+void libfunc_indirect() {
+}
diff --git a/test/prune_targets/program.cc b/test/prune_targets/program.cc
new file mode 100644
index 0000000..c9ac070
--- /dev/null
+++ b/test/prune_targets/program.cc
@@ -0,0 +1,7 @@
+// Copyright (c) 2013 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.
+
+int main() {
+ return 0;
+}
diff --git a/test/prune_targets/test1.gyp b/test/prune_targets/test1.gyp
new file mode 100644
index 0000000..b65ec19
--- /dev/null
+++ b/test/prune_targets/test1.gyp
@@ -0,0 +1,26 @@
+# Copyright (c) 2013 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.
+
+{
+ 'targets': [
+ {
+ 'target_name': 'program1',
+ 'type': 'executable',
+ 'sources': [ 'program.cc' ],
+ 'dependencies': [ 'test2.gyp:lib1' ],
+ },
+ {
+ 'target_name': 'program2',
+ 'type': 'executable',
+ 'sources': [ 'program.cc' ],
+ 'dependencies': [ 'test2.gyp:lib2' ],
+ },
+ {
+ 'target_name': 'program3',
+ 'type': 'executable',
+ 'sources': [ 'program.cc' ],
+ 'dependencies': [ 'test2.gyp:lib3' ],
+ },
+ ],
+}
diff --git a/test/prune_targets/test2.gyp b/test/prune_targets/test2.gyp
new file mode 100644
index 0000000..16f0fd3
--- /dev/null
+++ b/test/prune_targets/test2.gyp
@@ -0,0 +1,30 @@
+# Copyright (c) 2013 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.
+
+{
+ 'targets': [
+ {
+ 'target_name': 'lib1',
+ 'type': 'static_library',
+ 'sources': [ 'lib1.cc' ],
+ 'dependencies': [ 'lib_indirect' ],
+ },
+ {
+ 'target_name': 'lib2',
+ 'type': 'static_library',
+ 'sources': [ 'lib2.cc' ],
+ 'dependencies': [ 'lib_indirect' ],
+ },
+ {
+ 'target_name': 'lib3',
+ 'type': 'static_library',
+ 'sources': [ 'lib3.cc' ],
+ },
+ {
+ 'target_name': 'lib_indirect',
+ 'type': 'static_library',
+ 'sources': [ 'lib_indirect.cc' ],
+ },
+ ],
+}