blob: 60bf3a567863ef316040030bcb1094ec9776788f [file] [log] [blame]
# Copyright 2016 The Fuchsia Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import("//build/config/fuchsia/sdk.gni")
import("//build/dart/dart.gni")
import("//build/dart/toolchain.gni")
import("//build/json/validate_json.gni")
import("//build/sdk/sdk_atom.gni")
declare_args() {
# Enable all dart analysis
enable_dart_analysis = true
# Detect dart API changes
# TODO(fxb/36723, fxb/6623) Remove this flag once issues are resolved
enable_api_diff = false
}
# Defines a Dart library
#
# Parameters
#
# sources
# The list of all sources in this library.
# These sources must be within source_dir.
#
# sources_required (optional)
# Whether complete source files are required by the rule. If true, all
# files under the source_dir must be included in sources.
# Defaults to true.
#
# package_root (optional)
# Path to the directory hosting the library.
# This is useful for generated content, and can be ignored otherwise.
# Defaults to ".".
#
# package_name (optional)
# Name of the Dart package. This is used as an identifier in code that
# depends on this library.
#
# infer_package_name (optional)
# Infer the package name based on the path to the package.
#
# NOTE: Exactly one of package_name or infer_package_name must be set.
#
# source_dir (optional)
# Path to the directory containing the package sources, relative to
# package_root.
# Defaults to "lib".
#
# deps (optional)
# List of labels for Dart libraries this library depends on.
#
# non_dart_deps (optional)
# List of labels this library depends on that are not Dart libraries. This
# includes things like actions that generate Dart code. It typically doesn't
# need to be set.
# Note that these labels *must* have an explicit toolchain attached.
#
# disable_analysis (optional)
# Prevents analysis from being run on this target.
#
# sdk_category (optional)
# Publication level of the library in SDKs.
# See //build/sdk/sdk_atom.gni.
#
# api (optional)
# Path to the file representing the API of this library.
# This file is used to ensure modifications to the library's API are
# explicitly acknowledged. This file must exist for publication categories
# of "partner" or "public"; if it does not exist, it will be automatically
# generated by analyzing the given sources list.
# Defaults to "<Dart library name>.api".
#
# extra_sources (optional)
# Additional sources to consider for analysis.
#
# Example of usage:
#
# dart_library("baz") {
# package_name = "foo.bar.baz"
#
# sources = [
# "blah.dart",
# ]
#
# deps = [
# "//foo/bar/owl",
# ]
# }
if (current_toolchain == dart_toolchain) {
template("dart_library") {
forward_variables_from(invoker, [ "testonly" ])
if (defined(invoker.package_name)) {
package_name = invoker.package_name
} else if (defined(invoker.infer_package_name) &&
invoker.infer_package_name) {
# Compute a package name from the label:
# //foo/bar --> foo.bar
# //foo/bar:blah --> foo.bar._blah
# //garnet/public/foo/bar --> foo.bar
# Strip public directories.
full_dir = get_label_info(":$target_name", "dir")
foreach(sdk_dir, sdk_dirs) {
full_dir = string_replace(full_dir, "$sdk_dir/", "")
}
package_name = full_dir
package_name = string_replace(package_name, "//", "")
package_name = string_replace(package_name, "/", ".")
# If the last directory name does not match the target name, add the
# target name to the resulting package name.
name = get_label_info(":$target_name", "name")
last_dir = get_path_info(full_dir, "name")
if (last_dir != name) {
package_name = "$package_name._$name"
}
} else {
assert(false, "Must specify either a package_name or infer_package_name")
}
dart_deps = []
if (defined(invoker.deps)) {
foreach(dep, invoker.deps) {
dart_deps += [ get_label_info(dep, "label_no_toolchain") ]
}
}
package_root = "."
if (defined(invoker.package_root)) {
package_root = invoker.package_root
}
source_dir = "$package_root/lib"
if (defined(invoker.source_dir)) {
source_dir = "$package_root/${invoker.source_dir}"
}
assert(defined(invoker.sources), "Sources must be defined")
source_file = "$target_gen_dir/$target_name.sources"
rebased_sources = []
foreach(source, invoker.sources) {
rebased_source_dir = rebase_path(source_dir)
rebased_sources += [ "$rebased_source_dir/$source" ]
}
if (defined(invoker.extra_sources)) {
foreach(source, invoker.extra_sources) {
rebased_sources += [ rebase_path(source) ]
}
}
write_file(source_file, rebased_sources, "list lines")
# Dependencies of the umbrella group for the targets in this file.
group_deps = []
dot_packages_file = "$target_gen_dir/$target_name.packages"
dot_packages_target_name = "${target_name}_dot_packages"
group_deps += [ ":$dot_packages_target_name" ]
# Creates a .packages file listing dependencies of this library.
action(dot_packages_target_name) {
script = "//build/dart/gen_dot_packages.py"
deps = []
package_files = []
foreach(dep, dart_deps) {
deps += [ "${dep}_dot_packages" ]
dep_gen_dir = get_label_info(dep, "target_gen_dir")
dep_name = get_label_info(dep, "name")
package_files += [ "$dep_gen_dir/$dep_name.packages" ]
}
if (defined(invoker.non_dart_deps)) {
public_deps = invoker.non_dart_deps
}
sources = package_files + [
# Require a manifest file, allowing the analysis service to
# identify the package.
"$package_root/pubspec.yaml",
]
outputs = [
dot_packages_file,
]
args = [
"--out",
rebase_path(dot_packages_file),
"--source-dir",
rebase_path(source_dir),
"--package-name",
package_name,
"--deps",
] + rebase_path(package_files)
}
################################
# Dart source "verification"
# Warn if there are dart sources included in the build directory that are
# not explicitly part of sources. This may cause a failure when syncing to
# another repository, as they will be excluded from the resulting BUILD
# file.
sources_required = true
if (defined(invoker.sources_required)) {
sources_required = invoker.sources_required
}
if (sources_required) {
source_verification_target_name = "${target_name}_source_verification"
output_file = "$target_gen_dir/$target_name.missing"
action(source_verification_target_name) {
script = "//build/dart/verify_sources.py"
outputs = [
output_file,
]
args = [
"--package_root",
rebase_path(package_root),
"--source_dir",
source_dir,
"--stamp",
rebase_path(output_file),
]
foreach(source, invoker.sources) {
args += [ source ]
}
}
group_deps += [ ":$source_verification_target_name" ]
}
################################
# Don't run the analyzer if it is explicitly disabled or if we are using
# a custom-built Dart SDK in a cross-build.
with_analysis =
(!defined(invoker.disable_analysis) || !invoker.disable_analysis) &&
(use_prebuilt_dart_sdk || host_cpu == target_cpu)
with_analysis = enable_dart_analysis && with_analysis
if (with_analysis) {
options_file = "$package_root/analysis_options.yaml"
invocation_file = "$target_gen_dir/$target_name.analyzer.sh"
invocation_target_name = "${target_name}_analysis_runner"
group_deps += [ ":$invocation_target_name" ]
dart_analyzer_binary = "$dart_sdk/bin/dartanalyzer"
# Creates a script which can be used to manually perform analysis.
# TODO(BLD-256): remove this target.
action(invocation_target_name) {
script = "//build/dart/gen_analyzer_invocation.py"
deps = dart_sdk_deps + [ ":$dot_packages_target_name" ]
inputs = [
dart_analyzer_binary,
dot_packages_file,
options_file,
source_file,
]
outputs = [
invocation_file,
]
args = [
"--out",
rebase_path(invocation_file),
"--source-file",
rebase_path(source_file),
"--dot-packages",
rebase_path(dot_packages_file),
"--dartanalyzer",
rebase_path(dart_analyzer_binary),
"--dart-sdk",
rebase_path(dart_sdk),
"--options",
rebase_path(options_file),
"--package-name",
package_name,
]
}
analysis_target_name = "${target_name}_analysis"
group_deps += [ ":$analysis_target_name" ]
# Runs analysis on the sources.
action(analysis_target_name) {
script = "//build/dart/run_analysis.py"
depfile = "$target_gen_dir/$target_name.analysis.d"
output_file = "$target_gen_dir/$target_name.analyzed"
pool = "//build/dart:dart_pool($dart_toolchain)"
inputs = [
dart_analyzer_binary,
dot_packages_file,
options_file,
source_file,
] + rebased_sources
outputs = [
output_file,
]
args = [
"--source-file",
rebase_path(source_file),
"--dot-packages",
rebase_path(dot_packages_file),
"--dartanalyzer",
rebase_path(dart_analyzer_binary),
"--dart-sdk",
rebase_path(dart_sdk),
"--options",
rebase_path(options_file),
"--stamp",
rebase_path(output_file),
"--depname",
rebase_path(output_file, root_build_dir),
"--depfile",
rebase_path(depfile),
]
deps = dart_sdk_deps + [ ":$dot_packages_target_name" ]
}
}
group(target_name) {
# dart_deps are added here to ensure they are fully built.
# Up to this point, only the targets generating .packages had been
# depended on.
deps = dart_deps
public_deps = group_deps
}
################################################
# SDK support
#
with_api_diff = enable_api_diff && (defined(invoker.sdk_category) &&
invoker.sdk_category != "excluded")
if (with_api_diff) {
detect_target_name = "${target_name}_detect_action"
verify_target_name = "${target_name}_verify"
detect_target_file = "$target_gen_dir/$package_name.api"
api_file = rebase_path(".") + "/$package_name.api"
action(detect_target_name) {
runner_label = "//build/dart/sdk/detect_api_changes($dart_toolchain)"
runner_out_dir = get_label_info(runner_label, "root_out_dir")
script = "$runner_out_dir/dart-tools/detect_api_changes"
output_file = detect_target_file
inputs = [
dot_packages_file,
source_file,
] + rebased_sources
outputs = [
output_file,
]
args = [
"--api-name",
get_label_info(":$target_name", "label_no_toolchain"),
"--source-file",
rebase_path(source_file),
"--dot-packages",
rebase_path(dot_packages_file),
"--output-file",
rebase_path("$detect_target_file"),
"--golden-api",
api_file,
# Uncomment to force updating the .api files
#"--overwrite",
]
deps = dart_sdk_deps + [
":$dot_packages_target_name",
runner_label,
]
}
# Verify that the library API file complies with the specified schema.
validate_json(verify_target_name) {
forward_variables_from(invoker, [ "testonly" ])
data = rebase_path("$detect_target_file")
schema = rebase_path("//build/dart/sdk/detect_api_changes/schema.json")
deps = dart_sdk_deps + [ ":$detect_target_name" ]
public_deps = [
":$detect_target_name",
]
}
}
if (defined(invoker.sdk_category) && invoker.sdk_category != "excluded") {
assert(
defined(invoker.package_name),
"Dart libraries published in SDKs must have an explicit package name")
assert(
!defined(invoker.extra_sources),
"Extra sources can not be included in SDKs: put them in source_dir")
# Dependencies that should normally be included in any SDK containing this
# target.
sdk_deps = []
# Path to SDK metadata files for first-party dependencies.
sdk_metas = []
# Path to Dart manifest files for third-party dependencies.
third_party_pubspecs = []
if (defined(invoker.deps)) {
sorted_deps =
exec_script("//build/dart/sdk/sort_deps.py", invoker.deps, "scope")
foreach(dep, sorted_deps.local) {
full_label = get_label_info(dep, "label_no_toolchain")
sdk_dep = "${full_label}_sdk"
sdk_deps += [ sdk_dep ]
gen_dir = get_label_info(sdk_dep, "target_gen_dir")
name = get_label_info(sdk_dep, "name")
sdk_metas += [ rebase_path("$gen_dir/$name.meta.json") ]
}
foreach(dep, sorted_deps.third_party) {
path = get_label_info(dep, "dir")
third_party_pubspecs += [ rebase_path("$path/pubspec.yaml") ]
}
}
file_base = "dart/${invoker.package_name}"
sdk_sources = []
sdk_source_mappings = []
foreach(source, rebased_sources) {
relative_source = rebase_path(source, source_dir)
dest = "$file_base/lib/$relative_source"
sdk_sources += [ dest ]
sdk_source_mappings += [
{
source = source
dest = dest
},
]
}
metadata_target_name = "${target_name}_sdk_metadata"
metadata_file = "$target_gen_dir/$target_name.sdk_meta.json"
action(metadata_target_name) {
script = "//build/dart/sdk/gen_meta_file.py"
inputs = sdk_metas + third_party_pubspecs
outputs = [
metadata_file,
]
args = [
"--out",
rebase_path(metadata_file),
"--name",
package_name,
"--root",
file_base,
]
args += [ "--specs" ] + sdk_metas
args += [ "--sources" ] + sdk_sources
args += [ "--third-party-specs" ] + third_party_pubspecs
deps = sdk_deps
}
sdk_atom("${target_name}_sdk") {
id = "sdk://dart/${invoker.package_name}"
category = invoker.sdk_category
meta = {
source = metadata_file
dest = "$file_base/meta.json"
schema = "dart_library"
}
deps = sdk_deps
non_sdk_deps = [ ":$metadata_target_name" ]
if (with_api_diff) {
non_sdk_deps += [ ":$verify_target_name" ]
}
if (defined(invoker.non_dart_deps)) {
non_sdk_deps += invoker.non_dart_deps
}
files = sdk_source_mappings
}
}
}
} else { # Not the Dart toolchain.
template("dart_library") {
group(target_name) {
not_needed(invoker, "*")
public_deps = [
":$target_name($dart_toolchain)",
]
}
}
}