blob: a916003f43180583d55ca70e44fd8d3d015d6d8c [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/dart/dart.gni")
import("//build/dart/dart_package_config.gni")
import("//build/dart/toolchain.gni")
import("//build/json/validate_json.gni")
import("//build/sdk/sdk_atom.gni")
import("//build/toolchain/concurrent_jobs.gni")
# Defines a Dart library
#
# Parameters
#
# sources
# The list of all sources in this library.
# These sources must be within source_dir.
#
# 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. Must be a valid Dart package name, so for
# example "some_name" is OK, but "some-name" is not.
#
# language_version (optional)
# Specify the Dart language version to use for this package.
# If language_version is not specified but pubspec is then the language
# version will be read from the pubspec. If no language version can be
# determined then we will default to version "2.8".
# It is recommended to specify a language_version if it is well known
# instead of relying on the pubspec file since this will improve compilation
# times.
#
# 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. All non third-party dart files under source_dir must be
# included in sources.
# Defaults to "lib".
#
# deps (optional)
# List of labels this library depends on.
#
# TODO(https://fxbug.dev/42141604): non_dart_deps is deprecated. Use deps instead.
# non_dart_deps (optional, deprecated)
# 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.
#
# TODO(https://fxbug.dev/42151305): set up allowlist for disable_source_verification when
# dart_test no longer depends on dart_library.
# NOTE: Do NOT disable source verification unless you are 100% sure it is
# absolutely necessary.
# disable_source_verification (optional)
# Prevents source verification from being run on this target.
#
# sdk_category (optional)
# Publication level of the library in SDKs.
# See //build/sdk/sdk_atom.gni.
#
# sdk_area (optional)
# [string] The API area responsible for maintaining this library.
# See //build/sdk/sdk_atom.gni.
#
# extra_sources (optional)
# Additional sources to consider for analysis.
#
# disable_metadata_entry (optional)
# Prevents metedata entry from being written to the dart_packag_config json file.
#
# null_safe (optional)
# A flag that enables null safety check in dart libraries.
#
# 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,
[
"visibility",
"hermetic_deps",
])
if (defined(invoker.disable_analysis)) {
not_needed(invoker, [ "disable_analysis" ])
}
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
# Strip public directories.
full_dir = get_label_info(":$target_name", "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") ]
}
}
_non_dart_deps = []
if (defined(invoker.non_dart_deps)) {
_non_dart_deps += invoker.non_dart_deps
}
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")
disable_source_verification =
defined(invoker.disable_source_verification) &&
invoker.disable_source_verification
if (disable_source_verification && invoker.sources == []) {
not_needed([ source_dir ])
}
rebased_sources = []
foreach(source, invoker.sources) {
rebased_source_dir = rebase_path(source_dir, root_build_dir)
rebased_sources += [ "$rebased_source_dir/$source" ]
}
if (defined(invoker.extra_sources)) {
foreach(source, invoker.extra_sources) {
rebased_sources += [ rebase_path(source, root_build_dir) ]
}
}
source_file = "$target_gen_dir/$target_name.sources"
write_file(source_file, rebased_sources, "list lines")
# Dependencies of the umbrella group for the targets in this file.
group_deps = []
_public_deps = []
if (defined(invoker.public_deps)) {
_public_deps = invoker.public_deps
}
_metadata = {
package_config_entries = [
{
name = package_name
if (defined(invoker.language_version)) {
language_version = invoker.language_version
} else if (defined(invoker.null_safe) && invoker.null_safe) {
language_version = "2.12"
}
root_uri = rebase_path(package_root, root_build_dir)
if (defined(invoker.source_dir)) {
package_uri = invoker.source_dir
} else {
package_uri = "lib"
}
},
]
dart_build_info = [
{
__is_current_target = false
__package_name = package_name
__deps = _dart_deps + _non_dart_deps
__public_deps = _public_deps
__rebased_sources = rebased_sources
},
]
dart_build_info_barrier = []
}
# When we generate a package_config for the analyzer we need to make sure
# that we are including this library in that file. The dart_package_config
# collects metadata from its dependencies so we create this group to expose
# that data. We also expose this in the group target below so that users of
# the dart_package_config target can just add the targets to the deps list.
_publish_metadata_target_name = "${target_name}_package_metadata"
group(_publish_metadata_target_name) {
metadata = _metadata
}
_dart_package_config_target_name = "${target_name}_dart_package"
_packages_path = "$target_gen_dir/${target_name}_package_config.json"
dart_package_config(_dart_package_config_target_name) {
# Do not publish the metadata to the dart_package_config json file if the
# disable_metadata_entry flag is enabled in the dart_tool. The reason this is here
# is to avoid entries that may have identical rootUris as https://fxbug.dev/42136776 has highlighted.
deps = _dart_deps
if (!defined(invoker.disable_metadata_entry) ||
!invoker.disable_metadata_entry) {
deps += [ ":$_publish_metadata_target_name" ]
}
public_deps = _non_dart_deps
outputs = [ _packages_path ]
forward_variables_from(invoker, [ "testonly" ])
}
group_deps += [ ":$_dart_package_config_target_name" ]
################################
# Dart source "verification"
#
# Warn if there are dart sources from the source 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.
#
# Also warn if nonexistent files are included in sources.
if (!disable_source_verification) {
source_verification_target_name = "${target_name}_source_verification"
action(source_verification_target_name) {
script = "//build/dart/verify_sources.py"
output_file = "$target_gen_dir/$target_name.missing"
sources = rebase_path(rebased_sources, ".", root_build_dir)
outputs = [ output_file ]
args = [
"--source_dir",
rebase_path(source_dir, root_build_dir),
"--stamp",
rebase_path(output_file, root_build_dir),
] + invoker.sources
forward_variables_from(invoker, [ "testonly" ])
# Deps may include codegen dependencies that generate dart sources.
deps = _dart_deps + _non_dart_deps
}
group_deps += [ ":$source_verification_target_name" ]
}
# Generate a file that lists files containing full (including all direct and
# transitive dependencies) sources for this target's dependencies.
_all_deps_sources_list_target = "${target_name}.all_deps_sources.list"
_all_deps_sources_list_file =
"${target_gen_dir}/${target_name}.all_deps_sources.list"
generated_file(_all_deps_sources_list_target) {
forward_variables_from(invoker, [ "testonly" ])
outputs = [ _all_deps_sources_list_file ]
data_keys = [ "all_deps_sources" ]
walk_keys = [ "all_deps_sources_barrier" ]
deps = _dart_deps
}
# Generate full sources for this target by combining sources of this target
# with full sources of all dependencies.
#
# The generated file contains sources of this target and all of its direct
# and transitive dependencies.
#
# The output file is useful when writing depfiles for actions like dart
# analyzer, which recursively reads all sources.
_all_deps_sources_target = "${target_name}.all_deps_sources"
_all_deps_sources_file = "${target_gen_dir}/${target_name}.all_deps_sources"
action(_all_deps_sources_target) {
forward_variables_from(invoker, [ "testonly" ])
script = "//build/dart/merge_deps_sources.py"
outputs = [ _all_deps_sources_file ]
depfile = "${_all_deps_sources_file}.d"
args = [
"--output",
rebase_path(outputs[0], root_build_dir),
"--depfile",
rebase_path(depfile, root_build_dir),
"--source_lists",
"@" + rebase_path(_all_deps_sources_list_file, root_build_dir),
"--sources",
] + rebased_sources
inputs = [ _all_deps_sources_list_file ]
deps = [ ":${_all_deps_sources_list_target}" ]
metadata = {
all_deps_sources = [ rebase_path(outputs[0], root_build_dir) ]
all_deps_sources_barrier = []
}
}
group_deps += [ ":${_all_deps_sources_target}" ]
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 + _non_dart_deps + [ "//build/dart:dart_allowlist" ]
public_deps = group_deps
metadata = _metadata
forward_variables_from(invoker, [ "testonly" ])
}
################################################
# SDK support
#
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 = []
local_deps = filter_exclude(_dart_deps, [ "//third_party/*" ])
third_party_deps = filter_include(_dart_deps, [ "//third_party/*" ])
foreach(dep, local_deps) {
sdk_dep = "${dep}_sdk"
sdk_deps += [ sdk_dep ]
gen_dir = get_label_info(sdk_dep, "target_gen_dir")
name = get_label_info(sdk_dep, "name")
sdk_metas += [ "$gen_dir/$name.meta.json" ]
}
foreach(dep, third_party_deps) {
path = get_label_info(dep, "dir")
third_party_pubspecs += [ "$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, root_build_dir)
dest = "$file_base/lib/$relative_source"
sdk_sources += [ dest ]
sdk_source_mappings += [
{
source = "${source_dir}/${relative_source}"
dest = dest
},
]
}
metadata_target_name = "${target_name}_sdk_metadata"
metadata_file = "$target_gen_dir/$target_name.sdk_meta.json"
action(metadata_target_name) {
# This script is built by "//build/dart:gen_meta_file". However, because
# that target is not defined in this file we can't use
# `get_target_outputs`, which only supports targets previously defined
# in the current file. We don't want to include that target as part of
# the template, to avoid repeatedly building it for every dart_library.
script = "${root_out_dir}/obj/build/dart/gen_meta_file.pyz"
inputs = sdk_metas + third_party_pubspecs
outputs = [ metadata_file ]
args = [
"--out",
rebase_path(metadata_file, root_build_dir),
"--name",
package_name,
"--root",
file_base,
]
args += [ "--specs" ] + rebase_path(sdk_metas, root_build_dir)
args += [ "--sources" ] + sdk_sources
args += [ "--third-party-specs" ] +
rebase_path(third_party_pubspecs, root_build_dir)
if (defined(invoker.null_safe) && invoker.null_safe) {
args += [ "--dart-library-null-safe" ]
}
deps = sdk_deps + [ "//build/dart:gen_meta_file" ]
forward_variables_from(invoker, [ "testonly" ])
}
sdk_atom("${target_name}_sdk") {
forward_variables_from(invoker, [ "sdk_area" ])
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 (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) {
forward_variables_from(invoker, [ "testonly" ])
not_needed(invoker, "*")
public_deps = [ ":$target_name($dart_toolchain)" ]
# Ignore the Dart libraries that are only present in private versions of
# the SDK as they don't have the same versioning constraints.
is_vendor_google_library =
string_replace(target_out_dir, "/vendor/google", "") != target_out_dir
# Ensure that 'dart_library()' targets in the 'partner' or 'public' category that are not
# testonly are included in the SDK source_sets allowlist. The allowlist target's `visibility`
# list ensures that the target using this template is in the allowlist.
if (!is_vendor_google_library &&
(!defined(invoker.testonly) || !invoker.testonly) &&
(defined(invoker.sdk_category) &&
(invoker.sdk_category == "partner" ||
invoker.sdk_category == "public"))) {
deps = [ "//build/sdk:sdk_source_sets_allowlist" ]
}
}
}
}