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 cc
16
17import (
18	"regexp"
19	"strings"
20
21	"android/soong/android"
22	"android/soong/multitree"
23
24	"github.com/google/blueprint/proptools"
25)
26
27var (
28	ndkVariantRegex  = regexp.MustCompile("ndk\\.([a-zA-Z0-9]+)")
29	stubVariantRegex = regexp.MustCompile("apex\\.([a-zA-Z0-9]+)")
30)
31
32func init() {
33	RegisterLibraryStubBuildComponents(android.InitRegistrationContext)
34}
35
36func RegisterLibraryStubBuildComponents(ctx android.RegistrationContext) {
37	ctx.RegisterModuleType("cc_api_library", CcApiLibraryFactory)
38	ctx.RegisterModuleType("cc_api_headers", CcApiHeadersFactory)
39	ctx.RegisterModuleType("cc_api_variant", CcApiVariantFactory)
40}
41
42func updateImportedLibraryDependency(ctx android.BottomUpMutatorContext) {
43	m, ok := ctx.Module().(*Module)
44	if !ok {
45		return
46	}
47
48	apiLibrary, ok := m.linker.(*apiLibraryDecorator)
49	if !ok {
50		return
51	}
52
53	if m.InVendorOrProduct() && apiLibrary.hasLLNDKStubs() {
54		// Add LLNDK variant dependency
55		if inList("llndk", apiLibrary.properties.Variants) {
56			variantName := BuildApiVariantName(m.BaseModuleName(), "llndk", "")
57			ctx.AddDependency(m, nil, variantName)
58		}
59	} else if m.IsSdkVariant() {
60		// Add NDK variant dependencies
61		targetVariant := "ndk." + m.StubsVersion()
62		if inList(targetVariant, apiLibrary.properties.Variants) {
63			variantName := BuildApiVariantName(m.BaseModuleName(), targetVariant, "")
64			ctx.AddDependency(m, nil, variantName)
65		}
66	} else if m.IsStubs() {
67		targetVariant := "apex." + m.StubsVersion()
68		if inList(targetVariant, apiLibrary.properties.Variants) {
69			variantName := BuildApiVariantName(m.BaseModuleName(), targetVariant, "")
70			ctx.AddDependency(m, nil, variantName)
71		}
72	}
73}
74
75// 'cc_api_library' is a module type which is from the exported API surface
76// with C shared library type. The module will replace original module, and
77// offer a link to the module that generates shared library object from the
78// map file.
79type apiLibraryProperties struct {
80	Src      *string `android:"arch_variant"`
81	Variants []string
82}
83
84type apiLibraryDecorator struct {
85	*libraryDecorator
86	properties apiLibraryProperties
87}
88
89func CcApiLibraryFactory() android.Module {
90	module, decorator := NewLibrary(android.DeviceSupported)
91	apiLibraryDecorator := &apiLibraryDecorator{
92		libraryDecorator: decorator,
93	}
94	apiLibraryDecorator.BuildOnlyShared()
95
96	module.stl = nil
97	module.sanitize = nil
98	decorator.disableStripping()
99
100	module.compiler = nil
101	module.linker = apiLibraryDecorator
102	module.installer = nil
103	module.library = apiLibraryDecorator
104	module.AddProperties(&module.Properties, &apiLibraryDecorator.properties)
105
106	// Prevent default system libs (libc, libm, and libdl) from being linked
107	if apiLibraryDecorator.baseLinker.Properties.System_shared_libs == nil {
108		apiLibraryDecorator.baseLinker.Properties.System_shared_libs = []string{}
109	}
110
111	apiLibraryDecorator.baseLinker.Properties.No_libcrt = BoolPtr(true)
112	apiLibraryDecorator.baseLinker.Properties.Nocrt = BoolPtr(true)
113
114	module.Init()
115
116	return module
117}
118
119func (d *apiLibraryDecorator) Name(basename string) string {
120	return basename + multitree.GetApiImportSuffix()
121}
122
123// Export include dirs without checking for existence.
124// The directories are not guaranteed to exist during Soong analysis.
125func (d *apiLibraryDecorator) exportIncludes(ctx ModuleContext) {
126	exporterProps := d.flagExporter.Properties
127	for _, dir := range exporterProps.Export_include_dirs.GetOrDefault(ctx, nil) {
128		d.dirs = append(d.dirs, android.MaybeExistentPathForSource(ctx, ctx.ModuleDir(), dir))
129	}
130	// system headers
131	for _, dir := range exporterProps.Export_system_include_dirs {
132		d.systemDirs = append(d.systemDirs, android.MaybeExistentPathForSource(ctx, ctx.ModuleDir(), dir))
133	}
134}
135
136func (d *apiLibraryDecorator) linkerInit(ctx BaseModuleContext) {
137	d.baseLinker.linkerInit(ctx)
138
139	if d.hasNDKStubs() {
140		// Set SDK version of module as current
141		ctx.Module().(*Module).Properties.Sdk_version = StringPtr("current")
142
143		// Add NDK stub as NDK known libs
144		name := ctx.ModuleName()
145
146		ndkKnownLibsLock.Lock()
147		ndkKnownLibs := getNDKKnownLibs(ctx.Config())
148		if !inList(name, *ndkKnownLibs) {
149			*ndkKnownLibs = append(*ndkKnownLibs, name)
150		}
151		ndkKnownLibsLock.Unlock()
152	}
153}
154
155func (d *apiLibraryDecorator) link(ctx ModuleContext, flags Flags, deps PathDeps, objects Objects) android.Path {
156	m, _ := ctx.Module().(*Module)
157
158	var in android.Path
159
160	// src might not exist during the beginning of soong analysis in Multi-tree
161	if src := String(d.properties.Src); src != "" {
162		in = android.MaybeExistentPathForSource(ctx, ctx.ModuleDir(), src)
163	}
164
165	libName := m.BaseModuleName() + multitree.GetApiImportSuffix()
166
167	load_cc_variant := func(apiVariantModule string) {
168		var mod android.Module
169
170		ctx.VisitDirectDeps(func(depMod android.Module) {
171			if depMod.Name() == apiVariantModule {
172				mod = depMod
173				libName = apiVariantModule
174			}
175		})
176
177		if mod != nil {
178			variantMod, ok := mod.(*CcApiVariant)
179			if ok {
180				in = variantMod.Src()
181
182				// Copy LLDNK properties to cc_api_library module
183				exportIncludeDirs := append(d.libraryDecorator.flagExporter.Properties.Export_include_dirs.GetOrDefault(ctx, nil),
184					variantMod.exportProperties.Export_include_dirs...)
185				d.libraryDecorator.flagExporter.Properties.Export_include_dirs = proptools.NewConfigurable[[]string](
186					nil,
187					[]proptools.ConfigurableCase[[]string]{
188						proptools.NewConfigurableCase[[]string](nil, &exportIncludeDirs),
189					},
190				)
191
192				// Export headers as system include dirs if specified. Mostly for libc
193				if Bool(variantMod.exportProperties.Export_headers_as_system) {
194					d.libraryDecorator.flagExporter.Properties.Export_system_include_dirs = append(
195						d.libraryDecorator.flagExporter.Properties.Export_system_include_dirs,
196						d.libraryDecorator.flagExporter.Properties.Export_include_dirs.GetOrDefault(ctx, nil)...)
197					d.libraryDecorator.flagExporter.Properties.Export_include_dirs = proptools.NewConfigurable[[]string](nil, nil)
198				}
199			}
200		}
201	}
202
203	if m.InVendorOrProduct() && d.hasLLNDKStubs() {
204		// LLNDK variant
205		load_cc_variant(BuildApiVariantName(m.BaseModuleName(), "llndk", ""))
206	} else if m.IsSdkVariant() {
207		// NDK Variant
208		load_cc_variant(BuildApiVariantName(m.BaseModuleName(), "ndk", m.StubsVersion()))
209	} else if m.IsStubs() {
210		// APEX Variant
211		load_cc_variant(BuildApiVariantName(m.BaseModuleName(), "apex", m.StubsVersion()))
212	}
213
214	// Flags reexported from dependencies. (e.g. vndk_prebuilt_shared)
215	d.exportIncludes(ctx)
216	d.libraryDecorator.reexportDirs(deps.ReexportedDirs...)
217	d.libraryDecorator.reexportSystemDirs(deps.ReexportedSystemDirs...)
218	d.libraryDecorator.reexportFlags(deps.ReexportedFlags...)
219	d.libraryDecorator.reexportDeps(deps.ReexportedDeps...)
220	d.libraryDecorator.addExportedGeneratedHeaders(deps.ReexportedGeneratedHeaders...)
221
222	if in == nil {
223		ctx.PropertyErrorf("src", "Unable to locate source property")
224		return nil
225	}
226
227	// Make the _compilation_ of rdeps have an order-only dep on cc_api_library.src (an .so file)
228	// The .so file itself has an order-only dependency on the headers contributed by this library.
229	// Creating this dependency ensures that the headers are assembled before compilation of rdeps begins.
230	d.libraryDecorator.reexportDeps(in)
231	d.libraryDecorator.flagExporter.setProvider(ctx)
232
233	d.unstrippedOutputFile = in
234	libName += flags.Toolchain.ShlibSuffix()
235
236	tocFile := android.PathForModuleOut(ctx, libName+".toc")
237	d.tocFile = android.OptionalPathForPath(tocFile)
238	TransformSharedObjectToToc(ctx, in, tocFile)
239
240	outputFile := android.PathForModuleOut(ctx, libName)
241
242	// TODO(b/270485584) This copies with a new name, just to avoid conflict with prebuilts.
243	// We can just use original input if there is any way to avoid name conflict without copy.
244	ctx.Build(pctx, android.BuildParams{
245		Rule:        android.Cp,
246		Description: "API surface imported library",
247		Input:       in,
248		Output:      outputFile,
249		Args: map[string]string{
250			"cpFlags": "-L",
251		},
252	})
253
254	android.SetProvider(ctx, SharedLibraryInfoProvider, SharedLibraryInfo{
255		SharedLibrary: outputFile,
256		Target:        ctx.Target(),
257
258		TableOfContents: d.tocFile,
259	})
260
261	d.shareStubs(ctx)
262
263	return outputFile
264}
265
266// Share additional information about stub libraries with provider
267func (d *apiLibraryDecorator) shareStubs(ctx ModuleContext) {
268	stubs := ctx.GetDirectDepsWithTag(stubImplDepTag)
269	if len(stubs) > 0 {
270		var stubsInfo []SharedStubLibrary
271		for _, stub := range stubs {
272			stubInfo, _ := android.OtherModuleProvider(ctx, stub, SharedLibraryInfoProvider)
273			flagInfo, _ := android.OtherModuleProvider(ctx, stub, FlagExporterInfoProvider)
274			stubsInfo = append(stubsInfo, SharedStubLibrary{
275				Version:           moduleLibraryInterface(stub).stubsVersion(),
276				SharedLibraryInfo: stubInfo,
277				FlagExporterInfo:  flagInfo,
278			})
279		}
280		android.SetProvider(ctx, SharedLibraryStubsProvider, SharedLibraryStubsInfo{
281			SharedStubLibraries: stubsInfo,
282
283			IsLLNDK: ctx.IsLlndk(),
284		})
285	}
286}
287
288func (d *apiLibraryDecorator) availableFor(what string) bool {
289	// Stub from API surface should be available for any APEX.
290	return true
291}
292
293func (d *apiLibraryDecorator) hasApexStubs() bool {
294	for _, variant := range d.properties.Variants {
295		if strings.HasPrefix(variant, "apex") {
296			return true
297		}
298	}
299	return false
300}
301
302func (d *apiLibraryDecorator) hasStubsVariants() bool {
303	return d.hasApexStubs()
304}
305
306func (d *apiLibraryDecorator) stubsVersions(ctx android.BaseMutatorContext) []string {
307	m, ok := ctx.Module().(*Module)
308
309	if !ok {
310		return nil
311	}
312
313	// TODO(b/244244438) Create more version information for NDK and APEX variations
314	// NDK variants
315	if m.IsSdkVariant() {
316		// TODO(b/249193999) Do not check if module has NDK stubs once all NDK cc_api_library contains ndk variant of cc_api_variant.
317		if d.hasNDKStubs() {
318			return d.getNdkVersions()
319		}
320	}
321
322	if d.hasLLNDKStubs() && m.InVendorOrProduct() {
323		// LLNDK libraries only need a single stubs variant.
324		return []string{android.FutureApiLevel.String()}
325	}
326
327	stubsVersions := d.getStubVersions()
328
329	if len(stubsVersions) != 0 {
330		return stubsVersions
331	}
332
333	if m.MinSdkVersion() == "" {
334		return nil
335	}
336
337	firstVersion, err := nativeApiLevelFromUser(ctx,
338		m.MinSdkVersion())
339
340	if err != nil {
341		return nil
342	}
343
344	return ndkLibraryVersions(ctx, firstVersion)
345}
346
347func (d *apiLibraryDecorator) hasLLNDKStubs() bool {
348	return inList("llndk", d.properties.Variants)
349}
350
351func (d *apiLibraryDecorator) hasNDKStubs() bool {
352	for _, variant := range d.properties.Variants {
353		if ndkVariantRegex.MatchString(variant) {
354			return true
355		}
356	}
357	return false
358}
359
360func (d *apiLibraryDecorator) getNdkVersions() []string {
361	ndkVersions := []string{}
362
363	for _, variant := range d.properties.Variants {
364		if match := ndkVariantRegex.FindStringSubmatch(variant); len(match) == 2 {
365			ndkVersions = append(ndkVersions, match[1])
366		}
367	}
368
369	return ndkVersions
370}
371
372func (d *apiLibraryDecorator) getStubVersions() []string {
373	stubVersions := []string{}
374
375	for _, variant := range d.properties.Variants {
376		if match := stubVariantRegex.FindStringSubmatch(variant); len(match) == 2 {
377			stubVersions = append(stubVersions, match[1])
378		}
379	}
380
381	return stubVersions
382}
383
384// 'cc_api_headers' is similar with 'cc_api_library', but which replaces
385// header libraries. The module will replace any dependencies to existing
386// original header libraries.
387type apiHeadersDecorator struct {
388	*libraryDecorator
389}
390
391func CcApiHeadersFactory() android.Module {
392	module, decorator := NewLibrary(android.DeviceSupported)
393	apiHeadersDecorator := &apiHeadersDecorator{
394		libraryDecorator: decorator,
395	}
396	apiHeadersDecorator.HeaderOnly()
397
398	module.stl = nil
399	module.sanitize = nil
400	decorator.disableStripping()
401
402	module.compiler = nil
403	module.linker = apiHeadersDecorator
404	module.installer = nil
405
406	// Prevent default system libs (libc, libm, and libdl) from being linked
407	if apiHeadersDecorator.baseLinker.Properties.System_shared_libs == nil {
408		apiHeadersDecorator.baseLinker.Properties.System_shared_libs = []string{}
409	}
410
411	apiHeadersDecorator.baseLinker.Properties.No_libcrt = BoolPtr(true)
412	apiHeadersDecorator.baseLinker.Properties.Nocrt = BoolPtr(true)
413
414	module.Init()
415
416	return module
417}
418
419func (d *apiHeadersDecorator) Name(basename string) string {
420	return basename + multitree.GetApiImportSuffix()
421}
422
423func (d *apiHeadersDecorator) availableFor(what string) bool {
424	// Stub from API surface should be available for any APEX.
425	return true
426}
427
428type ccApiexportProperties struct {
429	Src     *string `android:"arch_variant"`
430	Variant *string
431	Version *string
432}
433
434type variantExporterProperties struct {
435	// Header directory to export
436	Export_include_dirs []string `android:"arch_variant"`
437
438	// Export all headers as system include
439	Export_headers_as_system *bool
440}
441
442type CcApiVariant struct {
443	android.ModuleBase
444
445	properties       ccApiexportProperties
446	exportProperties variantExporterProperties
447
448	src android.Path
449}
450
451var _ android.Module = (*CcApiVariant)(nil)
452var _ android.ImageInterface = (*CcApiVariant)(nil)
453
454func CcApiVariantFactory() android.Module {
455	module := &CcApiVariant{}
456
457	module.AddProperties(&module.properties)
458	module.AddProperties(&module.exportProperties)
459
460	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibBoth)
461	return module
462}
463
464func (v *CcApiVariant) GenerateAndroidBuildActions(ctx android.ModuleContext) {
465	// No need to build
466
467	if String(v.properties.Src) == "" {
468		ctx.PropertyErrorf("src", "src is a required property")
469	}
470
471	// Skip the existence check of the stub prebuilt file.
472	// The file is not guaranteed to exist during Soong analysis.
473	// Build orchestrator will be responsible for creating a connected ninja graph.
474	v.src = android.MaybeExistentPathForSource(ctx, ctx.ModuleDir(), String(v.properties.Src))
475}
476
477func (v *CcApiVariant) Name() string {
478	version := String(v.properties.Version)
479	return BuildApiVariantName(v.BaseModuleName(), *v.properties.Variant, version)
480}
481
482func (v *CcApiVariant) Src() android.Path {
483	return v.src
484}
485
486func BuildApiVariantName(baseName string, variant string, version string) string {
487	names := []string{baseName, variant}
488	if version != "" {
489		names = append(names, version)
490	}
491
492	return strings.Join(names[:], ".") + multitree.GetApiImportSuffix()
493}
494
495// Implement ImageInterface to generate image variants
496func (v *CcApiVariant) ImageMutatorBegin(ctx android.BaseModuleContext) {}
497func (v *CcApiVariant) CoreVariantNeeded(ctx android.BaseModuleContext) bool {
498	return inList(String(v.properties.Variant), []string{"ndk", "apex"})
499}
500func (v *CcApiVariant) RamdiskVariantNeeded(ctx android.BaseModuleContext) bool       { return false }
501func (v *CcApiVariant) VendorRamdiskVariantNeeded(ctx android.BaseModuleContext) bool { return false }
502func (v *CcApiVariant) DebugRamdiskVariantNeeded(ctx android.BaseModuleContext) bool  { return false }
503func (v *CcApiVariant) RecoveryVariantNeeded(ctx android.BaseModuleContext) bool      { return false }
504func (v *CcApiVariant) ExtraImageVariations(ctx android.BaseModuleContext) []string {
505	var variations []string
506
507	if String(v.properties.Variant) == "llndk" {
508		variations = append(variations, VendorVariation)
509		variations = append(variations, ProductVariation)
510	}
511
512	return variations
513}
514func (v *CcApiVariant) SetImageVariation(ctx android.BaseModuleContext, variation string) {
515}
516