• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright (C) 2022 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
15load("@bazel_skylib//lib:paths.bzl", "paths")
16load("@bazel_skylib//lib:sets.bzl", "sets")
17load("@bazel_skylib//lib:unittest.bzl", "analysistest", "asserts")
18load("//build/bazel/rules/cc:cc_library_shared.bzl", "cc_library_shared")
19load("//build/bazel/rules/cc:cc_library_static.bzl", "cc_library_static")
20load("//build/bazel/rules/test_common:args.bzl", "get_arg_value", "get_arg_values")
21load(":abi_dump.bzl", "abi_dump", "find_abi_config")
22
23ABI_LINKER = "prebuilts/clang-tools/linux-x86/bin/header-abi-linker"
24ABI_DIFF = "prebuilts/clang-tools/linux-x86/bin/header-abi-diff"
25
26# cxa_demangle.cpp is added as part of the stl in cc_library_shared, so it's dump
27# file is always created.
28CXA_DEMANGLE = "external/libcxxabi/external/libcxxabi/src/libc++demangle.cxa_demangle.cpp.sdump"
29REF_DUMPS_HOME = "build/bazel/rules/abi/abi-dumps"
30ARCH = "x86_64"
31BITNESS = 64
32CONFIG_SETTING_COVERAGE = {
33    "//command_line_option:collect_code_coverage": True,
34}
35CONFIG_SETTING_SKIP_ABI_CHECK = {
36    "@//build/bazel/flags/cc/abi:skip_abi_checks": True,
37}
38CONFIG_SETTING_IN_APEX = {
39    "@//build/bazel/rules/apex:within_apex": True,
40}
41
42def _abi_linker_action_test_impl(ctx):
43    env = analysistest.begin(ctx)
44    bin_home = analysistest.target_bin_dir_path(env)
45    bazel_out_base = paths.join(bin_home, ctx.label.package)
46
47    actions = analysistest.target_actions(env)
48    link_actions = [a for a in actions if a.mnemonic == "AbiLink"]
49
50    asserts.true(
51        env,
52        len(link_actions) == 1,
53        "Abi link action not found: %s" % link_actions,
54    )
55
56    action = link_actions[0]
57
58    stripped_so = paths.join(bazel_out_base, "lib" + ctx.attr.lib_name + "_stripped.so")
59    symbol_file = paths.join(ctx.label.package, ctx.attr.symbol_file)
60    asserts.set_equals(
61        env,
62        expected = sets.make(
63            [paths.join(bazel_out_base, ctx.label.package, file + ".sdump") for file in ctx.attr.dumps] + [
64                ABI_LINKER,
65                paths.join(bin_home, CXA_DEMANGLE),
66                stripped_so,
67                symbol_file,
68            ],
69        ),
70        actual = sets.make([
71            file.path
72            for file in action.inputs.to_list()
73        ]),
74    )
75
76    lsdump_file = paths.join(bazel_out_base, ctx.attr.lib_name + ".so.lsdump")
77    asserts.set_equals(
78        env,
79        expected = sets.make([lsdump_file]),
80        actual = sets.make([
81            file.path
82            for file in action.outputs.to_list()
83        ]),
84    )
85
86    argv = action.argv
87    _test_arg_set_correctly(env, argv, "--root-dir", ".")
88    _test_arg_set_correctly(env, argv, "-o", lsdump_file)
89    _test_arg_set_correctly(env, argv, "-so", stripped_so)
90    _test_arg_set_correctly(env, argv, "-arch", ARCH)
91    _test_arg_set_correctly(env, argv, "-v", symbol_file)
92    _test_arg_set_multi_values_correctly(env, argv, "--exclude-symbol-version", ctx.attr.exclude_symbol_versions)
93    _test_arg_set_multi_values_correctly(env, argv, "--exclude-symbol-tag", ctx.attr.exclude_symbol_tags)
94    _test_arg_set_multi_values_correctly(
95        env,
96        argv,
97        "-I",
98        [paths.join(bazel_out_base, file) for file in ctx.attr.export_includes] +
99        [paths.join(ctx.label.package, file) for file in ctx.attr.export_includes] +
100        ctx.attr.export_absolute_includes +
101        [paths.join(bin_home, file) for file in ctx.attr.export_absolute_includes],
102    )
103
104    sdump_files = []
105    args = " ".join(argv).split(" ")
106    args_len = len(args)
107
108    # The .sdump files are at the end of the args, the abi linker binary is always at index 0.
109    for i in reversed(range(args_len)):
110        if ".sdump" in args[i]:
111            sdump_files.append(args[i])
112        else:
113            break
114
115    asserts.set_equals(
116        env,
117        expected = sets.make(
118            [paths.join(bazel_out_base, ctx.label.package, file + ".sdump") for file in ctx.attr.dumps] + [
119                paths.join(bin_home, CXA_DEMANGLE),
120            ],
121        ),
122        actual = sets.make(sdump_files),
123    )
124
125    return analysistest.end(env)
126
127__abi_linker_action_test = analysistest.make(
128    impl = _abi_linker_action_test_impl,
129    attrs = {
130        "dumps": attr.string_list(),
131        "lib_name": attr.string(),
132        "symbol_file": attr.string(),
133        "exclude_symbol_versions": attr.string_list(),
134        "exclude_symbol_tags": attr.string_list(),
135        "export_includes": attr.string_list(),
136        "export_absolute_includes": attr.string_list(),
137        "_platform_utils": attr.label(default = Label("//build/bazel/platforms:platform_utils")),
138    },
139)
140
141def _abi_linker_action_test(**kwargs):
142    __abi_linker_action_test(
143        target_compatible_with = [
144            "//build/bazel/platforms/arch:x86_64",
145            "//build/bazel/platforms/os:android",
146        ],
147        **kwargs
148    )
149
150def _test_abi_linker_action():
151    name = "abi_linker_action"
152    static_dep_a = name + "_static_dep_a"
153    static_dep_b = name + "_static_dep_b"
154    test_name = name + "_test"
155
156    cc_library_static(
157        name = static_dep_a,
158        srcs = ["static_a.cpp"],
159        srcs_c = ["static_a.c"],
160        export_includes = ["export_includes_static_a"],
161        export_absolute_includes = ["export_absolute_includes_static_a"],
162        export_system_includes = ["export_system_includes_static_a"],
163        local_includes = ["local_includes_static_a"],
164        absolute_includes = ["absolute_includes_static_a"],
165        tags = ["manual"],
166    )
167
168    cc_library_static(
169        name = static_dep_b,
170        srcs = ["static_b.cpp"],
171        srcs_c = ["static_b.c"],
172        deps = [":" + static_dep_a],
173        export_includes = ["export_includes_static_b"],
174        export_absolute_includes = ["export_absolute_includes_static_b"],
175        export_system_includes = ["export_system_includes_static_b"],
176        local_includes = ["local_includes_static_b"],
177        absolute_includes = ["absolute_includes_static_b"],
178        tags = ["manual"],
179    )
180
181    symbol_file = "shared_a.map.txt"
182    exclude_symbol_versions = ["30", "31"]
183    exclude_symbol_tags = ["func_1", "func_2"]
184
185    cc_library_shared(
186        name = name,
187        srcs = ["shared.cpp"],
188        srcs_c = ["shared.c"],
189        deps = [":" + static_dep_b],
190        export_includes = ["export_includes_shared"],
191        export_absolute_includes = ["export_absolute_includes_shared"],
192        export_system_includes = ["export_system_includes_shared"],
193        local_includes = ["local_includes_shared"],
194        absolute_includes = ["absolute_includes_shared"],
195        stubs_symbol_file = name + ".map.txt",
196        abi_checker_symbol_file = symbol_file,
197        abi_checker_exclude_symbol_versions = exclude_symbol_versions,
198        abi_checker_exclude_symbol_tags = exclude_symbol_tags,
199        tags = ["manual"],
200    )
201
202    _abi_linker_action_test(
203        name = test_name,
204        target_under_test = name + "_abi_dump",
205        dumps = [
206            static_dep_a + ".static_a.cpp",
207            static_dep_b + ".static_b.cpp",
208            name + "__internal_root.shared.cpp",
209            static_dep_a + ".static_a.c",
210            static_dep_b + ".static_b.c",
211            name + "__internal_root.shared.c",
212        ],
213        lib_name = name,
214        symbol_file = symbol_file,
215        exclude_symbol_versions = exclude_symbol_versions,
216        exclude_symbol_tags = exclude_symbol_tags,
217        export_includes = [
218            "export_includes_shared",
219            "export_includes_static_a",
220            "export_includes_static_b",
221        ],
222        export_absolute_includes = [
223            "export_absolute_includes_shared",
224            "export_absolute_includes_static_a",
225            "export_absolute_includes_static_b",
226        ],
227    )
228
229    return test_name
230
231def _abi_linker_action_run_test_impl(ctx):
232    env = analysistest.begin(ctx)
233
234    actions = analysistest.target_actions(env)
235    link_actions = [a for a in actions if a.mnemonic == "AbiLink"]
236
237    asserts.true(
238        env,
239        len(link_actions) == 1,
240        "Abi link action not found: %s" % link_actions,
241    )
242
243    return analysistest.end(env)
244
245__abi_linker_action_run_test = analysistest.make(
246    impl = _abi_linker_action_run_test_impl,
247)
248
249def _abi_linker_action_run_test(**kwargs):
250    __abi_linker_action_run_test(
251        target_compatible_with = [
252            "//build/bazel/platforms/arch:x86_64",
253            "//build/bazel/platforms/os:android",
254        ],
255        **kwargs
256    )
257
258def _test_abi_linker_action_run_for_enabled():
259    name = "abi_linker_action_run_for_enabled"
260    test_name = name + "_test"
261
262    cc_library_shared(
263        name = name,
264        abi_checker_enabled = True,
265        tags = ["manual"],
266    )
267
268    _abi_linker_action_run_test(
269        name = test_name,
270        target_under_test = name + "_abi_dump",
271    )
272
273    return test_name
274
275def _abi_linker_action_not_run_test_impl(ctx):
276    env = analysistest.begin(ctx)
277
278    actions = analysistest.target_actions(env)
279    link_actions = [a for a in actions if a.mnemonic == "AbiLink"]
280
281    asserts.true(
282        env,
283        len(link_actions) == 0,
284        "Abi link action found: %s" % link_actions,
285    )
286
287    return analysistest.end(env)
288
289__abi_linker_action_not_run_test = analysistest.make(
290    impl = _abi_linker_action_not_run_test_impl,
291)
292
293def _abi_linker_action_not_run_test(**kwargs):
294    __abi_linker_action_not_run_test(
295        target_compatible_with = [
296            "//build/bazel/platforms/arch:x86_64",
297            "//build/bazel/platforms/os:android",
298        ],
299        **kwargs
300    )
301
302__abi_linker_action_not_run_for_no_device_test = analysistest.make(
303    impl = _abi_linker_action_not_run_test_impl,
304)
305
306def _abi_linker_action_not_run_for_no_device_test(**kwargs):
307    __abi_linker_action_not_run_for_no_device_test(
308        target_compatible_with = [
309            "//build/bazel/platforms/arch:x86_64",
310            "//build/bazel/platforms/os:linux",
311        ],
312        **kwargs
313    )
314
315__abi_linker_action_not_run_for_coverage_test = analysistest.make(
316    impl = _abi_linker_action_not_run_test_impl,
317    config_settings = CONFIG_SETTING_COVERAGE,
318)
319
320def _abi_linker_action_not_run_for_coverage_test(**kwargs):
321    __abi_linker_action_not_run_for_coverage_test(
322        target_compatible_with = [
323            "//build/bazel/platforms/arch:x86_64",
324            "//build/bazel/platforms/os:android",
325        ],
326        **kwargs
327    )
328
329__abi_linker_action_not_run_if_skipped_test = analysistest.make(
330    impl = _abi_linker_action_not_run_test_impl,
331    config_settings = CONFIG_SETTING_SKIP_ABI_CHECK,
332)
333
334def _abi_linker_action_not_run_if_skipped_test(**kwargs):
335    __abi_linker_action_not_run_if_skipped_test(
336        target_compatible_with = [
337            "//build/bazel/platforms/arch:x86_64",
338            "//build/bazel/platforms/os:android",
339        ],
340        **kwargs
341    )
342
343__abi_linker_action_not_run_apex_no_stubs_test = analysistest.make(
344    impl = _abi_linker_action_not_run_test_impl,
345    config_settings = CONFIG_SETTING_IN_APEX,
346)
347
348def _abi_linker_action_not_run_apex_no_stubs_test(**kwargs):
349    __abi_linker_action_not_run_apex_no_stubs_test(
350        target_compatible_with = [
351            "//build/bazel/platforms/arch:x86_64",
352            "//build/bazel/platforms/os:android",
353        ],
354        **kwargs
355    )
356
357def _test_abi_linker_action_not_run_for_default():
358    name = "abi_linker_action_not_run_for_default"
359    test_name = name + "_test"
360
361    cc_library_shared(
362        name = name,
363        tags = ["manual"],
364    )
365
366    _abi_linker_action_not_run_test(
367        name = test_name,
368        target_under_test = name + "_abi_dump",
369    )
370
371    return test_name
372
373def _test_abi_linker_action_not_run_for_disabled():
374    name = "abi_linker_action_not_run_for_disabled"
375    test_name = name + "_test"
376
377    cc_library_shared(
378        name = name,
379        stubs_symbol_file = name + ".map.txt",
380        abi_checker_enabled = False,
381        tags = ["manual"],
382    )
383
384    _abi_linker_action_not_run_test(
385        name = test_name,
386        target_under_test = name + "_abi_dump",
387    )
388
389    return test_name
390
391def _test_abi_linker_action_not_run_for_no_device():
392    name = "abi_linker_action_not_run_for_no_device"
393    test_name = name + "_test"
394
395    cc_library_shared(
396        name = name,
397        abi_checker_enabled = True,
398        tags = ["manual"],
399    )
400
401    _abi_linker_action_not_run_for_no_device_test(
402        name = test_name,
403        target_under_test = name + "_abi_dump",
404    )
405
406    return test_name
407
408def _test_abi_linker_action_not_run_if_skipped():
409    name = "abi_linker_action_not_run_if_skipped"
410    test_name = name + "_test"
411
412    cc_library_shared(
413        name = name,
414        abi_checker_enabled = True,
415        tags = ["manual"],
416    )
417
418    _abi_linker_action_not_run_if_skipped_test(
419        name = test_name,
420        target_under_test = name + "_abi_dump",
421    )
422
423    return test_name
424
425def _test_abi_linker_action_not_run_for_coverage_enabled():
426    name = "abi_linker_action_not_run_for_coverage_enabled"
427    test_name = name + "_test"
428
429    cc_library_shared(
430        name = name,
431        abi_checker_enabled = True,
432        features = ["coverage"],
433        # Coverage will add an extra lib to all the shared libs, we try to avoid
434        # that by clearing the system_dynamic_deps and stl.
435        system_dynamic_deps = [],
436        stl = "none",
437        tags = ["manual"],
438    )
439
440    _abi_linker_action_not_run_for_coverage_test(
441        name = test_name,
442        target_under_test = name + "_abi_dump",
443    )
444
445    return test_name
446
447def _test_abi_linker_action_not_run_for_apex_no_stubs():
448    name = "abi_linker_action_not_run_for_apex_no_stubs"
449    test_name = name + "_test"
450
451    cc_library_shared(
452        name = name,
453        abi_checker_enabled = True,
454        tags = ["manual"],
455    )
456
457    _abi_linker_action_not_run_apex_no_stubs_test(
458        name = test_name,
459        target_under_test = name + "_abi_dump",
460    )
461
462    return test_name
463
464def _abi_diff_action_test_impl(ctx):
465    env = analysistest.begin(ctx)
466    actions = analysistest.target_actions(env)
467    diff_actions = [a for a in actions if a.mnemonic == "AbiDiff"]
468
469    asserts.true(
470        env,
471        len(diff_actions) == 2,
472        "There should be two abi diff actions: %s" % diff_actions,
473    )
474
475    prev_version, version = find_abi_config(ctx)
476    _verify_abi_diff_action(ctx, env, diff_actions[0], prev_version, True)
477    _verify_abi_diff_action(ctx, env, diff_actions[1], version, False)
478
479    return analysistest.end(env)
480
481def _verify_abi_diff_action(ctx, env, action, version, is_prev_version):
482    bin_home = analysistest.target_bin_dir_path(env)
483    bazel_out_base = paths.join(bin_home, ctx.label.package)
484    lsdump_file = paths.join(bazel_out_base, ctx.attr.lib_name + ".so.lsdump")
485
486    ref_dump = paths.join(
487        REF_DUMPS_HOME,
488        "platform",
489        str(version),
490        str(BITNESS),
491        ARCH,
492        "source-based",
493        ctx.attr.lib_name + ".so.lsdump",
494    )
495    asserts.set_equals(
496        env,
497        expected = sets.make([
498            lsdump_file,
499            ABI_DIFF,
500            ref_dump,
501        ]),
502        actual = sets.make([
503            file.path
504            for file in action.inputs.to_list()
505        ]),
506    )
507
508    if is_prev_version:
509        diff_file = paths.join(bazel_out_base, ".".join([ctx.attr.lib_name, "so", str(version), "abidiff"]))
510    else:
511        diff_file = paths.join(bazel_out_base, ".".join([ctx.attr.lib_name, "so", "abidiff"]))
512
513    asserts.set_equals(
514        env,
515        expected = sets.make([diff_file]),
516        actual = sets.make([
517            file.path
518            for file in action.outputs.to_list()
519        ]),
520    )
521
522    argv = action.argv
523    _test_arg_set_correctly(env, argv, "-o", diff_file)
524    _test_arg_set_correctly(env, argv, "-old", ref_dump)
525    _test_arg_set_correctly(env, argv, "-new", lsdump_file)
526    _test_arg_set_correctly(env, argv, "-lib", ctx.attr.lib_name)
527    _test_arg_set_correctly(env, argv, "-arch", ARCH)
528    _test_arg_exists(env, argv, "-allow-unreferenced-changes")
529    _test_arg_exists(env, argv, "-allow-unreferenced-elf-symbol-changes")
530    _test_arg_exists(env, argv, "-allow-extensions")
531    if is_prev_version:
532        _test_arg_set_correctly(env, argv, "-target-version", str(version + 1))
533    else:
534        _test_arg_set_correctly(env, argv, "-target-version", "current")
535
536__abi_diff_action_test = analysistest.make(
537    impl = _abi_diff_action_test_impl,
538    attrs = {
539        "lib_name": attr.string(),
540        "_platform_utils": attr.label(default = Label("//build/bazel/platforms:platform_utils")),
541    },
542)
543
544def _abi_diff_action_test(**kwargs):
545    __abi_diff_action_test(
546        target_compatible_with = [
547            "//build/bazel/platforms/arch:x86_64",
548            "//build/bazel/platforms/os:android",
549        ],
550        **kwargs
551    )
552
553def _test_abi_diff_action():
554    name = "abi_diff_action"
555    test_name = name + "_test"
556
557    cc_library_shared(
558        name = name,
559        srcs = ["shared.cpp"],
560        tags = ["manual"],
561    )
562
563    lib_name = "lib" + name
564    abi_dump_name = name + "_abi_dump_new"
565    abi_dump(
566        name = abi_dump_name,
567        shared = name + "_stripped",
568        root = name + "__internal_root",
569        soname = lib_name + ".so",
570        enabled = True,
571        abi_ref_dumps_platform = "//build/bazel/rules/abi/abi-dumps/platform:bp2build_all_srcs",
572        ref_dumps_home = "build/bazel/rules/abi/abi-dumps",
573        tags = ["manual"],
574    )
575
576    _abi_diff_action_test(
577        name = test_name,
578        target_under_test = abi_dump_name,
579        lib_name = lib_name,
580    )
581
582    return test_name
583
584def _abi_diff_action_not_run_test_impl(ctx):
585    env = analysistest.begin(ctx)
586    actions = analysistest.target_actions(env)
587    diff_actions = [a for a in actions if a.mnemonic == "AbiDiff"]
588
589    asserts.true(
590        env,
591        len(diff_actions) == 0,
592        "Abi diff action found: %s" % diff_actions,
593    )
594
595    return analysistest.end(env)
596
597__abi_diff_action_not_run_test = analysistest.make(
598    impl = _abi_diff_action_not_run_test_impl,
599)
600
601def _abi_diff_action_not_run_test(**kwargs):
602    __abi_diff_action_not_run_test(
603        target_compatible_with = [
604            "//build/bazel/platforms/arch:x86_64",
605            "//build/bazel/platforms/os:android",
606        ],
607        **kwargs
608    )
609
610def _test_abi_diff_action_not_run_if_no_ref_dump_found():
611    name = "abi_diff_action_not_run_if_no_ref_dump_found"
612    test_name = name + "_test"
613
614    cc_library_shared(
615        name = name,
616        srcs = ["shared.cpp"],
617        tags = ["manual"],
618    )
619
620    lib_name = "lib" + name
621    abi_dump_name = name + "_abi_dump_new"
622    abi_dump(
623        name = abi_dump_name,
624        shared = name + "_stripped",
625        root = name + "__internal_root",
626        soname = lib_name + ".so",
627        enabled = True,
628        ref_dumps_home = "build/bazel/rules/abi/abi-dumps",
629        tags = ["manual"],
630    )
631
632    _abi_diff_action_not_run_test(
633        name = test_name,
634        target_under_test = abi_dump_name,
635    )
636
637    return test_name
638
639def _test_arg_set_correctly(env, argv, arg_name, expected):
640    arg = get_arg_value(argv, arg_name)
641    asserts.true(
642        env,
643        arg == expected,
644        "%s is not set correctly: expected %s, actual %s" % (arg_name, expected, arg),
645    )
646
647def _test_arg_set_multi_values_correctly(env, argv, arg_name, expected):
648    args = get_arg_values(argv, arg_name)
649    asserts.set_equals(
650        env,
651        expected = sets.make(expected),
652        actual = sets.make(args),
653    )
654
655def _test_arg_exists(env, argv, arg_name):
656    asserts.true(
657        env,
658        arg_name in argv,
659        "arg %s is not found" % arg_name,
660    )
661
662def abi_dump_test_suite(name):
663    native.test_suite(
664        name = name,
665        tests = [
666            _test_abi_linker_action(),
667            _test_abi_linker_action_not_run_for_default(),
668            _test_abi_linker_action_not_run_for_disabled(),
669            _test_abi_linker_action_run_for_enabled(),
670            _test_abi_linker_action_not_run_for_no_device(),
671            _test_abi_linker_action_not_run_for_coverage_enabled(),
672            _test_abi_linker_action_not_run_if_skipped(),
673            _test_abi_linker_action_not_run_for_apex_no_stubs(),
674            _test_abi_diff_action(),
675            _test_abi_diff_action_not_run_if_no_ref_dump_found(),
676        ],
677    )
678