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