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