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