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