| // 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") | 
 | } |