|  | // Copyright 2021 Google Inc. All rights reserved. | 
|  | // | 
|  | // Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | // you may not use this file except in compliance with the License. | 
|  | // You may obtain a copy of the License at | 
|  | // | 
|  | //     http://www.apache.org/licenses/LICENSE-2.0 | 
|  | // | 
|  | // Unless required by applicable law or agreed to in writing, software | 
|  | // distributed under the License is distributed on an "AS IS" BASIS, | 
|  | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | // See the License for the specific language governing permissions and | 
|  | // limitations under the License. | 
|  |  | 
|  | package cc | 
|  |  | 
|  | import ( | 
|  | "strings" | 
|  | "testing" | 
|  |  | 
|  | "android/soong/android" | 
|  |  | 
|  | "github.com/google/blueprint" | 
|  | ) | 
|  |  | 
|  | var LTOPreparer = android.GroupFixturePreparers( | 
|  | prepareForCcTest, | 
|  | ) | 
|  |  | 
|  | func hasDep(result *android.TestResult, m android.Module, wantDep android.Module) bool { | 
|  | var found bool | 
|  | result.VisitDirectDeps(m, func(dep blueprint.Module) { | 
|  | if dep == wantDep { | 
|  | found = true | 
|  | } | 
|  | }) | 
|  | return found | 
|  | } | 
|  |  | 
|  | func TestThinLtoDeps(t *testing.T) { | 
|  | t.Parallel() | 
|  | bp := ` | 
|  | cc_library_shared { | 
|  | name: "lto_enabled", | 
|  | srcs: ["src.c"], | 
|  | static_libs: ["foo", "lib_never_lto"], | 
|  | shared_libs: ["bar"], | 
|  | } | 
|  | cc_library_static { | 
|  | name: "foo", | 
|  | static_libs: ["baz"], | 
|  | } | 
|  | cc_library_shared { | 
|  | name: "bar", | 
|  | static_libs: ["qux"], | 
|  | } | 
|  | cc_library_static { | 
|  | name: "baz", | 
|  | } | 
|  | cc_library_static { | 
|  | name: "qux", | 
|  | } | 
|  | cc_library_static { | 
|  | name: "lib_never_lto", | 
|  | lto: { | 
|  | never: true, | 
|  | }, | 
|  | } | 
|  | ` | 
|  |  | 
|  | result := LTOPreparer.RunTestWithBp(t, bp) | 
|  |  | 
|  | libLto := result.ModuleForTests("lto_enabled", "android_arm64_armv8-a_shared").Module() | 
|  |  | 
|  | libFoo := result.ModuleForTests("foo", "android_arm64_armv8-a_static").Module() | 
|  | if !hasDep(result, libLto, libFoo) { | 
|  | t.Errorf("'lto_enabled' missing dependency on the default variant of 'foo'") | 
|  | } | 
|  |  | 
|  | libBaz := result.ModuleForTests("baz", "android_arm64_armv8-a_static").Module() | 
|  | if !hasDep(result, libFoo, libBaz) { | 
|  | t.Errorf("'foo' missing dependency on the default variant of transitive dep 'baz'") | 
|  | } | 
|  |  | 
|  | libNeverLto := result.ModuleForTests("lib_never_lto", "android_arm64_armv8-a_static").Module() | 
|  | if !hasDep(result, libLto, libNeverLto) { | 
|  | t.Errorf("'lto_enabled' missing dependency on the default variant of 'lib_never_lto'") | 
|  | } | 
|  |  | 
|  | libBar := result.ModuleForTests("bar", "android_arm64_armv8-a_shared").Module() | 
|  | if !hasDep(result, libLto, libBar) { | 
|  | t.Errorf("'lto_enabled' missing dependency on the default variant of 'bar'") | 
|  | } | 
|  |  | 
|  | barVariants := result.ModuleVariantsForTests("bar") | 
|  | for _, v := range barVariants { | 
|  | if strings.Contains(v, "lto-none") { | 
|  | t.Errorf("Expected variants for 'bar' to not contain 'lto-none', but found %q", v) | 
|  | } | 
|  | } | 
|  | quxVariants := result.ModuleVariantsForTests("qux") | 
|  | for _, v := range quxVariants { | 
|  | if strings.Contains(v, "lto-none") { | 
|  | t.Errorf("Expected variants for 'qux' to not contain 'lto-none', but found %q", v) | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | func TestThinLtoOnlyOnStaticDep(t *testing.T) { | 
|  | t.Parallel() | 
|  | bp := ` | 
|  | cc_library_shared { | 
|  | name: "root", | 
|  | srcs: ["src.c"], | 
|  | static_libs: ["foo"], | 
|  | } | 
|  | cc_library_shared { | 
|  | name: "root_no_lto", | 
|  | srcs: ["src.c"], | 
|  | static_libs: ["foo"], | 
|  | lto: { | 
|  | never: true, | 
|  | } | 
|  | } | 
|  | cc_library_static { | 
|  | name: "foo", | 
|  | srcs: ["foo.c"], | 
|  | static_libs: ["baz"], | 
|  | lto: { | 
|  | thin: true, | 
|  | } | 
|  | } | 
|  | cc_library_static { | 
|  | name: "baz", | 
|  | srcs: ["baz.c"], | 
|  | } | 
|  | ` | 
|  |  | 
|  | result := LTOPreparer.RunTestWithBp(t, bp) | 
|  |  | 
|  | libRoot := result.ModuleForTests("root", "android_arm64_armv8-a_shared").Module() | 
|  | libRootLtoNever := result.ModuleForTests("root_no_lto", "android_arm64_armv8-a_shared").Module() | 
|  |  | 
|  | libFoo := result.ModuleForTests("foo", "android_arm64_armv8-a_static") | 
|  | if !hasDep(result, libRoot, libFoo.Module()) { | 
|  | t.Errorf("'root' missing dependency on the default variant of 'foo'") | 
|  | } | 
|  |  | 
|  | libFooNoLto := result.ModuleForTests("foo", "android_arm64_armv8-a_static_lto-none") | 
|  | if !hasDep(result, libRootLtoNever, libFooNoLto.Module()) { | 
|  | t.Errorf("'root_no_lto' missing dependency on the lto_none variant of 'foo'") | 
|  | } | 
|  |  | 
|  | libFooCFlags := libFoo.Rule("cc").Args["cFlags"] | 
|  | if w := "-flto=thin -fsplit-lto-unit"; !strings.Contains(libFooCFlags, w) { | 
|  | t.Errorf("'foo' expected to have flags %q, but got %q", w, libFooCFlags) | 
|  | } | 
|  |  | 
|  | libBaz := result.ModuleForTests("baz", "android_arm64_armv8-a_static") | 
|  | if !hasDep(result, libFoo.Module(), libBaz.Module()) { | 
|  | t.Errorf("'foo' missing dependency on the default variant of transitive dep 'baz'") | 
|  | } | 
|  |  | 
|  | libBazCFlags := libFoo.Rule("cc").Args["cFlags"] | 
|  | if w := "-flto=thin -fsplit-lto-unit"; !strings.Contains(libBazCFlags, w) { | 
|  | t.Errorf("'baz' expected to have flags %q, but got %q", w, libFooCFlags) | 
|  | } | 
|  | } | 
|  |  | 
|  | func TestLtoDisabledButEnabledForArch(t *testing.T) { | 
|  | t.Parallel() | 
|  | bp := ` | 
|  | cc_library { | 
|  | name: "libfoo", | 
|  | srcs: ["foo.c"], | 
|  | lto: { | 
|  | never: true, | 
|  | }, | 
|  | target: { | 
|  | android_arm: { | 
|  | lto: { | 
|  | never: false, | 
|  | thin: true, | 
|  | }, | 
|  | }, | 
|  | }, | 
|  | }` | 
|  | result := LTOPreparer.RunTestWithBp(t, bp) | 
|  |  | 
|  | libFooWithLto := result.ModuleForTests("libfoo", "android_arm_armv7-a-neon_shared").Rule("ld") | 
|  | libFooWithoutLto := result.ModuleForTests("libfoo", "android_arm64_armv8-a_shared").Rule("ld") | 
|  |  | 
|  | android.AssertStringDoesContain(t, "missing flag for LTO in variant that expects it", | 
|  | libFooWithLto.Args["ldFlags"], "-flto=thin") | 
|  | android.AssertStringDoesNotContain(t, "got flag for LTO in variant that doesn't expect it", | 
|  | libFooWithoutLto.Args["ldFlags"], "-flto=thin") | 
|  | } | 
|  |  | 
|  | func TestLtoDoesNotPropagateToRuntimeLibs(t *testing.T) { | 
|  | t.Parallel() | 
|  | bp := ` | 
|  | cc_library { | 
|  | name: "runtime_libbar", | 
|  | srcs: ["bar.c"], | 
|  | } | 
|  |  | 
|  | cc_library { | 
|  | name: "libfoo", | 
|  | srcs: ["foo.c"], | 
|  | runtime_libs: ["runtime_libbar"], | 
|  | lto: { | 
|  | thin: true, | 
|  | }, | 
|  | }` | 
|  |  | 
|  | result := LTOPreparer.RunTestWithBp(t, bp) | 
|  |  | 
|  | libFoo := result.ModuleForTests("libfoo", "android_arm_armv7-a-neon_shared").Rule("ld") | 
|  | libBar := result.ModuleForTests("runtime_libbar", "android_arm_armv7-a-neon_shared").Rule("ld") | 
|  |  | 
|  | android.AssertStringDoesContain(t, "missing flag for LTO in LTO enabled library", | 
|  | libFoo.Args["ldFlags"], "-flto=thin") | 
|  | android.AssertStringDoesNotContain(t, "got flag for LTO in runtime_lib", | 
|  | libBar.Args["ldFlags"], "-flto=thin") | 
|  | } |