Home | History | Annotate | Download | only in cc
      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 
     15 package cc
     16 
     17 import (
     18 	"fmt"
     19 	"strconv"
     20 	"strings"
     21 	"sync"
     22 
     23 	"github.com/google/blueprint"
     24 
     25 	"android/soong/android"
     26 )
     27 
     28 var (
     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 //
     78 type 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 
    102 type stubDecorator struct {
    103 	*libraryDecorator
    104 
    105 	properties libraryProperties
    106 
    107 	versionScriptPath android.ModuleGenPath
    108 	installPath       android.Path
    109 }
    110 
    111 // OMG GO
    112 func intMax(a int, b int) int {
    113 	if a > b {
    114 		return a
    115 	} else {
    116 		return b
    117 	}
    118 }
    119 
    120 func 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 
    158 func 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 
    166 func 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 
    197 func 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 
    226 func 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 
    236 func (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 
    254 func 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 
    270 func (stub *stubDecorator) compilerFlags(ctx ModuleContext, flags Flags, deps PathDeps) Flags {
    271 	flags = stub.baseCompiler.compilerFlags(ctx, flags, deps)
    272 	return addStubLibraryCompilerFlags(flags)
    273 }
    274 
    275 func 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 
    301 func (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 
    312 func (linker *stubDecorator) linkerDeps(ctx DepsContext, deps Deps) Deps {
    313 	return Deps{}
    314 }
    315 
    316 func (linker *stubDecorator) Name(name string) string {
    317 	return name + ndkLibrarySuffix
    318 }
    319 
    320 func (stub *stubDecorator) linkerFlags(ctx ModuleContext, flags Flags) Flags {
    321 	stub.libraryDecorator.libName = ctx.baseModuleName()
    322 	return stub.libraryDecorator.linkerFlags(ctx, flags)
    323 }
    324 
    325 func (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 
    341 func (stub *stubDecorator) nativeCoverage() bool {
    342 	return false
    343 }
    344 
    345 func (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 
    361 func 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 
    380 func ndkLibraryFactory() android.Module {
    381 	module := newStubLibrary()
    382 	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibBoth)
    383 	return module
    384 }
    385