xref: /aosp_15_r20/build/soong/cc/sabi.go (revision 333d2b3687b3a337dbcca9d65000bca186795e39)
1// Copyright 2017 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 cc
16
17import (
18	"sync"
19
20	"android/soong/android"
21	"android/soong/cc/config"
22)
23
24var (
25	lsdumpPathsLock sync.Mutex
26	lsdumpKey       = android.NewOnceKey("lsdump")
27)
28
29func lsdumpPaths(config android.Config) *[]string {
30	return config.Once(lsdumpKey, func() any {
31		return &[]string{}
32	}).(*[]string)
33}
34
35type lsdumpTag string
36
37const (
38	apexLsdumpTag     lsdumpTag = "APEX"
39	llndkLsdumpTag    lsdumpTag = "LLNDK"
40	platformLsdumpTag lsdumpTag = "PLATFORM"
41	productLsdumpTag  lsdumpTag = "PRODUCT"
42	vendorLsdumpTag   lsdumpTag = "VENDOR"
43)
44
45// Return the prebuilt ABI dump directory for a tag; an empty string for an opt-in dump.
46func (tag *lsdumpTag) dirName() string {
47	switch *tag {
48	case apexLsdumpTag:
49		return "platform"
50	case llndkLsdumpTag:
51		return "vndk"
52	default:
53		return ""
54	}
55}
56
57// Properties for ABI compatibility checker in Android.bp.
58type headerAbiCheckerProperties struct {
59	// Enable ABI checks (even if this is not an LLNDK/VNDK lib)
60	Enabled *bool
61
62	// Path to a symbol file that specifies the symbols to be included in the generated
63	// ABI dump file
64	Symbol_file *string `android:"path"`
65
66	// Symbol versions that should be ignored from the symbol file
67	Exclude_symbol_versions []string
68
69	// Symbol tags that should be ignored from the symbol file
70	Exclude_symbol_tags []string
71
72	// Run checks on all APIs (in addition to the ones referred by
73	// one of exported ELF symbols.)
74	Check_all_apis *bool
75
76	// Extra flags passed to header-abi-diff
77	Diff_flags []string
78
79	// Opt-in reference dump directories
80	Ref_dump_dirs []string
81}
82
83func (props *headerAbiCheckerProperties) enabled() bool {
84	return Bool(props.Enabled)
85}
86
87func (props *headerAbiCheckerProperties) explicitlyDisabled() bool {
88	return !BoolDefault(props.Enabled, true)
89}
90
91type SAbiProperties struct {
92	// Whether ABI dump should be created for this module.
93	// Set by `sabiTransitionMutator` if this module is a shared library that needs ABI check,
94	// or a static library that is depended on by an ABI checked library.
95	ShouldCreateSourceAbiDump bool `blueprint:"mutated"`
96
97	// Include directories that may contain ABI information exported by a library.
98	// These directories are passed to the header-abi-dumper.
99	ReexportedIncludes       []string `blueprint:"mutated"`
100	ReexportedSystemIncludes []string `blueprint:"mutated"`
101}
102
103type sabi struct {
104	Properties SAbiProperties
105}
106
107func (sabi *sabi) props() []interface{} {
108	return []interface{}{&sabi.Properties}
109}
110
111func (sabi *sabi) flags(ctx ModuleContext, flags Flags) Flags {
112	// Filter out flags which libTooling don't understand.
113	// This is here for legacy reasons and future-proof, in case the version of libTooling and clang
114	// diverge.
115	flags.Local.ToolingCFlags = config.ClangLibToolingFilterUnknownCflags(flags.Local.CFlags)
116	flags.Global.ToolingCFlags = config.ClangLibToolingFilterUnknownCflags(flags.Global.CFlags)
117	flags.Local.ToolingCppFlags = config.ClangLibToolingFilterUnknownCflags(flags.Local.CppFlags)
118	flags.Global.ToolingCppFlags = config.ClangLibToolingFilterUnknownCflags(flags.Global.CppFlags)
119	return flags
120}
121
122// Returns true if ABI dump should be created for this library, either because library is ABI
123// checked or is depended on by an ABI checked library.
124// Could be called as a nil receiver.
125func (sabi *sabi) shouldCreateSourceAbiDump() bool {
126	return sabi != nil && sabi.Properties.ShouldCreateSourceAbiDump
127}
128
129// Returns a slice of strings that represent the ABI dumps generated for this module.
130func classifySourceAbiDump(m *Module) []lsdumpTag {
131	result := []lsdumpTag{}
132	headerAbiChecker := m.library.getHeaderAbiCheckerProperties(m)
133	if headerAbiChecker.explicitlyDisabled() {
134		return result
135	}
136	if !m.InProduct() && !m.InVendor() {
137		if m.isImplementationForLLNDKPublic() {
138			result = append(result, llndkLsdumpTag)
139		}
140		if m.library.hasStubsVariants() {
141			result = append(result, apexLsdumpTag)
142		}
143		if headerAbiChecker.enabled() {
144			result = append(result, platformLsdumpTag)
145		}
146	} else if headerAbiChecker.enabled() {
147		if m.InProduct() {
148			result = append(result, productLsdumpTag)
149		}
150		if m.InVendor() {
151			result = append(result, vendorLsdumpTag)
152		}
153	}
154	return result
155}
156
157type shouldCreateAbiDumpContext interface {
158	android.ModuleProviderContext
159	Module() android.Module
160	Config() android.Config
161}
162
163var _ shouldCreateAbiDumpContext = android.ModuleContext(nil)
164var _ shouldCreateAbiDumpContext = android.OutgoingTransitionContext(nil)
165
166// Called from sabiTransitionMutator to check whether ABI dumps should be created for this module.
167// ctx should be wrapping a native library type module.
168func shouldCreateSourceAbiDumpForLibrary(ctx shouldCreateAbiDumpContext) bool {
169	m, ok := ctx.Module().(*Module)
170	if !ok {
171		return false
172	}
173
174	// Only generate ABI dump for device modules.
175	if !m.Device() {
176		return false
177	}
178
179	// Only create ABI dump for native library module types.
180	if m.library == nil {
181		return false
182	}
183
184	// Don't create ABI dump for static libraries
185	// The sabi variant will be propagated to dependencies of ABI checked libraries.
186	if m.library.static() {
187		return false
188	}
189
190	// Module is shared library type.
191
192	// Don't check uninstallable modules.
193	if m.IsHideFromMake() {
194		return false
195	}
196
197	// Don't check ramdisk or recovery variants. Only check core, vendor or product variants.
198	if m.InRamdisk() || m.InVendorRamdisk() || m.InRecovery() {
199		return false
200	}
201
202	// Don't create ABI dump for prebuilts.
203	if m.Prebuilt() != nil || m.IsSnapshotPrebuilt() {
204		return false
205	}
206
207	// Coverage builds have extra symbols.
208	if m.isCoverageVariant() {
209		return false
210	}
211
212	// Some sanitizer variants may have different ABI.
213	if m.sanitize != nil && !m.sanitize.isVariantOnProductionDevice() {
214		return false
215	}
216
217	// Don't create ABI dump for stubs.
218	if m.isNDKStubLibrary() || m.IsLlndk() || m.IsStubs() {
219		return false
220	}
221
222	apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider)
223	if apexInfo.IsForPlatform() {
224		// Bionic libraries that are installed to the bootstrap directory are not ABI checked.
225		// Only the runtime APEX variants, which are the implementation libraries of bionic NDK stubs,
226		// are checked.
227		if InstallToBootstrap(m.BaseModuleName(), ctx.Config()) {
228			return false
229		}
230	} else {
231		// Don't create ABI dump if this library is for APEX but isn't exported.
232		if !m.HasStubsVariants() {
233			return false
234		}
235	}
236	return len(classifySourceAbiDump(m)) > 0
237}
238
239// Mark the direct and transitive dependencies of libraries that need ABI check, so that ABI dumps
240// of their dependencies would be generated.
241type sabiTransitionMutator struct{}
242
243func (s *sabiTransitionMutator) Split(ctx android.BaseModuleContext) []string {
244	return []string{""}
245}
246
247func (s *sabiTransitionMutator) OutgoingTransition(ctx android.OutgoingTransitionContext, sourceVariation string) string {
248	// Escape hatch to not check any ABI dump.
249	if ctx.Config().IsEnvTrue("SKIP_ABI_CHECKS") {
250		return ""
251	}
252
253	// Only create ABI dump for native shared libraries and their static library dependencies.
254	if m, ok := ctx.Module().(*Module); ok && m.sabi != nil {
255		if shouldCreateSourceAbiDumpForLibrary(ctx) {
256			if IsStaticDepTag(ctx.DepTag()) || ctx.DepTag() == reuseObjTag {
257				return "sabi"
258			}
259		} else if sourceVariation == "sabi" {
260			if IsWholeStaticLib(ctx.DepTag()) || ctx.DepTag() == reuseObjTag {
261				return "sabi"
262			}
263		}
264	}
265
266	return ""
267}
268
269func (s *sabiTransitionMutator) IncomingTransition(ctx android.IncomingTransitionContext, incomingVariation string) string {
270	if incomingVariation == "" {
271		return ""
272	}
273
274	if incomingVariation == "sabi" {
275		if m, ok := ctx.Module().(*Module); ok && m.sabi != nil {
276			return "sabi"
277		}
278	}
279
280	return ""
281}
282
283func (s *sabiTransitionMutator) Mutate(ctx android.BottomUpMutatorContext, variation string) {
284	if m, ok := ctx.Module().(*Module); ok && m.sabi != nil {
285		if variation == "sabi" {
286			m.sabi.Properties.ShouldCreateSourceAbiDump = true
287			m.HideFromMake()
288			m.Properties.PreventInstall = true
289		} else if shouldCreateSourceAbiDumpForLibrary(ctx) {
290			// Escape hatch to not check any ABI dump.
291			if !ctx.Config().IsEnvTrue("SKIP_ABI_CHECKS") {
292				m.sabi.Properties.ShouldCreateSourceAbiDump = true
293			}
294		}
295	}
296}
297
298// Add an entry to the global list of lsdump. The list is exported to a Make variable by
299// `cc.makeVarsProvider`.
300func addLsdumpPath(config android.Config, lsdumpPath string) {
301	lsdumpPaths := lsdumpPaths(config)
302	lsdumpPathsLock.Lock()
303	defer lsdumpPathsLock.Unlock()
304	*lsdumpPaths = append(*lsdumpPaths, lsdumpPath)
305}
306