• 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	"strconv"
20	"strings"
21	"sync"
22
23	"github.com/google/blueprint"
24
25	"android/soong/android"
26)
27
28var (
29	toolPath = pctx.SourcePathVariable("toolPath", "build/soong/cc/gen_stub_libs.py")
30
31	genStubSrc = pctx.AndroidStaticRule("genStubSrc",
32		blueprint.RuleParams{
33			Command: "$toolPath --arch $arch --api $apiLevel --api-map " +
34				"$apiMap $flags $in $out",
35			CommandDeps: []string{"$toolPath"},
36		}, "arch", "apiLevel", "apiMap", "flags")
37
38	ndkLibrarySuffix = ".ndk"
39
40	ndkPrebuiltSharedLibs = []string{
41		"android",
42		"binder_ndk",
43		"c",
44		"dl",
45		"EGL",
46		"GLESv1_CM",
47		"GLESv2",
48		"GLESv3",
49		"jnigraphics",
50		"log",
51		"mediandk",
52		"m",
53		"OpenMAXAL",
54		"OpenSLES",
55		"stdc++",
56		"sync",
57		"vulkan",
58		"z",
59	}
60	ndkPrebuiltSharedLibraries = addPrefix(append([]string(nil), ndkPrebuiltSharedLibs...), "lib")
61
62	// These libraries have migrated over to the new ndk_library, which is added
63	// as a variation dependency via depsMutator.
64	ndkMigratedLibs     = []string{}
65	ndkMigratedLibsLock sync.Mutex // protects ndkMigratedLibs writes during parallel BeginMutator
66)
67
68// Creates a stub shared library based on the provided version file.
69//
70// Example:
71//
72// ndk_library {
73//     name: "libfoo",
74//     symbol_file: "libfoo.map.txt",
75//     first_version: "9",
76// }
77//
78type libraryProperties struct {
79	// Relative path to the symbol map.
80	// An example file can be seen here: TODO(danalbert): Make an example.
81	Symbol_file *string
82
83	// The first API level a library was available. A library will be generated
84	// for every API level beginning with this one.
85	First_version *string
86
87	// The first API level that library should have the version script applied.
88	// This defaults to the value of first_version, and should almost never be
89	// used. This is only needed to work around platform bugs like
90	// https://github.com/android-ndk/ndk/issues/265.
91	Unversioned_until *string
92
93	// Private property for use by the mutator that splits per-API level.
94	ApiLevel string `blueprint:"mutated"`
95
96	// True if this API is not yet ready to be shipped in the NDK. It will be
97	// available in the platform for testing, but will be excluded from the
98	// sysroot provided to the NDK proper.
99	Draft bool
100}
101
102type stubDecorator struct {
103	*libraryDecorator
104
105	properties libraryProperties
106
107	versionScriptPath android.ModuleGenPath
108	installPath       android.Path
109}
110
111// OMG GO
112func intMax(a int, b int) int {
113	if a > b {
114		return a
115	} else {
116		return b
117	}
118}
119
120func normalizeNdkApiLevel(ctx android.BaseContext, apiLevel string,
121	arch android.Arch) (string, error) {
122
123	if apiLevel == "current" {
124		return apiLevel, nil
125	}
126
127	minVersion := ctx.Config().MinSupportedSdkVersion()
128	firstArchVersions := map[android.ArchType]int{
129		android.Arm:    minVersion,
130		android.Arm64:  21,
131		android.Mips:   minVersion,
132		android.Mips64: 21,
133		android.X86:    minVersion,
134		android.X86_64: 21,
135	}
136
137	firstArchVersion, ok := firstArchVersions[arch.ArchType]
138	if !ok {
139		panic(fmt.Errorf("Arch %q not found in firstArchVersions", arch.ArchType))
140	}
141
142	if apiLevel == "minimum" {
143		return strconv.Itoa(firstArchVersion), nil
144	}
145
146	// If the NDK drops support for a platform version, we don't want to have to
147	// fix up every module that was using it as its SDK version. Clip to the
148	// supported version here instead.
149	version, err := strconv.Atoi(apiLevel)
150	if err != nil {
151		return "", fmt.Errorf("API level must be an integer (is %q)", apiLevel)
152	}
153	version = intMax(version, minVersion)
154
155	return strconv.Itoa(intMax(version, firstArchVersion)), nil
156}
157
158func getFirstGeneratedVersion(firstSupportedVersion string, platformVersion int) (int, error) {
159	if firstSupportedVersion == "current" {
160		return platformVersion + 1, nil
161	}
162
163	return strconv.Atoi(firstSupportedVersion)
164}
165
166func shouldUseVersionScript(ctx android.BaseContext, stub *stubDecorator) (bool, error) {
167	// unversioned_until is normally empty, in which case we should use the version script.
168	if String(stub.properties.Unversioned_until) == "" {
169		return true, nil
170	}
171
172	if String(stub.properties.Unversioned_until) == "current" {
173		if stub.properties.ApiLevel == "current" {
174			return true, nil
175		} else {
176			return false, nil
177		}
178	}
179
180	if stub.properties.ApiLevel == "current" {
181		return true, nil
182	}
183
184	unversionedUntil, err := android.ApiStrToNum(ctx, String(stub.properties.Unversioned_until))
185	if err != nil {
186		return true, err
187	}
188
189	version, err := android.ApiStrToNum(ctx, stub.properties.ApiLevel)
190	if err != nil {
191		return true, err
192	}
193
194	return version >= unversionedUntil, nil
195}
196
197func generateStubApiVariants(mctx android.BottomUpMutatorContext, c *stubDecorator) {
198	platformVersion := mctx.Config().PlatformSdkVersionInt()
199
200	firstSupportedVersion, err := normalizeNdkApiLevel(mctx, String(c.properties.First_version),
201		mctx.Arch())
202	if err != nil {
203		mctx.PropertyErrorf("first_version", err.Error())
204	}
205
206	firstGenVersion, err := getFirstGeneratedVersion(firstSupportedVersion, platformVersion)
207	if err != nil {
208		// In theory this is impossible because we've already run this through
209		// normalizeNdkApiLevel above.
210		mctx.PropertyErrorf("first_version", err.Error())
211	}
212
213	var versionStrs []string
214	for version := firstGenVersion; version <= platformVersion; version++ {
215		versionStrs = append(versionStrs, strconv.Itoa(version))
216	}
217	versionStrs = append(versionStrs, mctx.Config().PlatformVersionActiveCodenames()...)
218	versionStrs = append(versionStrs, "current")
219
220	modules := mctx.CreateVariations(versionStrs...)
221	for i, module := range modules {
222		module.(*Module).compiler.(*stubDecorator).properties.ApiLevel = versionStrs[i]
223	}
224}
225
226func ndkApiMutator(mctx android.BottomUpMutatorContext) {
227	if m, ok := mctx.Module().(*Module); ok {
228		if m.Enabled() {
229			if compiler, ok := m.compiler.(*stubDecorator); ok {
230				generateStubApiVariants(mctx, compiler)
231			}
232		}
233	}
234}
235
236func (c *stubDecorator) compilerInit(ctx BaseModuleContext) {
237	c.baseCompiler.compilerInit(ctx)
238
239	name := ctx.baseModuleName()
240	if strings.HasSuffix(name, ndkLibrarySuffix) {
241		ctx.PropertyErrorf("name", "Do not append %q manually, just use the base name", ndkLibrarySuffix)
242	}
243
244	ndkMigratedLibsLock.Lock()
245	defer ndkMigratedLibsLock.Unlock()
246	for _, lib := range ndkMigratedLibs {
247		if lib == name {
248			return
249		}
250	}
251	ndkMigratedLibs = append(ndkMigratedLibs, name)
252}
253
254func addStubLibraryCompilerFlags(flags Flags) Flags {
255	flags.CFlags = append(flags.CFlags,
256		// We're knowingly doing some otherwise unsightly things with builtin
257		// functions here. We're just generating stub libraries, so ignore it.
258		"-Wno-incompatible-library-redeclaration",
259		"-Wno-builtin-requires-header",
260		"-Wno-invalid-noreturn",
261		"-Wall",
262		"-Werror",
263		// These libraries aren't actually used. Don't worry about unwinding
264		// (avoids the need to link an unwinder into a fake library).
265		"-fno-unwind-tables",
266	)
267	return flags
268}
269
270func (stub *stubDecorator) compilerFlags(ctx ModuleContext, flags Flags, deps PathDeps) Flags {
271	flags = stub.baseCompiler.compilerFlags(ctx, flags, deps)
272	return addStubLibraryCompilerFlags(flags)
273}
274
275func compileStubLibrary(ctx ModuleContext, flags Flags, symbolFile, apiLevel, genstubFlags string) (Objects, android.ModuleGenPath) {
276	arch := ctx.Arch().ArchType.String()
277
278	stubSrcPath := android.PathForModuleGen(ctx, "stub.c")
279	versionScriptPath := android.PathForModuleGen(ctx, "stub.map")
280	symbolFilePath := android.PathForModuleSrc(ctx, symbolFile)
281	apiLevelsJson := android.GetApiLevelsJson(ctx)
282	ctx.Build(pctx, android.BuildParams{
283		Rule:        genStubSrc,
284		Description: "generate stubs " + symbolFilePath.Rel(),
285		Outputs:     []android.WritablePath{stubSrcPath, versionScriptPath},
286		Input:       symbolFilePath,
287		Implicits:   []android.Path{apiLevelsJson},
288		Args: map[string]string{
289			"arch":     arch,
290			"apiLevel": apiLevel,
291			"apiMap":   apiLevelsJson.String(),
292			"flags":    genstubFlags,
293		},
294	})
295
296	subdir := ""
297	srcs := []android.Path{stubSrcPath}
298	return compileObjs(ctx, flagsToBuilderFlags(flags), subdir, srcs, nil, nil), versionScriptPath
299}
300
301func (c *stubDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) Objects {
302	if !strings.HasSuffix(String(c.properties.Symbol_file), ".map.txt") {
303		ctx.PropertyErrorf("symbol_file", "must end with .map.txt")
304	}
305
306	objs, versionScript := compileStubLibrary(ctx, flags, String(c.properties.Symbol_file),
307		c.properties.ApiLevel, "")
308	c.versionScriptPath = versionScript
309	return objs
310}
311
312func (linker *stubDecorator) linkerDeps(ctx DepsContext, deps Deps) Deps {
313	return Deps{}
314}
315
316func (linker *stubDecorator) Name(name string) string {
317	return name + ndkLibrarySuffix
318}
319
320func (stub *stubDecorator) linkerFlags(ctx ModuleContext, flags Flags) Flags {
321	stub.libraryDecorator.libName = ctx.baseModuleName()
322	return stub.libraryDecorator.linkerFlags(ctx, flags)
323}
324
325func (stub *stubDecorator) link(ctx ModuleContext, flags Flags, deps PathDeps,
326	objs Objects) android.Path {
327
328	useVersionScript, err := shouldUseVersionScript(ctx, stub)
329	if err != nil {
330		ctx.ModuleErrorf(err.Error())
331	}
332
333	if useVersionScript {
334		linkerScriptFlag := "-Wl,--version-script," + stub.versionScriptPath.String()
335		flags.LdFlags = append(flags.LdFlags, linkerScriptFlag)
336	}
337
338	return stub.libraryDecorator.link(ctx, flags, deps, objs)
339}
340
341func (stub *stubDecorator) nativeCoverage() bool {
342	return false
343}
344
345func (stub *stubDecorator) install(ctx ModuleContext, path android.Path) {
346	arch := ctx.Target().Arch.ArchType.Name
347	apiLevel := stub.properties.ApiLevel
348
349	// arm64 isn't actually a multilib toolchain, so unlike the other LP64
350	// architectures it's just installed to lib.
351	libDir := "lib"
352	if ctx.toolchain().Is64Bit() && arch != "arm64" {
353		libDir = "lib64"
354	}
355
356	installDir := getNdkInstallBase(ctx).Join(ctx, fmt.Sprintf(
357		"platforms/android-%s/arch-%s/usr/%s", apiLevel, arch, libDir))
358	stub.installPath = ctx.InstallFile(installDir, path.Base(), path)
359}
360
361func newStubLibrary() *Module {
362	module, library := NewLibrary(android.DeviceSupported)
363	library.BuildOnlyShared()
364	module.stl = nil
365	module.sanitize = nil
366	library.StripProperties.Strip.None = BoolPtr(true)
367
368	stub := &stubDecorator{
369		libraryDecorator: library,
370	}
371	module.compiler = stub
372	module.linker = stub
373	module.installer = stub
374
375	module.AddProperties(&stub.properties, &library.MutatedProperties)
376
377	return module
378}
379
380func ndkLibraryFactory() android.Module {
381	module := newStubLibrary()
382	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibBoth)
383	return module
384}
385