| # 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)) { |
| 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 specifying an SDK category are |
| # included in the 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.sdk_category)) { |
| assert( |
| invoker.sdk_category == "partner", |
| "Create a separate allowlist when adding support for other categories.") |
| deps = [ "//build/sdk:partner_idk_source_sets_allowlist" ] |
| } |
| } |
| } |
| } |