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