1// Copyright 2016 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	"fmt"
19	"path/filepath"
20	"runtime"
21	"strings"
22	"sync"
23
24	"github.com/google/blueprint"
25	"github.com/google/blueprint/proptools"
26
27	"android/soong/android"
28	"android/soong/cc/config"
29)
30
31func init() {
32	pctx.HostBinToolVariable("ndkStubGenerator", "ndkstubgen")
33	pctx.HostBinToolVariable("stg", "stg")
34	pctx.HostBinToolVariable("stgdiff", "stgdiff")
35}
36
37var (
38	genStubSrc = pctx.AndroidStaticRule("genStubSrc",
39		blueprint.RuleParams{
40			Command: "$ndkStubGenerator --arch $arch --api $apiLevel " +
41				"--api-map $apiMap $flags $in $out",
42			CommandDeps: []string{"$ndkStubGenerator"},
43		}, "arch", "apiLevel", "apiMap", "flags")
44
45	// $headersList should include paths to public headers. All types
46	// that are defined outside of public headers will be excluded from
47	// ABI monitoring.
48	//
49	// STG tool doesn't access content of files listed in $headersList,
50	// so there is no need to add them to dependencies.
51	stg = pctx.AndroidStaticRule("stg",
52		blueprint.RuleParams{
53			Command:     "$stg -S :$symbolList --file-filter :$headersList --elf $in -o $out",
54			CommandDeps: []string{"$stg"},
55		}, "symbolList", "headersList")
56
57	stgdiff = pctx.AndroidStaticRule("stgdiff",
58		blueprint.RuleParams{
59			// Need to create *some* output for ninja. We don't want to use tee
60			// because we don't want to spam the build output with "nothing
61			// changed" messages, so redirect output message to $out, and if
62			// changes were detected print the output and fail.
63			Command:     "$stgdiff $args --stg $in -o $out || (cat $out && echo 'Run $$ANDROID_BUILD_TOP/development/tools/ndk/update_ndk_abi.sh to update the ABI dumps.' && false)",
64			CommandDeps: []string{"$stgdiff"},
65		}, "args")
66
67	ndkLibrarySuffix = ".ndk"
68
69	ndkKnownLibsKey = android.NewOnceKey("ndkKnownLibsKey")
70	// protects ndkKnownLibs writes during parallel BeginMutator.
71	ndkKnownLibsLock sync.Mutex
72
73	stubImplementation = dependencyTag{name: "stubImplementation"}
74)
75
76// The First_version and Unversioned_until properties of this struct should not
77// be used directly, but rather through the ApiLevel returning methods
78// firstVersion() and unversionedUntil().
79
80// Creates a stub shared library based on the provided version file.
81//
82// Example:
83//
84// ndk_library {
85//
86//	name: "libfoo",
87//	symbol_file: "libfoo.map.txt",
88//	first_version: "9",
89//
90// }
91type libraryProperties struct {
92	// Relative path to the symbol map.
93	// An example file can be seen here: TODO(danalbert): Make an example.
94	Symbol_file *string `android:"path"`
95
96	// The first API level a library was available. A library will be generated
97	// for every API level beginning with this one.
98	First_version *string
99
100	// The first API level that library should have the version script applied.
101	// This defaults to the value of first_version, and should almost never be
102	// used. This is only needed to work around platform bugs like
103	// https://github.com/android-ndk/ndk/issues/265.
104	Unversioned_until *string
105
106	// Headers presented by this library to the Public API Surface
107	Export_header_libs []string
108}
109
110type stubDecorator struct {
111	*libraryDecorator
112
113	properties libraryProperties
114
115	versionScriptPath     android.ModuleGenPath
116	parsedCoverageXmlPath android.ModuleOutPath
117	installPath           android.Path
118	abiDumpPath           android.OutputPath
119	abiDiffPaths          android.Paths
120
121	apiLevel         android.ApiLevel
122	firstVersion     android.ApiLevel
123	unversionedUntil android.ApiLevel
124}
125
126var _ versionedInterface = (*stubDecorator)(nil)
127
128func shouldUseVersionScript(ctx BaseModuleContext, stub *stubDecorator) bool {
129	return stub.apiLevel.GreaterThanOrEqualTo(stub.unversionedUntil)
130}
131
132func (stub *stubDecorator) implementationModuleName(name string) string {
133	return strings.TrimSuffix(name, ndkLibrarySuffix)
134}
135
136func ndkLibraryVersions(ctx android.BaseMutatorContext, from android.ApiLevel) []string {
137	var versions []android.ApiLevel
138	versionStrs := []string{}
139	for _, version := range ctx.Config().AllSupportedApiLevels() {
140		if version.GreaterThanOrEqualTo(from) {
141			versions = append(versions, version)
142			versionStrs = append(versionStrs, version.String())
143		}
144	}
145	versionStrs = append(versionStrs, android.FutureApiLevel.String())
146
147	return versionStrs
148}
149
150func (this *stubDecorator) stubsVersions(ctx android.BaseMutatorContext) []string {
151	if !ctx.Module().Enabled(ctx) {
152		return nil
153	}
154	if ctx.Target().NativeBridge == android.NativeBridgeEnabled {
155		ctx.Module().Disable()
156		return nil
157	}
158	firstVersion, err := nativeApiLevelFromUser(ctx,
159		String(this.properties.First_version))
160	if err != nil {
161		ctx.PropertyErrorf("first_version", err.Error())
162		return nil
163	}
164	return ndkLibraryVersions(ctx, firstVersion)
165}
166
167func (this *stubDecorator) initializeProperties(ctx BaseModuleContext) bool {
168	this.apiLevel = nativeApiLevelOrPanic(ctx, this.stubsVersion())
169
170	var err error
171	this.firstVersion, err = nativeApiLevelFromUser(ctx,
172		String(this.properties.First_version))
173	if err != nil {
174		ctx.PropertyErrorf("first_version", err.Error())
175		return false
176	}
177
178	str := proptools.StringDefault(this.properties.Unversioned_until, "minimum")
179	this.unversionedUntil, err = nativeApiLevelFromUser(ctx, str)
180	if err != nil {
181		ctx.PropertyErrorf("unversioned_until", err.Error())
182		return false
183	}
184
185	return true
186}
187
188func getNDKKnownLibs(config android.Config) *[]string {
189	return config.Once(ndkKnownLibsKey, func() interface{} {
190		return &[]string{}
191	}).(*[]string)
192}
193
194func (c *stubDecorator) compilerInit(ctx BaseModuleContext) {
195	c.baseCompiler.compilerInit(ctx)
196
197	name := ctx.baseModuleName()
198	if strings.HasSuffix(name, ndkLibrarySuffix) {
199		ctx.PropertyErrorf("name", "Do not append %q manually, just use the base name", ndkLibrarySuffix)
200	}
201
202	ndkKnownLibsLock.Lock()
203	defer ndkKnownLibsLock.Unlock()
204	ndkKnownLibs := getNDKKnownLibs(ctx.Config())
205	for _, lib := range *ndkKnownLibs {
206		if lib == name {
207			return
208		}
209	}
210	*ndkKnownLibs = append(*ndkKnownLibs, name)
211}
212
213var stubLibraryCompilerFlags = []string{
214	// We're knowingly doing some otherwise unsightly things with builtin
215	// functions here. We're just generating stub libraries, so ignore it.
216	"-Wno-incompatible-library-redeclaration",
217	"-Wno-incomplete-setjmp-declaration",
218	"-Wno-builtin-requires-header",
219	"-Wno-invalid-noreturn",
220	"-Wall",
221	"-Werror",
222	// These libraries aren't actually used. Don't worry about unwinding
223	// (avoids the need to link an unwinder into a fake library).
224	"-fno-unwind-tables",
225}
226
227func init() {
228	pctx.StaticVariable("StubLibraryCompilerFlags", strings.Join(stubLibraryCompilerFlags, " "))
229}
230
231func addStubLibraryCompilerFlags(flags Flags) Flags {
232	flags.Global.CFlags = append(flags.Global.CFlags, stubLibraryCompilerFlags...)
233	// All symbols in the stubs library should be visible.
234	if inList("-fvisibility=hidden", flags.Local.CFlags) {
235		flags.Local.CFlags = append(flags.Local.CFlags, "-fvisibility=default")
236	}
237	return flags
238}
239
240func (stub *stubDecorator) compilerFlags(ctx ModuleContext, flags Flags, deps PathDeps) Flags {
241	flags = stub.baseCompiler.compilerFlags(ctx, flags, deps)
242	return addStubLibraryCompilerFlags(flags)
243}
244
245type ndkApiOutputs struct {
246	stubSrc       android.ModuleGenPath
247	versionScript android.ModuleGenPath
248	symbolList    android.ModuleGenPath
249}
250
251func parseNativeAbiDefinition(ctx ModuleContext, symbolFile string,
252	apiLevel android.ApiLevel, genstubFlags string) ndkApiOutputs {
253
254	stubSrcPath := android.PathForModuleGen(ctx, "stub.c")
255	versionScriptPath := android.PathForModuleGen(ctx, "stub.map")
256	symbolFilePath := android.PathForModuleSrc(ctx, symbolFile)
257	symbolListPath := android.PathForModuleGen(ctx, "abi_symbol_list.txt")
258	apiLevelsJson := android.GetApiLevelsJson(ctx)
259	ctx.Build(pctx, android.BuildParams{
260		Rule:        genStubSrc,
261		Description: "generate stubs " + symbolFilePath.Rel(),
262		Outputs: []android.WritablePath{stubSrcPath, versionScriptPath,
263			symbolListPath},
264		Input:     symbolFilePath,
265		Implicits: []android.Path{apiLevelsJson},
266		Args: map[string]string{
267			"arch":     ctx.Arch().ArchType.String(),
268			"apiLevel": apiLevel.String(),
269			"apiMap":   apiLevelsJson.String(),
270			"flags":    genstubFlags,
271		},
272	})
273
274	return ndkApiOutputs{
275		stubSrc:       stubSrcPath,
276		versionScript: versionScriptPath,
277		symbolList:    symbolListPath,
278	}
279}
280
281func compileStubLibrary(ctx ModuleContext, flags Flags, src android.Path) Objects {
282	// libc/libm stubs libraries end up mismatching with clang's internal definition of these
283	// functions (which have noreturn attributes and other things). Because we just want to create a
284	// stub with symbol definitions, and types aren't important in C, ignore the mismatch.
285	flags.Local.ConlyFlags = append(flags.Local.ConlyFlags, "-fno-builtin")
286	return compileObjs(ctx, flagsToBuilderFlags(flags), "",
287		android.Paths{src}, nil, nil, nil, nil)
288}
289
290func (this *stubDecorator) findImplementationLibrary(ctx ModuleContext) android.Path {
291	dep := ctx.GetDirectDepWithTag(strings.TrimSuffix(ctx.ModuleName(), ndkLibrarySuffix),
292		stubImplementation)
293	if dep == nil {
294		ctx.ModuleErrorf("Could not find implementation for stub")
295		return nil
296	}
297	impl, ok := dep.(*Module)
298	if !ok {
299		ctx.ModuleErrorf("Implementation for stub is not correct module type")
300		return nil
301	}
302	output := impl.UnstrippedOutputFile()
303	if output == nil {
304		ctx.ModuleErrorf("implementation module (%s) has no output", impl)
305		return nil
306	}
307
308	return output
309}
310
311func (this *stubDecorator) libraryName(ctx ModuleContext) string {
312	return strings.TrimSuffix(ctx.ModuleName(), ndkLibrarySuffix)
313}
314
315func (this *stubDecorator) findPrebuiltAbiDump(ctx ModuleContext,
316	apiLevel android.ApiLevel) android.OptionalPath {
317
318	subpath := filepath.Join("prebuilts/abi-dumps/ndk", apiLevel.String(),
319		ctx.Arch().ArchType.String(), this.libraryName(ctx), "abi.stg")
320	return android.ExistentPathForSource(ctx, subpath)
321}
322
323// Feature flag.
324func canDumpAbi(config android.Config) bool {
325	if runtime.GOOS == "darwin" {
326		return false
327	}
328	// http://b/156513478
329	return config.ReleaseNdkAbiMonitored()
330}
331
332// Feature flag to disable diffing against prebuilts.
333func canDiffAbi(config android.Config) bool {
334	return config.ReleaseNdkAbiMonitored()
335}
336
337func (this *stubDecorator) dumpAbi(ctx ModuleContext, symbolList android.Path) {
338	implementationLibrary := this.findImplementationLibrary(ctx)
339	this.abiDumpPath = getNdkAbiDumpInstallBase(ctx).Join(ctx,
340		this.apiLevel.String(), ctx.Arch().ArchType.String(),
341		this.libraryName(ctx), "abi.stg")
342	headersList := getNdkABIHeadersFile(ctx)
343	ctx.Build(pctx, android.BuildParams{
344		Rule:        stg,
345		Description: fmt.Sprintf("stg %s", implementationLibrary),
346		Input:       implementationLibrary,
347		Implicits: []android.Path{
348			symbolList,
349			headersList,
350		},
351		Output: this.abiDumpPath,
352		Args: map[string]string{
353			"symbolList":  symbolList.String(),
354			"headersList": headersList.String(),
355		},
356	})
357}
358
359func findNextApiLevel(ctx ModuleContext, apiLevel android.ApiLevel) *android.ApiLevel {
360	apiLevels := append(ctx.Config().AllSupportedApiLevels(),
361		android.FutureApiLevel)
362	for _, api := range apiLevels {
363		if api.GreaterThan(apiLevel) {
364			return &api
365		}
366	}
367	return nil
368}
369
370func (this *stubDecorator) diffAbi(ctx ModuleContext) {
371	// Catch any ABI changes compared to the checked-in definition of this API
372	// level.
373	abiDiffPath := android.PathForModuleOut(ctx, "stgdiff.timestamp")
374	prebuiltAbiDump := this.findPrebuiltAbiDump(ctx, this.apiLevel)
375	missingPrebuiltErrorTemplate :=
376		"Did not find prebuilt ABI dump for %q (%q). Generate with " +
377			"//development/tools/ndk/update_ndk_abi.sh."
378	missingPrebuiltError := fmt.Sprintf(
379		missingPrebuiltErrorTemplate, this.libraryName(ctx),
380		prebuiltAbiDump.InvalidReason())
381	if !prebuiltAbiDump.Valid() {
382		ctx.Build(pctx, android.BuildParams{
383			Rule:   android.ErrorRule,
384			Output: abiDiffPath,
385			Args: map[string]string{
386				"error": missingPrebuiltError,
387			},
388		})
389	} else {
390		ctx.Build(pctx, android.BuildParams{
391			Rule: stgdiff,
392			Description: fmt.Sprintf("Comparing ABI %s %s", prebuiltAbiDump,
393				this.abiDumpPath),
394			Output: abiDiffPath,
395			Inputs: android.Paths{prebuiltAbiDump.Path(), this.abiDumpPath},
396			Args: map[string]string{
397				"args": "--format=small",
398			},
399		})
400	}
401	this.abiDiffPaths = append(this.abiDiffPaths, abiDiffPath)
402
403	// Also ensure that the ABI of the next API level (if there is one) matches
404	// this API level. *New* ABI is allowed, but any changes to APIs that exist
405	// in this API level are disallowed.
406	if !this.apiLevel.IsCurrent() && prebuiltAbiDump.Valid() {
407		nextApiLevel := findNextApiLevel(ctx, this.apiLevel)
408		if nextApiLevel == nil {
409			panic(fmt.Errorf("could not determine which API level follows "+
410				"non-current API level %s", this.apiLevel))
411		}
412		nextAbiDiffPath := android.PathForModuleOut(ctx,
413			"abidiff_next.timestamp")
414		nextAbiDump := this.findPrebuiltAbiDump(ctx, *nextApiLevel)
415		missingNextPrebuiltError := fmt.Sprintf(
416			missingPrebuiltErrorTemplate, this.libraryName(ctx),
417			nextAbiDump.InvalidReason())
418		if !nextAbiDump.Valid() {
419			ctx.Build(pctx, android.BuildParams{
420				Rule:   android.ErrorRule,
421				Output: nextAbiDiffPath,
422				Args: map[string]string{
423					"error": missingNextPrebuiltError,
424				},
425			})
426		} else {
427			ctx.Build(pctx, android.BuildParams{
428				Rule: stgdiff,
429				Description: fmt.Sprintf(
430					"Comparing ABI to the next API level %s %s",
431					prebuiltAbiDump, nextAbiDump),
432				Output: nextAbiDiffPath,
433				Inputs: android.Paths{
434					prebuiltAbiDump.Path(), nextAbiDump.Path()},
435				Args: map[string]string{
436					"args": "--format=small --ignore=interface_addition",
437				},
438			})
439		}
440		this.abiDiffPaths = append(this.abiDiffPaths, nextAbiDiffPath)
441	}
442}
443
444func (c *stubDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) Objects {
445	if !strings.HasSuffix(String(c.properties.Symbol_file), ".map.txt") {
446		ctx.PropertyErrorf("symbol_file", "must end with .map.txt")
447	}
448
449	if !c.buildStubs() {
450		// NDK libraries have no implementation variant, nothing to do
451		return Objects{}
452	}
453
454	if !c.initializeProperties(ctx) {
455		// Emits its own errors, so we don't need to.
456		return Objects{}
457	}
458
459	symbolFile := String(c.properties.Symbol_file)
460	nativeAbiResult := parseNativeAbiDefinition(ctx, symbolFile, c.apiLevel, "")
461	objs := compileStubLibrary(ctx, flags, nativeAbiResult.stubSrc)
462	c.versionScriptPath = nativeAbiResult.versionScript
463	if canDumpAbi(ctx.Config()) {
464		c.dumpAbi(ctx, nativeAbiResult.symbolList)
465		if canDiffAbi(ctx.Config()) {
466			c.diffAbi(ctx)
467		}
468	}
469	if c.apiLevel.IsCurrent() && ctx.PrimaryArch() {
470		c.parsedCoverageXmlPath = parseSymbolFileForAPICoverage(ctx, symbolFile)
471	}
472	return objs
473}
474
475// Add a dependency on the header modules of this ndk_library
476func (linker *stubDecorator) linkerDeps(ctx DepsContext, deps Deps) Deps {
477	return Deps{
478		HeaderLibs: linker.properties.Export_header_libs,
479	}
480}
481
482func (linker *stubDecorator) moduleInfoJSON(ctx ModuleContext, moduleInfoJSON *android.ModuleInfoJSON) {
483	linker.libraryDecorator.moduleInfoJSON(ctx, moduleInfoJSON)
484	// Overwrites the SubName computed by libraryDecorator
485	moduleInfoJSON.SubName = ndkLibrarySuffix + "." + linker.apiLevel.String()
486}
487
488func (linker *stubDecorator) Name(name string) string {
489	return name + ndkLibrarySuffix
490}
491
492func (stub *stubDecorator) linkerFlags(ctx ModuleContext, flags Flags) Flags {
493	stub.libraryDecorator.libName = ctx.baseModuleName()
494	return stub.libraryDecorator.linkerFlags(ctx, flags)
495}
496
497func (stub *stubDecorator) link(ctx ModuleContext, flags Flags, deps PathDeps,
498	objs Objects) android.Path {
499
500	if !stub.buildStubs() {
501		// NDK libraries have no implementation variant, nothing to do
502		return nil
503	}
504
505	if shouldUseVersionScript(ctx, stub) {
506		linkerScriptFlag := "-Wl,--version-script," + stub.versionScriptPath.String()
507		flags.Local.LdFlags = append(flags.Local.LdFlags, linkerScriptFlag)
508		flags.LdFlagsDeps = append(flags.LdFlagsDeps, stub.versionScriptPath)
509	}
510
511	stub.libraryDecorator.skipAPIDefine = true
512	return stub.libraryDecorator.link(ctx, flags, deps, objs)
513}
514
515func (stub *stubDecorator) nativeCoverage() bool {
516	return false
517}
518
519// Returns the install path for unversioned NDK libraries (currently only static
520// libraries).
521func getUnversionedLibraryInstallPath(ctx ModuleContext) android.OutputPath {
522	return getNdkSysrootBase(ctx).Join(ctx, "usr/lib", config.NDKTriple(ctx.toolchain()))
523}
524
525// Returns the install path for versioned NDK libraries. These are most often
526// stubs, but the same paths are used for CRT objects.
527func getVersionedLibraryInstallPath(ctx ModuleContext, apiLevel android.ApiLevel) android.OutputPath {
528	return getUnversionedLibraryInstallPath(ctx).Join(ctx, apiLevel.String())
529}
530
531func (stub *stubDecorator) install(ctx ModuleContext, path android.Path) {
532	installDir := getVersionedLibraryInstallPath(ctx, stub.apiLevel)
533	out := installDir.Join(ctx, path.Base())
534	ctx.Build(pctx, android.BuildParams{
535		Rule:   android.Cp,
536		Input:  path,
537		Output: out,
538	})
539	stub.installPath = out
540}
541
542func newStubLibrary() *Module {
543	module, library := NewLibrary(android.DeviceSupported)
544	library.BuildOnlyShared()
545	module.stl = nil
546	module.sanitize = nil
547	library.disableStripping()
548
549	stub := &stubDecorator{
550		libraryDecorator: library,
551	}
552	module.compiler = stub
553	module.linker = stub
554	module.installer = stub
555	module.library = stub
556
557	module.Properties.AlwaysSdk = true
558	module.Properties.Sdk_version = StringPtr("current")
559
560	module.AddProperties(&stub.properties, &library.MutatedProperties)
561
562	return module
563}
564
565// ndk_library creates a library that exposes a stub implementation of functions
566// and variables for use at build time only.
567func NdkLibraryFactory() android.Module {
568	module := newStubLibrary()
569	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibBoth)
570	return module
571}
572