| # Copyright 2018 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/cpp/verify_pragma_once.gni") |
| import("//build/cpp/verify_public_headers.gni") |
| import("//build/cpp/verify_public_symbols.gni") |
| import("//build/cpp/verify_runtime_deps.gni") |
| import("//build/sdk/plasa/config.gni") |
| import("//build/sdk/plasa/plasa_fragment_cc.gni") |
| import("//build/sdk/sdk_atom.gni") |
| import("//build/sdk/sdk_atom_alias.gni") |
| |
| # A shared library that can be exported to an SDK in binary form. |
| # |
| # Parameters |
| # |
| # category (required) |
| # 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. It is mandatory for publication categories of |
| # "partner" or "public". |
| # Defaults to "<SDK name>.api". |
| # |
| # symbols_api (optional) |
| # Path to the ifs file containing the public symbols of this library. |
| # This file is used to ensure modifications to the library's ABI are |
| # explicitly acknowledged. It is mandatory for publication categories of |
| # "partner" or "public". |
| # |
| # no_headers (optional) |
| # Whether to include the library's headers in the SDK. |
| # Defaults to false. |
| # |
| # libcxx_linkage (optional) |
| # Whether or how to link libc++. SDK shared libraries cannot link libc++.so |
| # dynamically because libc++.so does not have a stable ABI. Can be either |
| # "none" or "static". |
| # Defaults to "none". |
| # |
| # sdk_name (optional) |
| # Name of the library in the SDK. |
| # Defaults to the library's output name. |
| # |
| # include_base (optional) |
| # Path to the root directory for includes. |
| # Defaults to "include". |
| # |
| # runtime_deps (optional) |
| # List of labels representing the library's runtime dependencies. This is |
| # Note that these labels should represent SDK targets. See note below. |
| # |
| # sdk_headers_for_internal_use (optional) |
| # Out of the headers specified in `public` or `sources`, some headers are |
| # part of the SDK but not meant for direct inclusion by users, i.e. they are |
| # only transitively included by other public headers. They usually contain |
| # implementation details. Re-specify those headers here. |
| # |
| # When enumerating the platform surface area (PlaSA), these headers will |
| # be excluded. See /build/sdk/plasa/plasa_fragment_cc.gni. |
| # |
| # See fxbug.dev/117085 for more details about this field. |
| # |
| # TECHNICAL NOTE ON 'runtime_deps': |
| # |
| # The `runtime_deps` parameter is used to list the sdk_shared_library() |
| # targets that this one depends on at runtime. Unfortunately, this cannot be |
| # computed directly by GN. To better understand why, consider the following |
| # example: |
| # |
| # sdk_shared_library("foo") { |
| # ... |
| # deps = [ ":bar" ] |
| # } |
| # |
| # sdk_shared_library("bar") { |
| # ... |
| # } |
| # |
| # These definitions end up creating at least four GN targets: |
| # |
| # - a 'foo' and 'bar' target, which are real shared_library() targets |
| # used to build libfoo.so and libbar.so respectively. |
| # |
| # and due to the 'deps' value, 'foo' will depend on 'bar'. |
| # |
| # - a 'foo_sdk' and a 'bar_sdk' targets that generate an sdk_atom() wrapping |
| # each library, i.e. the target responsible for creating a meta.json file |
| # for each library, and used to generate exported SDKs. |
| # |
| # 'foo_sdk' depends on 'foo', and 'bar_sdk' depends on 'bar', as in: |
| # |
| # |
| # foo <--- foo_sdk |
| # | |
| # v |
| # bar <--- bar_sdk |
| # |
| # |
| # However, without "runtime_deps", 'foo_sdk' will _not_ depend on 'bar_sdk', |
| # which means that if an sdk() target depends on 'foo_sdk', the atom for |
| # the 'bar_sdk' target will be ignored. The result is a "broken" exported |
| # SDK that will not include a prebuilt for libbar.so, even though it is |
| # needed at runtime by libfoo.so. |
| # |
| # To fix this, set 'runtime_deps' to point to the SDK atom target for bar, |
| # as in: |
| # |
| # sdk_shared_library("foo") { |
| # ... |
| # deps = [ ":bar" ] |
| # runtime_deps = [ ":bar_sdk" ] |
| # } |
| # |
| # sdk_shared_library("bar") { |
| # ... |
| # } |
| # |
| # Which results in the following (correct) dependency graph: |
| # |
| # foo <--- foo_sdk |
| # | | |
| # | |--- this dependency added through runtime_deps! |
| # v v |
| # bar <--- bar_sdk |
| # |
| |
| # The defaults for a sdk_shared_library should match that of a shared_library. |
| set_defaults("sdk_shared_library") { |
| configs = default_shared_library_configs |
| } |
| |
| template("sdk_shared_library") { |
| assert(defined(invoker.category), "Must define an SDK category") |
| is_internal = invoker.category == "internal" |
| |
| output_name = target_name |
| if (defined(invoker.output_name)) { |
| output_name = invoker.output_name |
| } |
| |
| if (defined(invoker.sdk_name)) { |
| atom_name = invoker.sdk_name |
| } else { |
| atom_name = output_name |
| } |
| |
| if (!is_internal) { |
| sdk_root_path = "pkg/${atom_name}" |
| } else { |
| # For internal SDK libraries, the sdk_name is the fully qualified target name with some tweaks. For example: |
| # //src/lib/fxl:fxl sdk_name = //src/lib/fxl_fxl sdk_root_path= src/lib/fxl_fxl |
| # //src/lib/fxl:functional sdk_name = //src/lib/fxl_functional sdk_root_path= src/lib/fxl_functional |
| # //src/lib/fxl/files:files sdk_name = //src/lib/fxl/files_files sdk_root_path= src/lib/fxl/files_files |
| full_label = get_label_info(":${invoker.target_name}", "label_no_toolchain") |
| path = get_label_info(full_label, "dir") |
| path = "${path}_${invoker.target_name}" |
| |
| atom_name = path |
| sdk_root_path = string_replace(path, "//", "") |
| } |
| sdk_id = "sdk://${sdk_root_path}" |
| |
| no_headers = defined(invoker.no_headers) && invoker.no_headers |
| |
| if ((invoker.category == "partner" || invoker.category == "public") && |
| !no_headers) { |
| api_reference = "${atom_name}.api" |
| if (defined(invoker.api)) { |
| api_reference = invoker.api |
| } |
| } |
| |
| main_target_name = target_name |
| metadata_target_name = "${target_name}_sdk_metadata" |
| manifest_target_name = "${target_name}_sdk_manifest" |
| sdk_target_name = "${target_name}_sdk" |
| |
| shared_library(main_target_name) { |
| forward_variables_from(invoker, |
| "*", |
| [ |
| "api", |
| "category", |
| "include_base", |
| "no_headers", |
| "sdk_headers_for_internal_use", |
| "runtime_deps", |
| "sdk_name", |
| "symbols_api", |
| ]) |
| |
| if (defined(visibility)) { |
| visibility += [ ":${manifest_target_name}" ] |
| } |
| |
| if (!defined(libcxx_linkage)) { |
| libcxx_linkage = "none" |
| } |
| assert(libcxx_linkage == "none" || libcxx_linkage == "static") |
| |
| # Prebuilt shared libraries are eligible for inclusion in the SDK. We do not |
| # want to dynamically link against libc++.so because we let clients bring |
| # their own toolchain, which might have a different C++ Standard Library or |
| # a different C++ ABI entirely. |
| if (!defined(configs)) { |
| configs = [] |
| } |
| if (libcxx_linkage == "static") { |
| configs += [ "//build/config/fuchsia:static_cpp_standard_library" ] |
| } else if (!is_internal) { |
| # Adding this linker flag keeps us honest about not committing to a |
| # specific C++ ABI. If this flag is causing your library to not |
| # compile, consider whether your library really ought to be in the SDK. |
| # If so, consider including your library in the SDK as source rather than |
| # precompiled. If you do require precompilation, you probably need to |
| # find a way not to depend on dynamically linking C++ symbols because C++ |
| # does not have a sufficiently stable ABI for the purposes of our SDK. |
| # |
| # NOTE: This flag is not necessary for internal SDK libraries since they |
| # are not intended to be consumed by SDK users. |
| configs += [ "//build/config/fuchsia:no_cpp_standard_library" ] |
| } |
| |
| metadata = { |
| if (defined(invoker.metadata)) { |
| forward_variables_from(invoker.metadata, "*") |
| } |
| if (is_fuchsia) { |
| component_catalog = [ |
| { |
| sdk_id = sdk_id |
| label = get_label_info(":${target_name}", "label_with_toolchain") |
| }, |
| ] |
| |
| # Used by sdk_verify_runtime_deps() template. |
| sdk_runtime_deps = [ |
| { |
| sdk_id = sdk_id |
| label = get_label_info(":${target_name}", "label_with_toolchain") |
| }, |
| ] |
| } |
| } |
| |
| # 'sdk_shared_library()' targets in the 'partner' or 'public' category that |
| # are not testonly must be tracked by |
| # //build/sdk:sdk_versioned_shared_libraries_allowlist for SDK consistency |
| # checks (see https://fxbug.dev/105940). |
| if (invoker.category == "partner" || invoker.category == "public") { |
| deps += [ "//build/sdk:sdk_versioned_shared_libraries_allowlist" ] |
| } |
| } |
| |
| # Base path for binaries of this library in SDKs. |
| prebuilt_base = "arch/${target_cpu}" |
| |
| # Identify dependencies and their metadata files. |
| sdk_deps = [] |
| sdk_metas = [] |
| all_deps = [] |
| if (defined(invoker.deps)) { |
| all_deps += invoker.deps |
| } |
| if (defined(invoker.public_deps)) { |
| all_deps += invoker.public_deps |
| } |
| |
| # If a prebuilt library is only provided for packaging purposes (by not |
| # exposing headers) then its dependencies need not be included in an SDK. |
| if (defined(invoker.public_deps) && !no_headers) { |
| foreach(dep, invoker.public_deps) { |
| full_label = get_label_info(dep, "label_no_toolchain") |
| sdk_dep = "${full_label}_sdk" |
| sdk_deps += [ sdk_dep ] |
| all_deps += [ sdk_dep ] |
| } |
| } |
| |
| # Runtime deps are already SDK targets. |
| if (defined(invoker.runtime_deps)) { |
| sdk_deps += invoker.runtime_deps |
| } |
| foreach(sdk_dep, sdk_deps) { |
| gen_dir = get_label_info(sdk_dep, "target_gen_dir") |
| name = get_label_info(sdk_dep, "name") |
| sdk_metas += [ "${gen_dir}/${name}.meta.json" ] |
| } |
| |
| # Process headers. |
| all_headers = [] |
| if ((defined(invoker.public) || defined(invoker.sources)) && !no_headers) { |
| if (defined(invoker.public)) { |
| all_headers += invoker.public |
| } |
| |
| if (defined(invoker.sources)) { |
| # If public headers are not defined, pick them from `sources`. |
| # |
| # NOTE: If this is an internal SDK library, headers from `sources` are |
| # always made available so Bazel can find them. |
| if (is_internal || !defined(invoker.public)) { |
| foreach(source_file, invoker.sources) { |
| extension = get_path_info(source_file, "extension") |
| if (extension == "h") { |
| all_headers += [ source_file ] |
| } |
| } |
| } |
| } |
| } |
| |
| sdk_metadata_headers = [] |
| sdk_header_files = [] |
| |
| if (defined(invoker.include_base)) { |
| include_base = invoker.include_base |
| } else { |
| if (is_internal) { |
| # by default, we want internal SDK elements to have their include path similar when used through |
| # an SDK (eg Bazel SDk) or directly (GN/Ninja), so that source code doesn't need to be changed |
| # when switching from one to the other. |
| include_base = "//" |
| } else { |
| include_base = "include" |
| } |
| } |
| assert(include_base != "") # Work around for unused variable warning when |
| |
| include_dest = "${sdk_root_path}/include" |
| |
| foreach(header, all_headers) { |
| destination = rebase_path(header, include_base) |
| header_dest = "${include_dest}/${destination}" |
| sdk_metadata_headers += [ header_dest ] |
| sdk_header_files += [ |
| { |
| source = header |
| dest = header_dest |
| }, |
| ] |
| } |
| |
| verify_public_headers(target_name) { |
| headers = all_headers |
| } |
| |
| # Add binaries. |
| # |
| # Select shared library binary from the proper toolchain. |
| # See shlib_toolchain_no_default_variant_redirect documentation comment |
| # in //build/config/BUILDCONFIG.gn to understand why this is needed. |
| shared_lib_target = |
| ":${main_target_name}(${shlib_toolchain_no_default_variant_redirect})" |
| shared_out_dir = get_label_info(shared_lib_target, "root_out_dir") |
| lib_name = "lib${output_name}.so" |
| link_lib = "${prebuilt_base}/lib/${lib_name}" |
| dist_lib = "${prebuilt_base}/dist/${lib_name}" |
| sdk_files = sdk_header_files + [ |
| { |
| source = "${shared_out_dir}/link_stub/${lib_name}" |
| dest = link_lib |
| }, |
| { |
| source = "${shared_out_dir}/${lib_name}" |
| dest = dist_lib |
| }, |
| ] |
| |
| if (defined(invoker.symbols_api)) { |
| sdk_files += [ |
| { |
| source = get_path_info(invoker.symbols_api, "abspath") |
| dest = "${sdk_root_path}/" + get_path_info(invoker.symbols_api, "file") |
| }, |
| ] |
| } |
| |
| if (generate_plasa_artifacts) { |
| _plasa_artifacts_target_name = "${main_target_name}_plasa" |
| plasa_fragment_cc(_plasa_artifacts_target_name) { |
| forward_variables_from(invoker, |
| [ |
| "source_dir", |
| "testonly", |
| "all_headers", |
| "all_deps", |
| "sdk_headers_for_internal_use", |
| ]) |
| file_base = sdk_root_path |
| } |
| } else { |
| not_needed([ |
| "all_deps", |
| "sdk_headers_for_internal_use", |
| ]) |
| } |
| |
| metadata_file = "${target_gen_dir}/${metadata_target_name}.sdk_meta.json" |
| debug_mapping_file = "${target_gen_dir}/${metadata_target_name}.mappings.txt" |
| debug_lib_file = "${shared_out_dir}/lib.unstripped/${lib_name}" |
| |
| action(metadata_target_name) { |
| forward_variables_from(invoker, [ "testonly" ]) |
| script = "//build/cpp/gen_sdk_prebuilt_meta_file.py" |
| |
| inputs = sdk_metas + [ |
| debug_lib_file, |
| "//build/cpp/binaries.py", |
| "//build/images/elfinfo.py", |
| ] |
| |
| outputs = [ |
| debug_mapping_file, |
| metadata_file, |
| ] |
| |
| args = [ |
| "--out", |
| rebase_path(metadata_file, root_build_dir), |
| "--name", |
| atom_name, |
| "--format", |
| "shared", |
| "--root", |
| sdk_root_path, |
| "--include-dir", |
| include_dest, |
| "--dist-path", |
| "lib/${lib_name}", |
| "--arch", |
| target_cpu, |
| "--lib-link", |
| link_lib, |
| "--lib-dist", |
| dist_lib, |
| "--lib-debug-file", |
| rebase_path(debug_lib_file, root_build_dir), |
| "--debug-mapping", |
| rebase_path(debug_mapping_file, root_build_dir), |
| ] |
| args += [ "--deps" ] + rebase_path(sdk_metas, root_build_dir) |
| args += [ "--headers" ] + sdk_metadata_headers |
| if (defined(invoker.symbols_api)) { |
| args += [ "--ifs" ] + [ get_path_info(invoker.symbols_api, "file") ] |
| } |
| |
| deps = sdk_deps + [ shared_lib_target ] |
| } |
| |
| # Exempt internal libraries from pragma verification. |
| should_verify_pragma = !is_internal && !no_headers |
| if (should_verify_pragma) { |
| verify_pragma_target_name = "${target_name}_sdk_pragma" |
| verify_pragma_once(verify_pragma_target_name) { |
| headers = all_headers |
| } |
| } |
| |
| # Don't verify the public symbols for non-publicized APIs. |
| # TODO(abrachet): Make this required for all libraries. |
| if (invoker.category == "partner" || invoker.category == "public") { |
| assert( |
| defined(invoker.symbols_api), |
| "Must define path to an ifs file. See comment in //build/cpp/verify_public_symbols.gni") |
| main_target_dir = get_label_info(":${main_target_name}", "dir") |
| |
| verify_public_symbols_target_name = |
| "${target_name}_sdk_verify_public_symbols" |
| verify_public_symbols(verify_public_symbols_target_name) { |
| current = "${shared_out_dir}/lib${output_name}.ifs" |
| reference = invoker.symbols_api |
| library_name = main_target_dir |
| |
| deps = [ shared_lib_target ] |
| } |
| } |
| |
| sdk_atom(manifest_target_name) { |
| forward_variables_from(invoker, [ "testonly" ]) |
| |
| id = sdk_id |
| |
| category = invoker.category |
| |
| if (defined(api_reference) && !no_headers) { |
| api = api_reference |
| |
| api_contents = sdk_header_files |
| } |
| |
| meta = { |
| source = metadata_file |
| dest = "${sdk_root_path}/meta.json" |
| schema = "cc_prebuilt_library" |
| } |
| |
| files = sdk_files |
| |
| file_list = debug_mapping_file |
| |
| deps = sdk_deps |
| |
| non_sdk_deps = [ |
| ":${metadata_target_name}", |
| shared_lib_target, |
| ] |
| if (should_verify_pragma) { |
| non_sdk_deps += [ ":${verify_pragma_target_name}" ] |
| } |
| if (generate_plasa_artifacts) { |
| non_sdk_deps += [ ":${_plasa_artifacts_target_name}" ] |
| } |
| if (category == "partner" || category == "public") { |
| non_sdk_deps += [ ":${verify_public_symbols_target_name}" ] |
| } |
| |
| # Explicitly add non-public dependencies, in case some of the source files |
| # are generated. |
| if (defined(invoker.deps)) { |
| non_sdk_deps += invoker.deps |
| } |
| } |
| |
| sdk_manifest_file = "${target_gen_dir}/${manifest_target_name}.sdk" |
| |
| verify_runtime_deps_target = "${target_name}_verify" |
| |
| sdk_verify_runtime_deps(verify_runtime_deps_target) { |
| atom_target = ":$main_target_name" |
| manifest_file = sdk_manifest_file |
| manifest_target = ":$manifest_target_name" |
| } |
| |
| sdk_atom_alias(sdk_target_name) { |
| atom = ":${manifest_target_name}" |
| |
| non_sdk_deps = [ ":${verify_runtime_deps_target}" ] |
| |
| if (generate_plasa_artifacts) { |
| non_sdk_deps += [ ":${_plasa_artifacts_target_name}" ] |
| } |
| } |
| } |