Invoke copy_tree.py only once to collect all input file lists.

fixes #30105

R=rmacnak@google.com

Review-Url: https://codereview.chromium.org/2992353002 .
This commit is contained in:
Zachary Anderson 2017-08-09 11:13:59 -07:00
parent bcaff5e269
commit 63b9312e1b
3 changed files with 228 additions and 105 deletions

View file

@ -10,11 +10,13 @@ _dart_root = rebase_path("..")
# Optional parameters:
# exclude - A comma separated list that is passed to shutil.ignore_patterns()
# in tools/copy_tree.py.
template("copy_tree") {
template("_copy_tree") {
assert(defined(invoker.source), "copy_tree must define 'source'")
assert(defined(invoker.dest), "copy_tree must define 'dest'")
assert(defined(invoker.inputs), "copy_tree must define 'inputs'")
source = invoker.source
dest = invoker.dest
inputs = invoker.inputs
action(target_name) {
if (defined(invoker.visibility)) {
visibility = invoker.visibility
@ -38,12 +40,7 @@ template("copy_tree") {
]
}
dry_run_args = common_args + [ "--dry-run" ]
input_files = exec_script("$_dart_root/tools/copy_tree.py",
dry_run_args,
"list lines")
inputs = input_files
relative_files = rebase_path(input_files, rebase_path(source))
relative_files = rebase_path(inputs, rebase_path(source))
output_files = []
foreach(input, relative_files) {
@ -55,3 +52,54 @@ template("copy_tree") {
args = common_args
}
}
# copy_trees() arranges to invoke copy_tree.py only once to gather the list of
# input source files for every _copy_tree() target. It takes a list of scopes as
# a parameter. The scopes should contain the following mappings.
#
# target: The target name for the _copy_tree() target.
# visibility: The visibility for the _copy_tree() target.
# source: The source directory relative to this directory.
# dest: The destination directory for the _copy_tree() target.
# deps: Any deps needed for the _copy_tree() target.
# ignore_patterns: Patterns to ignore when walking the directory tree.
# This should be '{}' if nothing should be ignored.
#
# copy_trees() will then make sure each invocation of _copy_tree() has the
# correct 'inputs' parameter
template("copy_trees") {
assert(defined(invoker.sources), "$target_name must define 'source'")
sources = invoker.sources
copy_tree_source_paths = []
foreach(copy_tree_spec, sources) {
copy_tree_source_paths += [
rebase_path(copy_tree_spec.source),
copy_tree_spec.ignore_patterns
]
}
# Evaluate script output as GN, producing a scope containing a single value
# "sources"
copy_tree_inputs_scope = exec_script("$_dart_root/tools/copy_tree.py",
["--gn"] + copy_tree_source_paths,
"scope")
# A list of lists of input source files for copy_tree.
copy_tree_inputs = copy_tree_inputs_scope.sources
copy_tree_inputs_index = 0
foreach(copy_tree_spec, sources) {
_copy_tree(copy_tree_spec.target) {
visibility = copy_tree_spec.visibility
source = copy_tree_spec.source
dest = copy_tree_spec.dest
inputs = copy_tree_inputs[copy_tree_inputs_index]
if (defined(copy_tree_spec.deps)) {
deps = copy_tree_spec.deps
}
if (copy_tree_spec.ignore_patterns != "{}") {
exclude = copy_tree_spec.ignore_patterns
}
}
copy_tree_inputs_index = copy_tree_inputs_index + 1
}
}

View file

@ -210,6 +210,103 @@ _analyzer_source_dirs = [
"kernel",
]
# From here down to the copy_trees() invocation, we collect all the information
# about trees that need to be copied in the list of scopes, copy_tree_specs.
copy_tree_specs = []
# This loop generates rules for copying analyzer sources into lib/
foreach(analyzer_source_dir, _analyzer_source_dirs) {
copy_tree_specs += [{
target = "copy_${analyzer_source_dir}_source_dir"
visibility = [ ":copy_analyzer_sources" ]
source = "../pkg/$analyzer_source_dir"
dest = "$root_out_dir/dart-sdk/lib/$analyzer_source_dir"
ignore_patterns = "*.svn,doc,*.py,*.gypi,*.sh,.gitignore,packages,test,testcases"
}]
}
# This rule copies dartdoc templates to
# bin/snapshots/resources/dartdoc/templates
copy_tree_specs += [{
target = "copy_dartdoc_templates"
visibility = [ ":copy_dartdoc_files" ]
source = "../third_party/pkg/dartdoc/lib/templates"
dest = "$root_out_dir/dart-sdk/bin/snapshots/resources/dartdoc/templates"
ignore_patterns = "{}"
}]
# This rule copies dartdoc resources to
# bin/snapshots/resources/dartdoc/resources
copy_tree_specs += [{
target = "copy_dartdoc_resources"
visibility = [ ":copy_dartdoc_files" ]
source = "../third_party/pkg/dartdoc/lib/resources"
dest = "$root_out_dir/dart-sdk/bin/snapshots/resources/dartdoc/resources"
ignore_patterns = "{}"
}]
# This rule copies js needed by ddc to lib/dev_compiler
copy_tree_specs += [{
target = "copy_dev_compiler_js"
visibility = [
":copy_dev_compiler_sdk",
":copy_dev_compiler_require_js",
":copy_dev_compiler_tools",
]
source = "../pkg/dev_compiler/lib/js"
dest = "$root_out_dir/dart-sdk/lib/dev_compiler"
ignore_patterns = "{}"
}]
# This rule copies pub assets to lib/_internal/pub/asset
copy_tree_specs += [{
target = "copy_pub_assets"
visibility = [
":create_common_sdk",
":copy_7zip",
]
deps = [
":copy_libraries",
]
source = "../third_party/pkg/pub/lib/src/asset"
dest = "$root_out_dir/dart-sdk/lib/_internal/pub/asset"
ignore_patterns = "{}"
}]
# This loop generates rules to copy libraries to lib/
foreach(library, _full_sdk_libraries) {
copy_tree_specs += [{
target = "copy_${library}_library"
visibility = [
":copy_platform_sdk_libraries",
":copy_full_sdk_libraries",
]
source = "lib/$library"
dest = "$root_out_dir/dart-sdk/lib/$library"
ignore_patterns = "*.svn,doc,*.py,*.gypi,*.sh,.gitignore"
}]
}
if (is_win) {
copy_tree_specs += [{
target = "copy_7zip"
visibility = [ ":create_common_sdk" ]
deps = [
":copy_libraries",
":copy_pub_assets",
]
source = "../third_party/7zip"
dest = "$root_out_dir/dart-sdk/lib/_internal/pub/asset/7zip"
ignore_patterns = ".svn"
}]
}
# This generates targets for everything in copy_tree_specs. The targets have the
# same name as the "target" fields in the scopes of copy_tree_specs.
copy_trees("copy_trees") {
sources = copy_tree_specs
}
# Copies the Dart VM binary into bin/
copy("copy_dart") {
visibility = [ ":create_common_sdk" ]
@ -372,16 +469,6 @@ group("copy_full_sdk_snapshots") {
}
}
# This loop generates rules for copying analyzer sources into lib/
foreach(analyzer_source_dir, _analyzer_source_dirs) {
copy_tree("copy_${analyzer_source_dir}_source_dir") {
visibility = [ ":copy_analyzer_sources" ]
source = "../pkg/$analyzer_source_dir"
dest = "$root_out_dir/dart-sdk/lib/$analyzer_source_dir"
exclude = "*.svn,doc,*.py,*.gypi,*.sh,.gitignore,packages,test,testcases"
}
}
# This is the main rule for copying analyzer sources to lib/
group("copy_analyzer_sources") {
visibility = [ ":create_common_sdk" ]
@ -391,22 +478,6 @@ group("copy_analyzer_sources") {
}
}
# This rule copies dartdoc templates to
# bin/snapshots/resources/dartdoc/templates
copy_tree("copy_dartdoc_templates") {
visibility = [ ":copy_dartdoc_files" ]
source = "../third_party/pkg/dartdoc/lib/templates"
dest = "$root_out_dir/dart-sdk/bin/snapshots/resources/dartdoc/templates"
}
# This rule copies dartdoc resources to
# bin/snapshots/resources/dartdoc/resources
copy_tree("copy_dartdoc_resources") {
visibility = [ ":copy_dartdoc_files" ]
source = "../third_party/pkg/dartdoc/lib/resources"
dest = "$root_out_dir/dart-sdk/bin/snapshots/resources/dartdoc/resources"
}
# This rule writes the .packages file for dartdoc resources.
write_file("$root_out_dir/dart-sdk/bin/snapshots/resources/dartdoc/.packages",
"dartdoc:.")
@ -481,17 +552,6 @@ copy("copy_dev_compiler_summary") {
]
}
# This rule copies js needed by ddc to lib/dev_compiler
copy_tree("copy_dev_compiler_js") {
visibility = [
":copy_dev_compiler_sdk",
":copy_dev_compiler_require_js",
":copy_dev_compiler_tools",
]
source = "../pkg/dev_compiler/lib/js"
dest = "$root_out_dir/dart-sdk/lib/dev_compiler"
}
# This rule copies require.js to lib/dev_compiler/amd
copy("copy_dev_compiler_require_js") {
visibility = [ ":copy_dev_compiler_sdk" ]
@ -562,32 +622,6 @@ copy("copy_platform_files") {
]
}
# This rule copies pub assets to lib/_internal/pub/asset
copy_tree("copy_pub_assets") {
visibility = [
":create_common_sdk",
":copy_7zip",
]
deps = [
":copy_libraries",
]
source = "../third_party/pkg/pub/lib/src/asset"
dest = "$root_out_dir/dart-sdk/lib/_internal/pub/asset"
}
# This loop generates rules to copy libraries to lib/
foreach(library, _full_sdk_libraries) {
copy_tree("copy_${library}_library") {
visibility = [
":copy_platform_sdk_libraries",
":copy_full_sdk_libraries",
]
source = "lib/$library"
dest = "$root_out_dir/dart-sdk/lib/$library"
exclude = "*.svn,doc,*.py,*.gypi,*.sh,.gitignore"
}
}
# This is the main rule to copy libraries in _platform_sdk_libraries to lib/
group("copy_platform_sdk_libraries") {
visibility = [
@ -624,19 +658,6 @@ group("copy_libraries") {
}
}
if (is_win) {
copy_tree("copy_7zip") {
visibility = [ ":create_common_sdk" ]
deps = [
":copy_libraries",
":copy_pub_assets",
]
source = "../third_party/7zip"
dest = "$root_out_dir/dart-sdk/lib/_internal/pub/asset/7zip"
exclude = ".svn"
}
}
# This rule writes the version file.
action("write_version_file") {
visibility = [ ":create_common_sdk" ]

View file

@ -4,6 +4,7 @@
# BSD-style license that can be found in the LICENSE file.
import argparse
import gn_helpers
import os
import re
import shutil
@ -14,43 +15,57 @@ def ParseArgs(args):
parser = argparse.ArgumentParser(
description='A script to copy a file tree somewhere')
parser.add_argument('--dry-run', '-d',
dest='dryrun',
default=False,
action='store_true',
help='Print the paths of the source files, but do not copy anything.')
parser.add_argument('--exclude_patterns', '-e',
type=str,
help='Patterns to exclude [passed to shutil.copytree]')
parser.add_argument('--from', '-f',
dest="copy_from",
type=str,
required=True,
help='Source directory')
parser.add_argument('--gn', '-g',
dest='gn',
default=False,
action='store_true',
help='Output for GN for multiple sources, but do not copy anything.')
parser.add_argument('gn_paths',
metavar='name path ignore_pattern',
type=str,
nargs='*',
default=None,
help='When --gn is given, the specification of source paths to list.')
parser.add_argument('--to', '-t',
type=str,
required=True,
help='Destination directory')
return parser.parse_args(args)
def ValidateArgs(args):
if not os.path.isdir(args.copy_from):
if args.gn:
if args.exclude_patterns or args.copy_from or args.to:
print "--gn mode does not accept other switches"
return False
if not args.gn_paths:
print "--gn mode requires a list of source specifications"
return False
return True
if not args.copy_from or not os.path.isdir(args.copy_from):
print "--from argument must refer to a directory"
return False
if not args.to:
print "--to is required"
return False
return True
def CopyTree(src, dst, dryrun=False, symlinks=False, ignore=None):
def CopyTree(src, dst, ignore=None):
names = os.listdir(src)
if ignore is not None:
ignored_names = ignore(src, names)
else:
ignored_names = set()
if not dryrun:
os.makedirs(dst)
os.makedirs(dst)
errors = []
for name in names:
if name in ignored_names:
@ -59,12 +74,9 @@ def CopyTree(src, dst, dryrun=False, symlinks=False, ignore=None):
dstname = os.path.join(dst, name)
try:
if os.path.isdir(srcname):
CopyTree(srcname, dstname, dryrun, symlinks, ignore)
CopyTree(srcname, dstname, ignore)
else:
if dryrun:
print srcname
else:
shutil.copy(srcname, dstname)
shutil.copy(srcname, dstname)
except (IOError, os.error) as why:
errors.append((srcname, dstname, str(why)))
# catch the Error from the recursive CopyTree so that we can
@ -72,8 +84,7 @@ def CopyTree(src, dst, dryrun=False, symlinks=False, ignore=None):
except Error as err:
errors.extend(err.args[0])
try:
if not dryrun:
shutil.copystat(src, dst)
shutil.copystat(src, dst)
except WindowsError:
# can't copy file access times on Windows
pass
@ -83,19 +94,62 @@ def CopyTree(src, dst, dryrun=False, symlinks=False, ignore=None):
raise Error(errors)
def ListTree(src, ignore=None):
names = os.listdir(src)
if ignore is not None:
ignored_names = ignore(src, names)
else:
ignored_names = set()
srcnames = []
for name in names:
if name in ignored_names:
continue
srcname = os.path.join(src, name)
if os.path.isdir(srcname):
srcnames.extend(ListTree(srcname, ignore))
else:
srcnames.append(srcname)
return srcnames
# source_dirs is organized such that sources_dirs[n] is the path for the source
# directory, and source_dirs[n+1] is a list of ignore patterns.
def SourcesToGN(source_dirs):
if len(source_dirs) % 2 != 0:
print "--gn list length should be a multiple of 2."
return False
data = []
for i in xrange(0, len(source_dirs), 2):
path = source_dirs[i]
ignores = source_dirs[i + 1]
if ignores in ["{}"]:
sources = ListTree(path)
else:
patterns = ignores.split(',')
sources = ListTree(path, ignore=shutil.ignore_patterns(*patterns))
data.append(sources)
scope_data = {"sources": data}
print gn_helpers.ToGNString(scope_data)
return True
def Main(argv):
args = ParseArgs(argv)
if not ValidateArgs(args):
return -1
if os.path.exists(args.to) and not args.dryrun:
if args.gn:
SourcesToGN(args.gn_paths)
return 0
if os.path.exists(args.to):
shutil.rmtree(args.to)
if args.exclude_patterns == None:
CopyTree(args.copy_from, args.to, dryrun=args.dryrun)
CopyTree(args.copy_from, args.to)
else:
patterns = args.exclude_patterns.split(',')
CopyTree(args.copy_from, args.to, dryrun=args.dryrun,
ignore=shutil.ignore_patterns(*patterns))
CopyTree(args.copy_from, args.to, ignore=shutil.ignore_patterns(*patterns))
return 0