xref: /aosp_15_r20/external/cronet/build/partitioned_shared_library.gni (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1# Copyright 2019 The Chromium Authors
2
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6import("//build/config/android/config.gni")
7import("//build/config/clang/clang.gni")
8import("//build/config/compiler/compiler.gni")
9if (build_with_chromium) {
10  import("//third_party/jni_zero/jni_zero.gni")
11}
12
13# This template creates a set of shared libraries, by linking a single
14# "partitioned" shared library, then splitting it into multiple pieces.
15# The intention is to facilitate code-splitting between a base library and
16# additional feature-specific libraries that may be obtained and loaded at a
17# later time.
18#
19# The combined library is an intermediate product made by leveraging the LLVM
20# toolchain.  Code modules may be labeled via compiler flag as belonging to a
21# particular partition.  At link time, any symbols reachable by only a single
22# partition's entrypoints will be located in a partition-specific library
23# segment.  After linking, the segments are split apart using objcopy into
24# separate libraries.  The main library is then packaged with the application
25# as usual, while feature libraries may be packaged, delivered and loaded
26# separately (via an Android Dynamic Feature Module).
27#
28# When loading a feature library, the intended address of the library must be
29# supplied to the loader, so that it can be mapped to the memory location.  The
30# address offsets of the feature libraries are stored in the base library and
31# accessed through special symbols named according to the partitions.
32#
33# The template instantiates targets for the base library, as well as each
34# specified partition, based on the root target name.  Example:
35#
36#   - libmonochrome            (base library)
37#   - libmonochrome_foo        (partition library for feature 'foo')
38#   - libmonochrome_bar        (partition library for feature 'bar')
39#
40# Note that the feature library filenames are chosen based on the main
41# library's name (eg. libmonochrome_foo.so), but the soname of the feature
42# library is based on the feature name (eg. "foo").  This should generally be
43# okay, with the caveat that loading the library multiple times *might* cause
44# problems in Android.
45#
46# This template uses shared_library's default configurations.
47#
48# Variables:
49#   partitions: A list of library partition names to extract, in addition to
50#     the base library.
51
52template("partitioned_shared_library") {
53  assert(is_clang)
54  forward_variables_from(invoker, [ "testonly" ])
55
56  _combined_library_target = "${target_name}__combined"
57
58  # Strip "lib" from target names; it will be re-added to output libraries.
59  _output_name = string_replace(target_name, "lib", "")
60
61  shared_library(_combined_library_target) {
62    forward_variables_from(invoker, "*", [ "partitions" ])
63    if (!defined(ldflags)) {
64      ldflags = []
65    }
66    ldflags += [
67      "-Wl,-soname,lib${_output_name}.so",
68      "--partitioned-library",
69    ]
70
71    # This shared library is an intermediate artifact that should not packaged
72    # into the final build. Therefore, reset metadata.
73    metadata = {
74    }
75  }
76
77  template("partition_action") {
78    action(target_name) {
79      deps = [ ":$_combined_library_target" ]
80      script = "//build/extract_partition.py"
81      sources =
82          [ "$root_out_dir/lib.unstripped/lib${_output_name}__combined.so" ]
83      outputs = [
84        invoker.unstripped_output,
85        invoker.stripped_output,
86      ]
87      data = [ invoker.unstripped_output ]
88      metadata = {
89        shared_libraries = [ invoker.stripped_output ]
90      }
91      args = [
92        "--objcopy",
93        rebase_path("$clang_base_path/bin/llvm-objcopy", root_build_dir),
94        "--unstripped-output",
95        rebase_path(invoker.unstripped_output, root_build_dir),
96        "--stripped-output",
97        rebase_path(invoker.stripped_output, root_build_dir),
98      ]
99      if (defined(invoker.partition) && invoker.partition != "") {
100        args += [
101          "--partition",
102          "${invoker.partition}",
103        ]
104      }
105
106      if (use_debug_fission) {
107        args += [ "--split-dwarf" ]
108        outputs += [ invoker.unstripped_output + ".dwp" ]
109      }
110      args += [ rebase_path(sources[0], root_build_dir) ]
111    }
112  }
113
114  partition_action(target_name) {
115    stripped_output = "$root_out_dir/lib${_output_name}.so"
116    unstripped_output = "$root_out_dir/lib.unstripped/lib${_output_name}.so"
117  }
118
119  # Note that as of now, non-base partition libraries are placed in a
120  # subdirectory of the root output directory.  This is because partition
121  # sonames are not sensitive to the filename of the base library, and as such,
122  # their corresponding file names may be generated multiple times by different
123  # base libraries.  To avoid collisions, each base library target has a
124  # corresponding subdir for its extra partitions.
125  #
126  # If this proves problematic to various pieces of infrastructure, a proposed
127  # alternative is allowing the linker to rename partitions.  For example,
128  # feature "foo" may be a partition.  If two different base libraries both
129  # define "foo" partitions, the linker may be made to accept an extra command
130  # to rename the partition's soname to "foo1" or "foo2".  Other build config
131  # can name the libraries foo1.so and foo2.so, allowing them to reside in the
132  # same directory.
133  foreach(_partition, invoker.partitions) {
134    partition_action("${target_name}_${_partition}") {
135      partition = "${_partition}_partition"
136      stripped_output = "$root_out_dir/lib${_output_name}_${partition}.so"
137      unstripped_output =
138          "$root_out_dir/lib.unstripped/lib${_output_name}_${partition}.so"
139    }
140  }
141}
142
143set_defaults("partitioned_shared_library") {
144  configs = default_shared_library_configs
145}
146
147# native_with_jni for partitioned shared libraries - see native_with_jni for
148# details.
149template("partitioned_shared_library_with_jni") {
150  native_with_jni(target_name) {
151    forward_variables_from(invoker, "*", TESTONLY_AND_VISIBILITY)
152    forward_variables_from(invoker, TESTONLY_AND_VISIBILITY)
153    target_type = "partitioned_shared_library"
154    target_type_import = "//build/partitioned_shared_library.gni"
155  }
156}
157set_defaults("partitioned_shared_library_with_jni") {
158  configs = default_shared_library_configs
159}
160