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 $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 //
     76 type 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 
     95 type stubDecorator struct {
     96 	*libraryDecorator
     97 
     98 	properties libraryProperties
     99 
    100 	versionScriptPath android.ModuleGenPath
    101 	installPath       android.Path
    102 }
    103 
    104 // OMG GO
    105 func intMax(a int, b int) int {
    106 	if a > b {
    107 		return a
    108 	} else {
    109 		return b
    110 	}
    111 }
    112 
    113 func normalizeNdkApiLevel(ctx android.BaseContext, apiLevel string,
    114 	arch android.Arch) (string, error) {
    115 
    116 	if apiLevel == "current" {
    117 		return apiLevel, nil
    118 	}
    119 
    120 	minVersion := ctx.Config().MinSupportedSdkVersion()
    121 	firstArchVersions := map[android.ArchType]int{
    122 		android.Arm:    minVersion,
    123 		android.Arm64:  21,
    124 		android.Mips:   minVersion,
    125 		android.Mips64: 21,
    126 		android.X86:    minVersion,
    127 		android.X86_64: 21,
    128 	}
    129 
    130 	firstArchVersion, ok := firstArchVersions[arch.ArchType]
    131 	if !ok {
    132 		panic(fmt.Errorf("Arch %q not found in firstArchVersions", arch.ArchType))
    133 	}
    134 
    135 	if apiLevel == "minimum" {
    136 		return strconv.Itoa(firstArchVersion), nil
    137 	}
    138 
    139 	// If the NDK drops support for a platform version, we don't want to have to
    140 	// fix up every module that was using it as its SDK version. Clip to the
    141 	// supported version here instead.
    142 	version, err := strconv.Atoi(apiLevel)
    143 	if err != nil {
    144 		return "", fmt.Errorf("API level must be an integer (is %q)", apiLevel)
    145 	}
    146 	version = intMax(version, minVersion)
    147 
    148 	return strconv.Itoa(intMax(version, firstArchVersion)), nil
    149 }
    150 
    151 func getFirstGeneratedVersion(firstSupportedVersion string, platformVersion int) (int, error) {
    152 	if firstSupportedVersion == "current" {
    153 		return platformVersion + 1, nil
    154 	}
    155 
    156 	return strconv.Atoi(firstSupportedVersion)
    157 }
    158 
    159 func shouldUseVersionScript(stub *stubDecorator) (bool, error) {
    160 	// unversioned_until is normally empty, in which case we should use the version script.
    161 	if String(stub.properties.Unversioned_until) == "" {
    162 		return true, nil
    163 	}
    164 
    165 	if String(stub.properties.Unversioned_until) == "current" {
    166 		if stub.properties.ApiLevel == "current" {
    167 			return true, nil
    168 		} else {
    169 			return false, nil
    170 		}
    171 	}
    172 
    173 	if stub.properties.ApiLevel == "current" {
    174 		return true, nil
    175 	}
    176 
    177 	unversionedUntil, err := strconv.Atoi(String(stub.properties.Unversioned_until))
    178 	if err != nil {
    179 		return true, err
    180 	}
    181 
    182 	version, err := strconv.Atoi(stub.properties.ApiLevel)
    183 	if err != nil {
    184 		return true, err
    185 	}
    186 
    187 	return version >= unversionedUntil, nil
    188 }
    189 
    190 func generateStubApiVariants(mctx android.BottomUpMutatorContext, c *stubDecorator) {
    191 	platformVersion := mctx.Config().PlatformSdkVersionInt()
    192 
    193 	firstSupportedVersion, err := normalizeNdkApiLevel(mctx, String(c.properties.First_version),
    194 		mctx.Arch())
    195 	if err != nil {
    196 		mctx.PropertyErrorf("first_version", err.Error())
    197 	}
    198 
    199 	firstGenVersion, err := getFirstGeneratedVersion(firstSupportedVersion, platformVersion)
    200 	if err != nil {
    201 		// In theory this is impossible because we've already run this through
    202 		// normalizeNdkApiLevel above.
    203 		mctx.PropertyErrorf("first_version", err.Error())
    204 	}
    205 
    206 	var versionStrs []string
    207 	for version := firstGenVersion; version <= platformVersion; version++ {
    208 		versionStrs = append(versionStrs, strconv.Itoa(version))
    209 	}
    210 	versionStrs = append(versionStrs, mctx.Config().PlatformVersionActiveCodenames()...)
    211 	versionStrs = append(versionStrs, "current")
    212 
    213 	modules := mctx.CreateVariations(versionStrs...)
    214 	for i, module := range modules {
    215 		module.(*Module).compiler.(*stubDecorator).properties.ApiLevel = versionStrs[i]
    216 	}
    217 }
    218 
    219 func ndkApiMutator(mctx android.BottomUpMutatorContext) {
    220 	if m, ok := mctx.Module().(*Module); ok {
    221 		if m.Enabled() {
    222 			if compiler, ok := m.compiler.(*stubDecorator); ok {
    223 				generateStubApiVariants(mctx, compiler)
    224 			}
    225 		}
    226 	}
    227 }
    228 
    229 func (c *stubDecorator) compilerInit(ctx BaseModuleContext) {
    230 	c.baseCompiler.compilerInit(ctx)
    231 
    232 	name := ctx.baseModuleName()
    233 	if strings.HasSuffix(name, ndkLibrarySuffix) {
    234 		ctx.PropertyErrorf("name", "Do not append %q manually, just use the base name", ndkLibrarySuffix)
    235 	}
    236 
    237 	ndkMigratedLibsLock.Lock()
    238 	defer ndkMigratedLibsLock.Unlock()
    239 	for _, lib := range ndkMigratedLibs {
    240 		if lib == name {
    241 			return
    242 		}
    243 	}
    244 	ndkMigratedLibs = append(ndkMigratedLibs, name)
    245 }
    246 
    247 func addStubLibraryCompilerFlags(flags Flags) Flags {
    248 	flags.CFlags = append(flags.CFlags,
    249 		// We're knowingly doing some otherwise unsightly things with builtin
    250 		// functions here. We're just generating stub libraries, so ignore it.
    251 		"-Wno-incompatible-library-redeclaration",
    252 		"-Wno-builtin-requires-header",
    253 		"-Wno-invalid-noreturn",
    254 		"-Wall",
    255 		"-Werror",
    256 		// These libraries aren't actually used. Don't worry about unwinding
    257 		// (avoids the need to link an unwinder into a fake library).
    258 		"-fno-unwind-tables",
    259 	)
    260 	return flags
    261 }
    262 
    263 func (stub *stubDecorator) compilerFlags(ctx ModuleContext, flags Flags, deps PathDeps) Flags {
    264 	flags = stub.baseCompiler.compilerFlags(ctx, flags, deps)
    265 	return addStubLibraryCompilerFlags(flags)
    266 }
    267 
    268 func compileStubLibrary(ctx ModuleContext, flags Flags, symbolFile, apiLevel, vndk string) (Objects, android.ModuleGenPath) {
    269 	arch := ctx.Arch().ArchType.String()
    270 
    271 	stubSrcPath := android.PathForModuleGen(ctx, "stub.c")
    272 	versionScriptPath := android.PathForModuleGen(ctx, "stub.map")
    273 	symbolFilePath := android.PathForModuleSrc(ctx, symbolFile)
    274 	apiLevelsJson := android.GetApiLevelsJson(ctx)
    275 	ctx.Build(pctx, android.BuildParams{
    276 		Rule:        genStubSrc,
    277 		Description: "generate stubs " + symbolFilePath.Rel(),
    278 		Outputs:     []android.WritablePath{stubSrcPath, versionScriptPath},
    279 		Input:       symbolFilePath,
    280 		Implicits:   []android.Path{apiLevelsJson},
    281 		Args: map[string]string{
    282 			"arch":     arch,
    283 			"apiLevel": apiLevel,
    284 			"apiMap":   apiLevelsJson.String(),
    285 			"vndk":     vndk,
    286 		},
    287 	})
    288 
    289 	subdir := ""
    290 	srcs := []android.Path{stubSrcPath}
    291 	return compileObjs(ctx, flagsToBuilderFlags(flags), subdir, srcs, nil, nil), versionScriptPath
    292 }
    293 
    294 func (c *stubDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) Objects {
    295 	if !strings.HasSuffix(String(c.properties.Symbol_file), ".map.txt") {
    296 		ctx.PropertyErrorf("symbol_file", "must end with .map.txt")
    297 	}
    298 
    299 	objs, versionScript := compileStubLibrary(ctx, flags, String(c.properties.Symbol_file),
    300 		c.properties.ApiLevel, "")
    301 	c.versionScriptPath = versionScript
    302 	return objs
    303 }
    304 
    305 func (linker *stubDecorator) linkerDeps(ctx DepsContext, deps Deps) Deps {
    306 	return Deps{}
    307 }
    308 
    309 func (linker *stubDecorator) Name(name string) string {
    310 	return name + ndkLibrarySuffix
    311 }
    312 
    313 func (stub *stubDecorator) linkerFlags(ctx ModuleContext, flags Flags) Flags {
    314 	stub.libraryDecorator.libName = ctx.baseModuleName()
    315 	return stub.libraryDecorator.linkerFlags(ctx, flags)
    316 }
    317 
    318 func (stub *stubDecorator) link(ctx ModuleContext, flags Flags, deps PathDeps,
    319 	objs Objects) android.Path {
    320 
    321 	useVersionScript, err := shouldUseVersionScript(stub)
    322 	if err != nil {
    323 		ctx.ModuleErrorf(err.Error())
    324 	}
    325 
    326 	if useVersionScript {
    327 		linkerScriptFlag := "-Wl,--version-script," + stub.versionScriptPath.String()
    328 		flags.LdFlags = append(flags.LdFlags, linkerScriptFlag)
    329 	}
    330 
    331 	return stub.libraryDecorator.link(ctx, flags, deps, objs)
    332 }
    333 
    334 func (stub *stubDecorator) install(ctx ModuleContext, path android.Path) {
    335 	arch := ctx.Target().Arch.ArchType.Name
    336 	apiLevel := stub.properties.ApiLevel
    337 
    338 	// arm64 isn't actually a multilib toolchain, so unlike the other LP64
    339 	// architectures it's just installed to lib.
    340 	libDir := "lib"
    341 	if ctx.toolchain().Is64Bit() && arch != "arm64" {
    342 		libDir = "lib64"
    343 	}
    344 
    345 	installDir := getNdkInstallBase(ctx).Join(ctx, fmt.Sprintf(
    346 		"platforms/android-%s/arch-%s/usr/%s", apiLevel, arch, libDir))
    347 	stub.installPath = ctx.InstallFile(installDir, path.Base(), path)
    348 }
    349 
    350 func newStubLibrary() *Module {
    351 	module, library := NewLibrary(android.DeviceSupported)
    352 	library.BuildOnlyShared()
    353 	module.stl = nil
    354 	module.sanitize = nil
    355 	library.StripProperties.Strip.None = BoolPtr(true)
    356 
    357 	stub := &stubDecorator{
    358 		libraryDecorator: library,
    359 	}
    360 	module.compiler = stub
    361 	module.linker = stub
    362 	module.installer = stub
    363 
    364 	module.AddProperties(&stub.properties, &library.MutatedProperties)
    365 
    366 	return module
    367 }
    368 
    369 func ndkLibraryFactory() android.Module {
    370 	module := newStubLibrary()
    371 	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibBoth)
    372 	return module
    373 }
    374