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 $vndk $in $out",
     34 			Description: "genStubSrc $out",
     35 			CommandDeps: []string{"$toolPath"},
     36 		}, "arch", "apiLevel", "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       string
    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(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[string]int{
    120 		"arm":    9,
    121 		"arm64":  21,
    122 		"mips":   9,
    123 		"mips64": 21,
    124 		"x86":    9,
    125 		"x86_64": 21,
    126 	}
    127 
    128 	// If the NDK drops support for a platform version, we don't want to have to
    129 	// fix up every module that was using it as its SDK version. Clip to the
    130 	// supported version here instead.
    131 	version, err := strconv.Atoi(apiLevel)
    132 	if err != nil {
    133 		return "", fmt.Errorf("API level must be an integer (is %q)", apiLevel)
    134 	}
    135 	version = intMax(version, minVersion)
    136 
    137 	archStr := arch.ArchType.String()
    138 	firstArchVersion, ok := firstArchVersions[archStr]
    139 	if !ok {
    140 		panic(fmt.Errorf("Arch %q not found in firstArchVersions", archStr))
    141 	}
    142 
    143 	return strconv.Itoa(intMax(version, firstArchVersion)), nil
    144 }
    145 
    146 func getFirstGeneratedVersion(firstSupportedVersion string, platformVersion int) (int, error) {
    147 	if firstSupportedVersion == "current" {
    148 		return platformVersion + 1, nil
    149 	}
    150 
    151 	return strconv.Atoi(firstSupportedVersion)
    152 }
    153 
    154 func shouldUseVersionScript(stub *stubDecorator) (bool, error) {
    155 	// unversioned_until is normally empty, in which case we should use the version script.
    156 	if stub.properties.Unversioned_until == "" {
    157 		return true, nil
    158 	}
    159 
    160 	if stub.properties.Unversioned_until == "current" {
    161 		if stub.properties.ApiLevel == "current" {
    162 			return true, nil
    163 		} else {
    164 			return false, nil
    165 		}
    166 	}
    167 
    168 	if stub.properties.ApiLevel == "current" {
    169 		return true, nil
    170 	}
    171 
    172 	unversionedUntil, err := strconv.Atoi(stub.properties.Unversioned_until)
    173 	if err != nil {
    174 		return true, err
    175 	}
    176 
    177 	version, err := strconv.Atoi(stub.properties.ApiLevel)
    178 	if err != nil {
    179 		return true, err
    180 	}
    181 
    182 	return version >= unversionedUntil, nil
    183 }
    184 
    185 func generateStubApiVariants(mctx android.BottomUpMutatorContext, c *stubDecorator) {
    186 	platformVersion := mctx.AConfig().PlatformSdkVersionInt()
    187 
    188 	firstSupportedVersion, err := normalizeNdkApiLevel(c.properties.First_version,
    189 		mctx.Arch())
    190 	if err != nil {
    191 		mctx.PropertyErrorf("first_version", err.Error())
    192 	}
    193 
    194 	firstGenVersion, err := getFirstGeneratedVersion(firstSupportedVersion, platformVersion)
    195 	if err != nil {
    196 		// In theory this is impossible because we've already run this through
    197 		// normalizeNdkApiLevel above.
    198 		mctx.PropertyErrorf("first_version", err.Error())
    199 	}
    200 
    201 	var versionStrs []string
    202 	for version := firstGenVersion; version <= platformVersion; version++ {
    203 		versionStrs = append(versionStrs, strconv.Itoa(version))
    204 	}
    205 	versionStrs = append(versionStrs, "current")
    206 
    207 	modules := mctx.CreateVariations(versionStrs...)
    208 	for i, module := range modules {
    209 		module.(*Module).compiler.(*stubDecorator).properties.ApiLevel = versionStrs[i]
    210 	}
    211 }
    212 
    213 func ndkApiMutator(mctx android.BottomUpMutatorContext) {
    214 	if m, ok := mctx.Module().(*Module); ok {
    215 		if compiler, ok := m.compiler.(*stubDecorator); ok {
    216 			generateStubApiVariants(mctx, compiler)
    217 		}
    218 	}
    219 }
    220 
    221 func (c *stubDecorator) compilerInit(ctx BaseModuleContext) {
    222 	c.baseCompiler.compilerInit(ctx)
    223 
    224 	name := ctx.baseModuleName()
    225 	if strings.HasSuffix(name, ndkLibrarySuffix) {
    226 		ctx.PropertyErrorf("name", "Do not append %q manually, just use the base name", ndkLibrarySuffix)
    227 	}
    228 
    229 	ndkMigratedLibsLock.Lock()
    230 	defer ndkMigratedLibsLock.Unlock()
    231 	for _, lib := range ndkMigratedLibs {
    232 		if lib == name {
    233 			return
    234 		}
    235 	}
    236 	ndkMigratedLibs = append(ndkMigratedLibs, name)
    237 }
    238 
    239 func compileStubLibrary(ctx ModuleContext, flags Flags, symbolFile, apiLevel, vndk string) (Objects, android.ModuleGenPath) {
    240 	arch := ctx.Arch().ArchType.String()
    241 
    242 	stubSrcPath := android.PathForModuleGen(ctx, "stub.c")
    243 	versionScriptPath := android.PathForModuleGen(ctx, "stub.map")
    244 	symbolFilePath := android.PathForModuleSrc(ctx, symbolFile)
    245 	ctx.ModuleBuild(pctx, android.ModuleBuildParams{
    246 		Rule:    genStubSrc,
    247 		Outputs: []android.WritablePath{stubSrcPath, versionScriptPath},
    248 		Input:   symbolFilePath,
    249 		Args: map[string]string{
    250 			"arch":     arch,
    251 			"apiLevel": apiLevel,
    252 			"vndk":     vndk,
    253 		},
    254 	})
    255 
    256 	flags.CFlags = append(flags.CFlags,
    257 		// We're knowingly doing some otherwise unsightly things with builtin
    258 		// functions here. We're just generating stub libraries, so ignore it.
    259 		"-Wno-incompatible-library-redeclaration",
    260 		"-Wno-builtin-requires-header",
    261 		"-Wno-invalid-noreturn",
    262 
    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 
    268 	subdir := ""
    269 	srcs := []android.Path{stubSrcPath}
    270 	return compileObjs(ctx, flagsToBuilderFlags(flags), subdir, srcs, nil), versionScriptPath
    271 }
    272 
    273 func (c *stubDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) Objects {
    274 	objs, versionScript := compileStubLibrary(ctx, flags, c.properties.Symbol_file, c.properties.ApiLevel, "")
    275 	c.versionScriptPath = versionScript
    276 	return objs
    277 }
    278 
    279 func (linker *stubDecorator) linkerDeps(ctx DepsContext, deps Deps) Deps {
    280 	return Deps{}
    281 }
    282 
    283 func (linker *stubDecorator) Name(name string) string {
    284 	return name + ndkLibrarySuffix
    285 }
    286 
    287 func (stub *stubDecorator) linkerFlags(ctx ModuleContext, flags Flags) Flags {
    288 	stub.libraryDecorator.libName = ctx.baseModuleName()
    289 	return stub.libraryDecorator.linkerFlags(ctx, flags)
    290 }
    291 
    292 func (stub *stubDecorator) link(ctx ModuleContext, flags Flags, deps PathDeps,
    293 	objs Objects) android.Path {
    294 
    295 	useVersionScript, err := shouldUseVersionScript(stub)
    296 	if err != nil {
    297 		ctx.ModuleErrorf(err.Error())
    298 	}
    299 
    300 	if useVersionScript {
    301 		linkerScriptFlag := "-Wl,--version-script," + stub.versionScriptPath.String()
    302 		flags.LdFlags = append(flags.LdFlags, linkerScriptFlag)
    303 	}
    304 
    305 	return stub.libraryDecorator.link(ctx, flags, deps, objs)
    306 }
    307 
    308 func (stub *stubDecorator) install(ctx ModuleContext, path android.Path) {
    309 	arch := ctx.Target().Arch.ArchType.Name
    310 	apiLevel := stub.properties.ApiLevel
    311 
    312 	// arm64 isn't actually a multilib toolchain, so unlike the other LP64
    313 	// architectures it's just installed to lib.
    314 	libDir := "lib"
    315 	if ctx.toolchain().Is64Bit() && arch != "arm64" {
    316 		libDir = "lib64"
    317 	}
    318 
    319 	installDir := getNdkInstallBase(ctx).Join(ctx, fmt.Sprintf(
    320 		"platforms/android-%s/arch-%s/usr/%s", apiLevel, arch, libDir))
    321 	stub.installPath = ctx.InstallFile(installDir, path).String()
    322 }
    323 
    324 func newStubLibrary() (*Module, []interface{}) {
    325 	module, library := NewLibrary(android.DeviceSupported)
    326 	library.BuildOnlyShared()
    327 	module.stl = nil
    328 	module.sanitize = nil
    329 	library.StripProperties.Strip.None = true
    330 
    331 	stub := &stubDecorator{
    332 		libraryDecorator: library,
    333 	}
    334 	module.compiler = stub
    335 	module.linker = stub
    336 	module.installer = stub
    337 
    338 	return module, []interface{}{&stub.properties, &library.MutatedProperties}
    339 }
    340 
    341 func ndkLibraryFactory() (blueprint.Module, []interface{}) {
    342 	module, properties := newStubLibrary()
    343 	return android.InitAndroidArchModule(module, android.DeviceSupported,
    344 		android.MultilibBoth, properties...)
    345 }
    346