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