1# Copyright (C) 2021 The Android Open Source Project
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#     http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15"""A macro to handle shared library stripping."""
16
17load("@bazel_skylib//lib:paths.bzl", "paths")
18load(":cc_library_common.bzl", "CcAndroidMkInfo", "check_valid_ldlibs")
19load(":clang_tidy.bzl", "collect_deps_clang_tidy_info")
20load(
21    ":composed_transitions.bzl",
22    "drop_lto_and_sanitizer_transition",
23    "lto_and_sanitizer_deps_transition",
24)
25
26CcUnstrippedInfo = provider(
27    "Provides unstripped binary/shared library",
28    fields = {
29        "unstripped": "unstripped target",
30    },
31)
32
33# Keep this consistent with soong/cc/strip.go#NeedsStrip.
34def _needs_strip(ctx):
35    if ctx.attr.none:
36        return False
37    if ctx.target_platform_has_constraint(ctx.attr._android_constraint[platform_common.ConstraintValueInfo]):
38        return True
39    return (ctx.attr.all or ctx.attr.keep_symbols or
40            ctx.attr.keep_symbols_and_debug_frame or ctx.attr.keep_symbols_list)
41
42# Keep this consistent with soong/cc/strip.go#strip and soong/cc/builder.go#transformStrip.
43def _get_strip_args(attrs):
44    strip_args = []
45    keep_mini_debug_info = False
46    if attrs.keep_symbols:
47        strip_args.append("--keep-symbols")
48    elif attrs.keep_symbols_and_debug_frame:
49        strip_args.append("--keep-symbols-and-debug-frame")
50    elif attrs.keep_symbols_list:
51        strip_args.append("-k" + ",".join(attrs.keep_symbols_list))
52    elif not attrs.all:
53        strip_args.append("--keep-mini-debug-info")
54        keep_mini_debug_info = True
55
56    if not keep_mini_debug_info:
57        strip_args.append("--add-gnu-debuglink")
58
59    return strip_args
60
61# https://cs.android.com/android/platform/superproject/+/master:build/soong/cc/builder.go;l=131-146;drc=master
62def stripped_impl(ctx, file, prefix = "", stem = "", suffix = "", extension = "", subdir = ""):
63    filename_stem = stem or ctx.attr.name
64    filename = prefix + filename_stem + suffix + extension
65    out_file = ctx.actions.declare_file(
66        paths.join(
67            subdir,  # Prevent name collision by generating in a directory unique to the target
68            filename,
69        ),
70    )
71    if not _needs_strip(ctx):
72        ctx.actions.symlink(
73            output = out_file,
74            target_file = file,
75        )
76        return out_file
77    d_file = ctx.actions.declare_file(
78        paths.join(
79            subdir,
80            filename + ".d",
81        ),
82    )
83
84    ctx.actions.run(
85        env = {
86            "CREATE_MINIDEBUGINFO": ctx.executable._create_minidebuginfo.path,
87            "XZ": ctx.executable._xz.path,
88            "CLANG_BIN": ctx.executable._ar.dirname,
89        },
90        inputs = [file],
91        tools = [
92            ctx.executable._ar,
93            ctx.executable._create_minidebuginfo,
94            ctx.executable._objcopy,
95            ctx.executable._readelf,
96            ctx.executable._strip,
97            ctx.executable._strip_script,
98            ctx.executable._xz,
99        ],
100        outputs = [out_file, d_file],
101        executable = ctx.executable._strip_script,
102        arguments = _get_strip_args(ctx.attr) + [
103            "-i",
104            file.path,
105            "-o",
106            out_file.path,
107            "-d",
108            d_file.path,
109        ],
110        mnemonic = "CcStrip",
111    )
112    return out_file
113
114strip_attrs = dict(
115    keep_symbols = attr.bool(default = False),
116    keep_symbols_and_debug_frame = attr.bool(default = False),
117    all = attr.bool(default = False),
118    none = attr.bool(default = False),
119    keep_symbols_list = attr.string_list(default = []),
120)
121common_strip_attrs = dict(
122    strip_attrs,
123    _xz = attr.label(
124        cfg = "exec",
125        executable = True,
126        allow_single_file = True,
127        default = "//prebuilts/build-tools:linux-x86/bin/xz",
128    ),
129    _create_minidebuginfo = attr.label(
130        cfg = "exec",
131        executable = True,
132        allow_single_file = True,
133        default = "//prebuilts/build-tools:linux-x86/bin/create_minidebuginfo",
134    ),
135    _strip_script = attr.label(
136        cfg = "exec",
137        executable = True,
138        allow_single_file = True,
139        default = "//build/soong/scripts:strip.sh",
140    ),
141    _ar = attr.label(
142        cfg = "exec",
143        executable = True,
144        allow_single_file = True,
145        default = "//prebuilts/clang/host/linux-x86:llvm-ar",
146    ),
147    _strip = attr.label(
148        cfg = "exec",
149        executable = True,
150        allow_single_file = True,
151        default = "//prebuilts/clang/host/linux-x86:llvm-strip",
152    ),
153    _readelf = attr.label(
154        cfg = "exec",
155        executable = True,
156        allow_single_file = True,
157        default = "//prebuilts/clang/host/linux-x86:llvm-readelf",
158    ),
159    _objcopy = attr.label(
160        cfg = "exec",
161        executable = True,
162        allow_single_file = True,
163        default = "//prebuilts/clang/host/linux-x86:llvm-objcopy",
164    ),
165    _cc_toolchain = attr.label(
166        default = Label("@local_config_cc//:toolchain"),
167        providers = [cc_common.CcToolchainInfo],
168    ),
169    _android_constraint = attr.label(
170        default = Label("//build/bazel_common_rules/platforms/os:android"),
171    ),
172    _darwin_constraint = attr.label(
173        default = Label("//build/bazel_common_rules/platforms/os:darwin"),
174    ),
175    _linux_constraint = attr.label(
176        default = Label("//build/bazel_common_rules/platforms/os:linux"),
177    ),
178    _windows_constraint = attr.label(
179        default = Label("//build/bazel_common_rules/platforms/os:windows"),
180    ),
181)
182
183def _stripped_shared_library_impl(ctx):
184    check_valid_ldlibs(ctx, ctx.attr.linkopts)
185
186    out_file = stripped_impl(ctx, ctx.file.src, prefix = "lib", extension = ".so", subdir = ctx.attr.name)
187
188    return [
189        DefaultInfo(files = depset([out_file])),
190        ctx.attr.src[CcSharedLibraryInfo],
191        ctx.attr.src[OutputGroupInfo],
192    ]
193
194stripped_shared_library = rule(
195    implementation = _stripped_shared_library_impl,
196    attrs = dict(
197        common_strip_attrs,
198        src = attr.label(
199            mandatory = True,
200            providers = [CcSharedLibraryInfo],
201            allow_single_file = True,
202        ),
203        linkopts = attr.string_list(default = []),  # Used for validation
204    ),
205    toolchains = ["@bazel_tools//tools/cpp:toolchain_type"],
206)
207
208# A marker provider to distinguish a cc_binary from everything else that exports
209# a CcInfo.
210StrippedCcBinaryInfo = provider()
211
212def _stripped_binary_impl(ctx):
213    check_valid_ldlibs(ctx, ctx.attr.linkopts)
214
215    common_providers = [
216        ctx.attr.src[0][CcInfo],
217        ctx.attr.src[0][InstrumentedFilesInfo],
218        ctx.attr.src[0][DebugPackageInfo],
219        ctx.attr.src[0][OutputGroupInfo],
220        StrippedCcBinaryInfo(),  # a marker for dependents
221        CcUnstrippedInfo(
222            unstripped = ctx.attr.unstripped,
223        ),
224        collect_deps_clang_tidy_info(ctx),
225    ] + [
226        d[CcAndroidMkInfo]
227        for d in ctx.attr.androidmk_deps
228    ]
229
230    # Generate binary in a directory unique to this target to prevent possible collisions due to common `stem`
231    # Generate in `bin` to prevent incrementality issues for mixed builds where <package>/<name> could be a file and not a dir
232    subdir = paths.join("bin", ctx.attr.name)
233    out_file = stripped_impl(ctx, ctx.file.src, stem = ctx.attr.stem, suffix = ctx.attr.suffix, subdir = subdir)
234
235    return [
236        DefaultInfo(
237            files = depset([out_file]),
238            executable = out_file,
239            runfiles = ctx.attr.src[0][DefaultInfo].default_runfiles,
240        ),
241    ] + common_providers
242
243_rule_attrs = dict(
244    common_strip_attrs,
245    src = attr.label(
246        mandatory = True,
247        allow_single_file = True,
248        providers = [CcInfo],
249        cfg = lto_and_sanitizer_deps_transition,
250    ),
251    linkopts = attr.string_list(default = []),  # Used for validation
252    runtime_deps = attr.label_list(
253        providers = [CcInfo],
254        doc = "Deps that should be installed along with this target. Read by the apex cc aspect.",
255    ),
256    androidmk_deps = attr.label_list(
257        providers = [CcAndroidMkInfo],
258        cfg = lto_and_sanitizer_deps_transition,
259    ),
260    stem = attr.string(),
261    suffix = attr.string(),
262    unstripped = attr.label(
263        mandatory = True,
264        allow_single_file = True,
265        cfg = lto_and_sanitizer_deps_transition,
266        doc = "Unstripped binary to be returned by ",
267    ),
268    package_name = attr.string(
269        mandatory = True,
270        doc = "Just the path to the target package. Used by transitions.",
271    ),
272    _allowlist_function_transition = attr.label(
273        default = "@bazel_tools//tools/allowlists/function_transition_allowlist",
274    ),
275)
276
277stripped_binary = rule(
278    implementation = _stripped_binary_impl,
279    cfg = drop_lto_and_sanitizer_transition,
280    attrs = _rule_attrs,
281    executable = True,
282    toolchains = ["@bazel_tools//tools/cpp:toolchain_type"],
283)
284
285stripped_test = rule(
286    implementation = _stripped_binary_impl,
287    cfg = drop_lto_and_sanitizer_transition,
288    attrs = _rule_attrs,
289    test = True,
290    toolchains = ["@bazel_tools//tools/cpp:toolchain_type"],
291)
292