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