1// Copyright 2021 Google Inc. All rights reserved.
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
15package bp2build
16
17import (
18	"android/soong/android"
19	"android/soong/cc"
20	"strings"
21	"testing"
22)
23
24const (
25	// See cc/testing.go for more context
26	soongCcLibraryPreamble = `
27cc_defaults {
28	name: "linux_bionic_supported",
29}
30
31toolchain_library {
32	name: "libclang_rt.builtins-x86_64-android",
33	defaults: ["linux_bionic_supported"],
34	vendor_available: true,
35	vendor_ramdisk_available: true,
36	product_available: true,
37	recovery_available: true,
38	native_bridge_supported: true,
39	src: "",
40}`
41)
42
43func TestCcLibraryBp2Build(t *testing.T) {
44	// b/191166471 disabled in sc-dev
45	t.Skip()
46	testCases := []struct {
47		description                        string
48		moduleTypeUnderTest                string
49		moduleTypeUnderTestFactory         android.ModuleFactory
50		moduleTypeUnderTestBp2BuildMutator func(android.TopDownMutatorContext)
51		bp                                 string
52		expectedBazelTargets               []string
53		filesystem                         map[string]string
54		dir                                string
55		depsMutators                       []android.RegisterMutatorFunc
56	}{
57		{
58			description:                        "cc_library - simple example",
59			moduleTypeUnderTest:                "cc_library",
60			moduleTypeUnderTestFactory:         cc.LibraryFactory,
61			moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
62			filesystem: map[string]string{
63				"android.cpp": "",
64				"darwin.cpp":  "",
65				// Refer to cc.headerExts for the supported header extensions in Soong.
66				"header.h":         "",
67				"header.hh":        "",
68				"header.hpp":       "",
69				"header.hxx":       "",
70				"header.h++":       "",
71				"header.inl":       "",
72				"header.inc":       "",
73				"header.ipp":       "",
74				"header.h.generic": "",
75				"impl.cpp":         "",
76				"linux.cpp":        "",
77				"x86.cpp":          "",
78				"x86_64.cpp":       "",
79				"foo-dir/a.h":      "",
80			},
81			bp: soongCcLibraryPreamble + `
82cc_library_headers { name: "some-headers" }
83cc_library {
84    name: "foo-lib",
85    srcs: ["impl.cpp"],
86    cflags: ["-Wall"],
87    header_libs: ["some-headers"],
88    export_include_dirs: ["foo-dir"],
89    ldflags: ["-Wl,--exclude-libs=bar.a"],
90    arch: {
91        x86: {
92            ldflags: ["-Wl,--exclude-libs=baz.a"],
93            srcs: ["x86.cpp"],
94        },
95        x86_64: {
96            ldflags: ["-Wl,--exclude-libs=qux.a"],
97            srcs: ["x86_64.cpp"],
98        },
99    },
100    target: {
101        android: {
102            srcs: ["android.cpp"],
103        },
104        linux_glibc: {
105            srcs: ["linux.cpp"],
106        },
107        darwin: {
108            srcs: ["darwin.cpp"],
109        },
110    },
111}
112`,
113			expectedBazelTargets: []string{`cc_library(
114    name = "foo-lib",
115    copts = [
116        "-Wall",
117        "-I.",
118    ],
119    deps = [":some-headers"],
120    includes = ["foo-dir"],
121    linkopts = ["-Wl,--exclude-libs=bar.a"] + select({
122        "//build/bazel/platforms/arch:x86": ["-Wl,--exclude-libs=baz.a"],
123        "//build/bazel/platforms/arch:x86_64": ["-Wl,--exclude-libs=qux.a"],
124        "//conditions:default": [],
125    }),
126    srcs = ["impl.cpp"] + select({
127        "//build/bazel/platforms/arch:x86": ["x86.cpp"],
128        "//build/bazel/platforms/arch:x86_64": ["x86_64.cpp"],
129        "//conditions:default": [],
130    }) + select({
131        "//build/bazel/platforms/os:android": ["android.cpp"],
132        "//build/bazel/platforms/os:darwin": ["darwin.cpp"],
133        "//build/bazel/platforms/os:linux": ["linux.cpp"],
134        "//conditions:default": [],
135    }),
136)`},
137		},
138		{
139			description:                        "cc_library - trimmed example of //bionic/linker:ld-android",
140			moduleTypeUnderTest:                "cc_library",
141			moduleTypeUnderTestFactory:         cc.LibraryFactory,
142			moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
143			filesystem: map[string]string{
144				"ld-android.cpp":           "",
145				"linked_list.h":            "",
146				"linker.h":                 "",
147				"linker_block_allocator.h": "",
148				"linker_cfi.h":             "",
149			},
150			bp: soongCcLibraryPreamble + `
151cc_library_headers { name: "libc_headers" }
152cc_library {
153    name: "fake-ld-android",
154    srcs: ["ld_android.cpp"],
155    cflags: [
156        "-Wall",
157        "-Wextra",
158        "-Wunused",
159        "-Werror",
160    ],
161    header_libs: ["libc_headers"],
162    ldflags: [
163        "-Wl,--exclude-libs=libgcc.a",
164        "-Wl,--exclude-libs=libgcc_stripped.a",
165        "-Wl,--exclude-libs=libclang_rt.builtins-arm-android.a",
166        "-Wl,--exclude-libs=libclang_rt.builtins-aarch64-android.a",
167        "-Wl,--exclude-libs=libclang_rt.builtins-i686-android.a",
168        "-Wl,--exclude-libs=libclang_rt.builtins-x86_64-android.a",
169    ],
170    arch: {
171        x86: {
172            ldflags: ["-Wl,--exclude-libs=libgcc_eh.a"],
173        },
174        x86_64: {
175            ldflags: ["-Wl,--exclude-libs=libgcc_eh.a"],
176        },
177    },
178}
179`,
180			expectedBazelTargets: []string{`cc_library(
181    name = "fake-ld-android",
182    copts = [
183        "-Wall",
184        "-Wextra",
185        "-Wunused",
186        "-Werror",
187        "-I.",
188    ],
189    deps = [":libc_headers"],
190    linkopts = [
191        "-Wl,--exclude-libs=libgcc.a",
192        "-Wl,--exclude-libs=libgcc_stripped.a",
193        "-Wl,--exclude-libs=libclang_rt.builtins-arm-android.a",
194        "-Wl,--exclude-libs=libclang_rt.builtins-aarch64-android.a",
195        "-Wl,--exclude-libs=libclang_rt.builtins-i686-android.a",
196        "-Wl,--exclude-libs=libclang_rt.builtins-x86_64-android.a",
197    ] + select({
198        "//build/bazel/platforms/arch:x86": ["-Wl,--exclude-libs=libgcc_eh.a"],
199        "//build/bazel/platforms/arch:x86_64": ["-Wl,--exclude-libs=libgcc_eh.a"],
200        "//conditions:default": [],
201    }),
202    srcs = ["ld_android.cpp"],
203)`},
204		},
205		{
206			description:                        "cc_library exclude_srcs - trimmed example of //external/arm-optimized-routines:libarm-optimized-routines-math",
207			moduleTypeUnderTest:                "cc_library",
208			moduleTypeUnderTestFactory:         cc.LibraryFactory,
209			moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
210			dir:                                "external",
211			filesystem: map[string]string{
212				"external/math/cosf.c":      "",
213				"external/math/erf.c":       "",
214				"external/math/erf_data.c":  "",
215				"external/math/erff.c":      "",
216				"external/math/erff_data.c": "",
217				"external/Android.bp": `
218cc_library {
219    name: "fake-libarm-optimized-routines-math",
220    exclude_srcs: [
221        // Provided by:
222        // bionic/libm/upstream-freebsd/lib/msun/src/s_erf.c
223        // bionic/libm/upstream-freebsd/lib/msun/src/s_erff.c
224        "math/erf.c",
225        "math/erf_data.c",
226        "math/erff.c",
227        "math/erff_data.c",
228    ],
229    srcs: [
230        "math/*.c",
231    ],
232    // arch-specific settings
233    arch: {
234        arm64: {
235            cflags: [
236                "-DHAVE_FAST_FMA=1",
237            ],
238        },
239    },
240    bazel_module: { bp2build_available: true },
241}
242`,
243			},
244			bp: soongCcLibraryPreamble,
245			expectedBazelTargets: []string{`cc_library(
246    name = "fake-libarm-optimized-routines-math",
247    copts = ["-Iexternal"] + select({
248        "//build/bazel/platforms/arch:arm64": ["-DHAVE_FAST_FMA=1"],
249        "//conditions:default": [],
250    }),
251    srcs = ["math/cosf.c"],
252)`},
253		},
254		{
255			description:                        "cc_library shared/static props",
256			moduleTypeUnderTest:                "cc_library",
257			moduleTypeUnderTestFactory:         cc.LibraryFactory,
258			moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
259			depsMutators:                       []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build},
260			dir:                                "foo/bar",
261			filesystem: map[string]string{
262				"foo/bar/both.cpp":       "",
263				"foo/bar/sharedonly.cpp": "",
264				"foo/bar/staticonly.cpp": "",
265				"foo/bar/Android.bp": `
266cc_library {
267    name: "a",
268    srcs: ["both.cpp"],
269    cflags: ["bothflag"],
270    shared_libs: ["shared_dep_for_both"],
271    static_libs: ["static_dep_for_both"],
272    whole_static_libs: ["whole_static_lib_for_both"],
273    static: {
274        srcs: ["staticonly.cpp"],
275        cflags: ["staticflag"],
276        shared_libs: ["shared_dep_for_static"],
277        static_libs: ["static_dep_for_static"],
278        whole_static_libs: ["whole_static_lib_for_static"],
279    },
280    shared: {
281        srcs: ["sharedonly.cpp"],
282        cflags: ["sharedflag"],
283        shared_libs: ["shared_dep_for_shared"],
284        static_libs: ["static_dep_for_shared"],
285        whole_static_libs: ["whole_static_lib_for_shared"],
286    },
287    bazel_module: { bp2build_available: true },
288}
289
290cc_library_static { name: "static_dep_for_shared" }
291
292cc_library_static { name: "static_dep_for_static" }
293
294cc_library_static { name: "static_dep_for_both" }
295
296cc_library_static { name: "whole_static_lib_for_shared" }
297
298cc_library_static { name: "whole_static_lib_for_static" }
299
300cc_library_static { name: "whole_static_lib_for_both" }
301
302cc_library { name: "shared_dep_for_shared" }
303
304cc_library { name: "shared_dep_for_static" }
305
306cc_library { name: "shared_dep_for_both" }
307`,
308			},
309			bp: soongCcLibraryPreamble,
310			expectedBazelTargets: []string{`cc_library(
311    name = "a",
312    copts = [
313        "bothflag",
314        "-Ifoo/bar",
315    ],
316    deps = [":static_dep_for_both"],
317    dynamic_deps = [":shared_dep_for_both"],
318    dynamic_deps_for_shared = [":shared_dep_for_shared"],
319    dynamic_deps_for_static = [":shared_dep_for_static"],
320    shared_copts = ["sharedflag"],
321    shared_srcs = ["sharedonly.cpp"],
322    srcs = ["both.cpp"],
323    static_copts = ["staticflag"],
324    static_deps_for_shared = [":static_dep_for_shared"],
325    static_deps_for_static = [":static_dep_for_static"],
326    static_srcs = ["staticonly.cpp"],
327    whole_archive_deps = [":whole_static_lib_for_both"],
328    whole_archive_deps_for_shared = [":whole_static_lib_for_shared"],
329    whole_archive_deps_for_static = [":whole_static_lib_for_static"],
330)`},
331		},
332		{
333			description:                        "cc_library non-configured version script",
334			moduleTypeUnderTest:                "cc_library",
335			moduleTypeUnderTestFactory:         cc.LibraryFactory,
336			moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
337			depsMutators:                       []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build},
338			dir:                                "foo/bar",
339			filesystem: map[string]string{
340				"foo/bar/Android.bp": `
341cc_library {
342    name: "a",
343    srcs: ["a.cpp"],
344    version_script: "v.map",
345    bazel_module: { bp2build_available: true },
346}
347`,
348			},
349			bp: soongCcLibraryPreamble,
350			expectedBazelTargets: []string{`cc_library(
351    name = "a",
352    copts = ["-Ifoo/bar"],
353    srcs = ["a.cpp"],
354    version_script = "v.map",
355)`},
356		},
357		{
358			description:                        "cc_library configured version script",
359			moduleTypeUnderTest:                "cc_library",
360			moduleTypeUnderTestFactory:         cc.LibraryFactory,
361			moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
362			depsMutators:                       []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build},
363			dir:                                "foo/bar",
364			filesystem: map[string]string{
365				"foo/bar/Android.bp": `
366		cc_library {
367		   name: "a",
368		   srcs: ["a.cpp"],
369		   arch: {
370		     arm: {
371		       version_script: "arm.map",
372		     },
373		     arm64: {
374		       version_script: "arm64.map",
375		     },
376		   },
377
378		   bazel_module: { bp2build_available: true },
379		}
380		`,
381			},
382			bp: soongCcLibraryPreamble,
383			expectedBazelTargets: []string{`cc_library(
384    name = "a",
385    copts = ["-Ifoo/bar"],
386    srcs = ["a.cpp"],
387    version_script = select({
388        "//build/bazel/platforms/arch:arm": "arm.map",
389        "//build/bazel/platforms/arch:arm64": "arm64.map",
390        "//conditions:default": None,
391    }),
392)`},
393		},
394		{
395			description:                        "cc_library shared_libs",
396			moduleTypeUnderTest:                "cc_library",
397			moduleTypeUnderTestFactory:         cc.LibraryFactory,
398			moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
399			depsMutators:                       []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build},
400			dir:                                "foo/bar",
401			filesystem: map[string]string{
402				"foo/bar/Android.bp": `
403cc_library {
404    name: "mylib",
405    bazel_module: { bp2build_available: true },
406}
407
408cc_library {
409    name: "a",
410    shared_libs: ["mylib",],
411    bazel_module: { bp2build_available: true },
412}
413`,
414			},
415			bp: soongCcLibraryPreamble,
416			expectedBazelTargets: []string{`cc_library(
417    name = "a",
418    copts = ["-Ifoo/bar"],
419    dynamic_deps = [":mylib"],
420)`, `cc_library(
421    name = "mylib",
422    copts = ["-Ifoo/bar"],
423)`},
424		},
425		{
426			description:                        "cc_library pack_relocations test",
427			moduleTypeUnderTest:                "cc_library",
428			moduleTypeUnderTestFactory:         cc.LibraryFactory,
429			moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
430			depsMutators:                       []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build},
431			dir:                                "foo/bar",
432			filesystem: map[string]string{
433				"foo/bar/Android.bp": `
434cc_library {
435    name: "a",
436    srcs: ["a.cpp"],
437    pack_relocations: false,
438    bazel_module: { bp2build_available: true },
439}
440
441cc_library {
442    name: "b",
443    srcs: ["b.cpp"],
444    arch: {
445        x86_64: {
446		pack_relocations: false,
447	},
448    },
449    bazel_module: { bp2build_available: true },
450}
451
452cc_library {
453    name: "c",
454    srcs: ["c.cpp"],
455    target: {
456        darwin: {
457		pack_relocations: false,
458	},
459    },
460    bazel_module: { bp2build_available: true },
461}`,
462			},
463			bp: soongCcLibraryPreamble,
464			expectedBazelTargets: []string{`cc_library(
465    name = "a",
466    copts = ["-Ifoo/bar"],
467    linkopts = ["-Wl,--pack-dyn-relocs=none"],
468    srcs = ["a.cpp"],
469)`, `cc_library(
470    name = "b",
471    copts = ["-Ifoo/bar"],
472    linkopts = select({
473        "//build/bazel/platforms/arch:x86_64": ["-Wl,--pack-dyn-relocs=none"],
474        "//conditions:default": [],
475    }),
476    srcs = ["b.cpp"],
477)`, `cc_library(
478    name = "c",
479    copts = ["-Ifoo/bar"],
480    linkopts = select({
481        "//build/bazel/platforms/os:darwin": ["-Wl,--pack-dyn-relocs=none"],
482        "//conditions:default": [],
483    }),
484    srcs = ["c.cpp"],
485)`},
486		},
487		{
488			description:                        "cc_library spaces in copts",
489			moduleTypeUnderTest:                "cc_library",
490			moduleTypeUnderTestFactory:         cc.LibraryFactory,
491			moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
492			depsMutators:                       []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build},
493			dir:                                "foo/bar",
494			filesystem: map[string]string{
495				"foo/bar/Android.bp": `
496cc_library {
497    name: "a",
498    cflags: ["-include header.h",],
499    bazel_module: { bp2build_available: true },
500}
501`,
502			},
503			bp: soongCcLibraryPreamble,
504			expectedBazelTargets: []string{`cc_library(
505    name = "a",
506    copts = [
507        "-include",
508        "header.h",
509        "-Ifoo/bar",
510    ],
511)`},
512		},
513		{
514			description:                        "cc_library cppflags goes into copts",
515			moduleTypeUnderTest:                "cc_library",
516			moduleTypeUnderTestFactory:         cc.LibraryFactory,
517			moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
518			depsMutators:                       []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build},
519			dir:                                "foo/bar",
520			filesystem: map[string]string{
521				"foo/bar/Android.bp": `cc_library {
522    name: "a",
523    srcs: ["a.cpp"],
524    cflags: [
525		"-Wall",
526	],
527    cppflags: [
528        "-fsigned-char",
529        "-pedantic",
530	],
531    arch: {
532        arm64: {
533            cppflags: ["-DARM64=1"],
534		},
535	},
536    target: {
537        android: {
538            cppflags: ["-DANDROID=1"],
539		},
540	},
541    bazel_module: { bp2build_available: true  },
542}
543`,
544			},
545			bp: soongCcLibraryPreamble,
546			expectedBazelTargets: []string{`cc_library(
547    name = "a",
548    copts = [
549        "-Wall",
550        "-fsigned-char",
551        "-pedantic",
552        "-Ifoo/bar",
553    ] + select({
554        "//build/bazel/platforms/arch:arm64": ["-DARM64=1"],
555        "//conditions:default": [],
556    }) + select({
557        "//build/bazel/platforms/os:android": ["-DANDROID=1"],
558        "//conditions:default": [],
559    }),
560    srcs = ["a.cpp"],
561)`},
562		},
563	}
564
565	dir := "."
566	for _, testCase := range testCases {
567		filesystem := make(map[string][]byte)
568		toParse := []string{
569			"Android.bp",
570		}
571		for f, content := range testCase.filesystem {
572			if strings.HasSuffix(f, "Android.bp") {
573				toParse = append(toParse, f)
574			}
575			filesystem[f] = []byte(content)
576		}
577		config := android.TestConfig(buildDir, nil, testCase.bp, filesystem)
578		ctx := android.NewTestContext(config)
579
580		cc.RegisterCCBuildComponents(ctx)
581		ctx.RegisterModuleType("cc_library_static", cc.LibraryStaticFactory)
582		ctx.RegisterModuleType("toolchain_library", cc.ToolchainLibraryFactory)
583		ctx.RegisterModuleType("cc_library_headers", cc.LibraryHeaderFactory)
584		ctx.RegisterModuleType(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestFactory)
585		ctx.RegisterBp2BuildMutator(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestBp2BuildMutator)
586		ctx.RegisterBp2BuildConfig(bp2buildConfig) // TODO(jingwen): make this the default for all tests
587		for _, m := range testCase.depsMutators {
588			ctx.DepsBp2BuildMutators(m)
589		}
590		ctx.RegisterForBazelConversion()
591
592		_, errs := ctx.ParseFileList(dir, toParse)
593		if Errored(t, testCase.description, errs) {
594			continue
595		}
596		_, errs = ctx.ResolveDependencies(config)
597		if Errored(t, testCase.description, errs) {
598			continue
599		}
600
601		checkDir := dir
602		if testCase.dir != "" {
603			checkDir = testCase.dir
604		}
605		codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
606		bazelTargets := generateBazelTargetsForDir(codegenCtx, checkDir)
607		if actualCount, expectedCount := len(bazelTargets), len(testCase.expectedBazelTargets); actualCount != expectedCount {
608			t.Errorf("%s: Expected %d bazel target, got %d", testCase.description, expectedCount, actualCount)
609		} else {
610			for i, target := range bazelTargets {
611				if w, g := testCase.expectedBazelTargets[i], target.content; w != g {
612					t.Errorf(
613						"%s: Expected generated Bazel target to be '%s', got '%s'",
614						testCase.description,
615						w,
616						g,
617					)
618				}
619			}
620		}
621	}
622}
623