blob: ef5eec91e0522a5a2ffa09da265220e437dcfce5 [file] [log] [blame]
#!/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 filename == 'pubspec.yaml':
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 err:
if err.errno == errno.ENOENT:
pass
try:
os.symlink(from_root, to_root)
except OSError as err:
if err.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, rt=root: filter_func(os.path.join(rt, 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 err:
if err.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, rt=root: filter_func(os.path.join(rt, 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 err:
# Path was not a symlink.
if err.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 err:
print('Failed analyzing %s' % entrypoints)
print(err.output)
return err.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 file:
for entrypoint in entrypoint_targets:
file.write(entrypoint + '\n')
# Write stamp file.
with open(args.stamp_file, 'w'):
pass
return 0
if __name__ == '__main__':
sys.exit(main())