|  | #!/usr/bin/env python3 | 
|  | # | 
|  | # Copyright 2013 The Flutter Authors. All rights reserved. | 
|  | # Use of this source code is governed by a BSD-style license that can be | 
|  | # found in the LICENSE file. | 
|  |  | 
|  | """Utility for dart_pkg and dart_pkg_app rules""" | 
|  |  | 
|  | import argparse | 
|  | import errno | 
|  | import json | 
|  | import os | 
|  | import shutil | 
|  | import subprocess | 
|  | import sys | 
|  |  | 
|  | USE_LINKS = sys.platform != "win32" | 
|  |  | 
|  | DART_ANALYZE = os.path.join(os.path.dirname(os.path.abspath(__file__)), | 
|  | "dart_analyze.py") | 
|  |  | 
|  | def dart_filter(path): | 
|  | if os.path.isdir(path): | 
|  | return True | 
|  | _, ext = os.path.splitext(path) | 
|  | # .dart includes '.mojom.dart' | 
|  | return ext == '.dart' | 
|  |  | 
|  |  | 
|  | def ensure_dir_exists(path): | 
|  | abspath = os.path.abspath(path) | 
|  | if not os.path.exists(abspath): | 
|  | os.makedirs(abspath) | 
|  |  | 
|  |  | 
|  | def has_pubspec_yaml(paths): | 
|  | for path in paths: | 
|  | _, filename = os.path.split(path) | 
|  | if 'pubspec.yaml' == filename: | 
|  | return True | 
|  | return False | 
|  |  | 
|  |  | 
|  | def link(from_root, to_root): | 
|  | ensure_dir_exists(os.path.dirname(to_root)) | 
|  | try: | 
|  | os.unlink(to_root) | 
|  | except OSError as e: | 
|  | if e.errno == errno.ENOENT: | 
|  | pass | 
|  |  | 
|  | try: | 
|  | os.symlink(from_root, to_root) | 
|  | except OSError as e: | 
|  | if e.errno == errno.EEXIST: | 
|  | pass | 
|  |  | 
|  |  | 
|  | def copy(from_root, to_root, filter_func=None): | 
|  | if not os.path.exists(from_root): | 
|  | return | 
|  | if os.path.isfile(from_root): | 
|  | ensure_dir_exists(os.path.dirname(to_root)) | 
|  | shutil.copy(from_root, to_root) | 
|  | return | 
|  |  | 
|  | ensure_dir_exists(to_root) | 
|  |  | 
|  | for root, dirs, files in os.walk(from_root): | 
|  | # filter_func expects paths not names, so wrap it to make them absolute. | 
|  | wrapped_filter = None | 
|  | if filter_func: | 
|  | wrapped_filter = lambda name: filter_func(os.path.join(root, name)) | 
|  |  | 
|  | for name in filter(wrapped_filter, files): | 
|  | from_path = os.path.join(root, name) | 
|  | root_rel_path = os.path.relpath(from_path, from_root) | 
|  | to_path = os.path.join(to_root, root_rel_path) | 
|  | to_dir = os.path.dirname(to_path) | 
|  | if not os.path.exists(to_dir): | 
|  | os.makedirs(to_dir) | 
|  | shutil.copy(from_path, to_path) | 
|  |  | 
|  | dirs[:] = list(filter(wrapped_filter, dirs)) | 
|  |  | 
|  |  | 
|  | def copy_or_link(from_root, to_root, filter_func=None): | 
|  | if USE_LINKS: | 
|  | link(from_root, to_root) | 
|  | else: | 
|  | copy(from_root, to_root, filter_func) | 
|  |  | 
|  |  | 
|  | def link_if_possible(from_root, to_root): | 
|  | if USE_LINKS: | 
|  | link(from_root, to_root) | 
|  |  | 
|  |  | 
|  | def remove_if_exists(path): | 
|  | try: | 
|  | os.remove(path) | 
|  | except OSError as e: | 
|  | if e.errno != errno.ENOENT: | 
|  | raise | 
|  |  | 
|  |  | 
|  | def list_files(from_root, filter_func=None): | 
|  | file_list = [] | 
|  | for root, dirs, files in os.walk(from_root): | 
|  | # filter_func expects paths not names, so wrap it to make them absolute. | 
|  | wrapped_filter = None | 
|  | if filter_func: | 
|  | wrapped_filter = lambda name: filter_func(os.path.join(root, name)) | 
|  | for name in filter(wrapped_filter, files): | 
|  | path = os.path.join(root, name) | 
|  | file_list.append(path) | 
|  | dirs[:] = list(filter(wrapped_filter, dirs)) | 
|  | return file_list | 
|  |  | 
|  |  | 
|  | def remove_broken_symlink(path): | 
|  | if not USE_LINKS: | 
|  | return | 
|  | try: | 
|  | link_path = os.readlink(path) | 
|  | except OSError as e: | 
|  | # Path was not a symlink. | 
|  | if e.errno == errno.EINVAL: | 
|  | pass | 
|  | else: | 
|  | if not os.path.exists(link_path): | 
|  | remove_if_exists(path) | 
|  |  | 
|  |  | 
|  | def remove_broken_symlinks(root_dir): | 
|  | if not USE_LINKS: | 
|  | return | 
|  | for current_dir, _, child_files in os.walk(root_dir): | 
|  | for filename in child_files: | 
|  | path = os.path.join(current_dir, filename) | 
|  | remove_broken_symlink(path) | 
|  |  | 
|  |  | 
|  | def analyze_entrypoints(dart_sdk, package_root, entrypoints): | 
|  | cmd = [ "python", DART_ANALYZE ] | 
|  | cmd.append("--dart-sdk") | 
|  | cmd.append(dart_sdk) | 
|  | cmd.append("--entrypoints") | 
|  | cmd.extend(entrypoints) | 
|  | cmd.append("--package-root") | 
|  | cmd.append(package_root) | 
|  | cmd.append("--no-hints") | 
|  | try: | 
|  | subprocess.check_output(cmd, stderr=subprocess.STDOUT) | 
|  | except subprocess.CalledProcessError as e: | 
|  | print('Failed analyzing %s' % entrypoints) | 
|  | print(e.output) | 
|  | return e.returncode | 
|  | return 0 | 
|  |  | 
|  |  | 
|  | def main(): | 
|  | parser = argparse.ArgumentParser(description='Generate a dart-pkg') | 
|  | parser.add_argument('--dart-sdk', | 
|  | action='store', | 
|  | metavar='dart_sdk', | 
|  | help='Path to the Dart SDK.') | 
|  | parser.add_argument('--package-name', | 
|  | action='store', | 
|  | metavar='package_name', | 
|  | help='Name of package', | 
|  | required=True) | 
|  | parser.add_argument('--pkg-directory', | 
|  | metavar='pkg_directory', | 
|  | help='Directory where dart_pkg should go', | 
|  | required=True) | 
|  | parser.add_argument('--package-root', | 
|  | metavar='package_root', | 
|  | help='packages/ directory', | 
|  | required=True) | 
|  | parser.add_argument('--stamp-file', | 
|  | metavar='stamp_file', | 
|  | help='timestamp file', | 
|  | required=True) | 
|  | parser.add_argument('--entries-file', | 
|  | metavar='entries_file', | 
|  | help='script entries file', | 
|  | required=True) | 
|  | parser.add_argument('--package-sources', | 
|  | metavar='package_sources', | 
|  | help='Package sources', | 
|  | nargs='+') | 
|  | parser.add_argument('--package-entrypoints', | 
|  | metavar='package_entrypoints', | 
|  | help='Package entry points for analyzer', | 
|  | nargs='*', | 
|  | default=[]) | 
|  | parser.add_argument('--sdk-ext-directories', | 
|  | metavar='sdk_ext_directories', | 
|  | help='Directory containing .dart sources', | 
|  | nargs='*', | 
|  | default=[]) | 
|  | parser.add_argument('--sdk-ext-files', | 
|  | metavar='sdk_ext_files', | 
|  | help='List of .dart files that are part of sdk_ext.', | 
|  | nargs='*', | 
|  | default=[]) | 
|  | parser.add_argument('--sdk-ext-mappings', | 
|  | metavar='sdk_ext_mappings', | 
|  | help='Mappings for SDK extension libraries.', | 
|  | nargs='*', | 
|  | default=[]) | 
|  | parser.add_argument('--read_only', | 
|  | action='store_true', | 
|  | dest='read_only', | 
|  | help='Package is a read only package.', | 
|  | default=False) | 
|  | args = parser.parse_args() | 
|  |  | 
|  | # We must have a pubspec.yaml. | 
|  | assert has_pubspec_yaml(args.package_sources) | 
|  |  | 
|  | target_dir = os.path.join(args.pkg_directory, args.package_name) | 
|  | target_packages_dir = os.path.join(target_dir, 'packages') | 
|  | lib_path = os.path.join(target_dir, "lib") | 
|  | ensure_dir_exists(lib_path) | 
|  |  | 
|  | mappings = {} | 
|  | for mapping in args.sdk_ext_mappings: | 
|  | library, path = mapping.split(',', 1) | 
|  | mappings[library] = '../sdk_ext/%s' % path | 
|  |  | 
|  | sdkext_path = os.path.join(lib_path, '_sdkext') | 
|  | if mappings: | 
|  | with open(sdkext_path, 'w') as stream: | 
|  | json.dump(mappings, stream, sort_keys=True, | 
|  | indent=2, separators=(',', ': ')) | 
|  | else: | 
|  | remove_if_exists(sdkext_path) | 
|  |  | 
|  | # Copy or symlink package sources into pkg directory. | 
|  | common_source_prefix = os.path.dirname(os.path.commonprefix( | 
|  | args.package_sources)) | 
|  | for source in args.package_sources: | 
|  | relative_source = os.path.relpath(source, common_source_prefix) | 
|  | target = os.path.join(target_dir, relative_source) | 
|  | copy_or_link(source, target) | 
|  |  | 
|  | entrypoint_targets = [] | 
|  | for source in args.package_entrypoints: | 
|  | relative_source = os.path.relpath(source, common_source_prefix) | 
|  | target = os.path.join(target_dir, relative_source) | 
|  | copy_or_link(source, target) | 
|  | entrypoint_targets.append(target) | 
|  |  | 
|  | # Copy sdk-ext sources into pkg directory | 
|  | sdk_ext_dir = os.path.join(target_dir, 'sdk_ext') | 
|  | for directory in args.sdk_ext_directories: | 
|  | sdk_ext_sources = list_files(directory, dart_filter) | 
|  | common_prefix = os.path.commonprefix(sdk_ext_sources) | 
|  | for source in sdk_ext_sources: | 
|  | relative_source = os.path.relpath(source, common_prefix) | 
|  | target = os.path.join(sdk_ext_dir, relative_source) | 
|  | copy_or_link(source, target) | 
|  |  | 
|  | common_source_prefix = os.path.dirname(os.path.commonprefix( | 
|  | args.sdk_ext_files)) | 
|  | for source in args.sdk_ext_files: | 
|  | relative_source = os.path.relpath(source, common_source_prefix) | 
|  | target = os.path.join(sdk_ext_dir, relative_source) | 
|  | copy_or_link(source, target) | 
|  |  | 
|  | # Symlink packages/ | 
|  | package_path = os.path.join(args.package_root, args.package_name) | 
|  | copy_or_link(lib_path, package_path) | 
|  |  | 
|  | # Link dart-pkg/$package/packages to dart-pkg/packages | 
|  | link_if_possible(args.package_root, target_packages_dir) | 
|  |  | 
|  | # Remove any broken symlinks in target_dir and package root. | 
|  | remove_broken_symlinks(target_dir) | 
|  | remove_broken_symlinks(args.package_root) | 
|  |  | 
|  | # If any entrypoints are defined, write them to disk so that the analyzer | 
|  | # test can find them. | 
|  | with open(args.entries_file, 'w') as f: | 
|  | for entrypoint in entrypoint_targets: | 
|  | f.write(entrypoint + '\n') | 
|  |  | 
|  | # Write stamp file. | 
|  | with open(args.stamp_file, 'w'): | 
|  | pass | 
|  |  | 
|  | return 0 | 
|  |  | 
|  | if __name__ == '__main__': | 
|  | sys.exit(main()) |