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 "strings" 20 "sync" 21 22 "github.com/google/blueprint" 23 "github.com/google/blueprint/proptools" 24 25 "android/soong/android" 26) 27 28func init() { 29 pctx.HostBinToolVariable("ndkStubGenerator", "ndkstubgen") 30 pctx.HostBinToolVariable("ndk_api_coverage_parser", "ndk_api_coverage_parser") 31} 32 33var ( 34 genStubSrc = pctx.AndroidStaticRule("genStubSrc", 35 blueprint.RuleParams{ 36 Command: "$ndkStubGenerator --arch $arch --api $apiLevel " + 37 "--api-map $apiMap $flags $in $out", 38 CommandDeps: []string{"$ndkStubGenerator"}, 39 }, "arch", "apiLevel", "apiMap", "flags") 40 41 parseNdkApiRule = pctx.AndroidStaticRule("parseNdkApiRule", 42 blueprint.RuleParams{ 43 Command: "$ndk_api_coverage_parser $in $out --api-map $apiMap", 44 CommandDeps: []string{"$ndk_api_coverage_parser"}, 45 }, "apiMap") 46 47 ndkLibrarySuffix = ".ndk" 48 49 ndkKnownLibsKey = android.NewOnceKey("ndkKnownLibsKey") 50 // protects ndkKnownLibs writes during parallel BeginMutator. 51 ndkKnownLibsLock sync.Mutex 52) 53 54// The First_version and Unversioned_until properties of this struct should not 55// be used directly, but rather through the ApiLevel returning methods 56// firstVersion() and unversionedUntil(). 57 58// Creates a stub shared library based on the provided version file. 59// 60// Example: 61// 62// ndk_library { 63// name: "libfoo", 64// symbol_file: "libfoo.map.txt", 65// first_version: "9", 66// } 67// 68type libraryProperties struct { 69 // Relative path to the symbol map. 70 // An example file can be seen here: TODO(danalbert): Make an example. 71 Symbol_file *string 72 73 // The first API level a library was available. A library will be generated 74 // for every API level beginning with this one. 75 First_version *string 76 77 // The first API level that library should have the version script applied. 78 // This defaults to the value of first_version, and should almost never be 79 // used. This is only needed to work around platform bugs like 80 // https://github.com/android-ndk/ndk/issues/265. 81 Unversioned_until *string 82} 83 84type stubDecorator struct { 85 *libraryDecorator 86 87 properties libraryProperties 88 89 versionScriptPath android.ModuleGenPath 90 parsedCoverageXmlPath android.ModuleOutPath 91 installPath android.Path 92 93 apiLevel android.ApiLevel 94 firstVersion android.ApiLevel 95 unversionedUntil android.ApiLevel 96} 97 98var _ versionedInterface = (*stubDecorator)(nil) 99 100func shouldUseVersionScript(ctx BaseModuleContext, stub *stubDecorator) bool { 101 return stub.apiLevel.GreaterThanOrEqualTo(stub.unversionedUntil) 102} 103 104func (stub *stubDecorator) implementationModuleName(name string) string { 105 return strings.TrimSuffix(name, ndkLibrarySuffix) 106} 107 108func ndkLibraryVersions(ctx android.BaseMutatorContext, from android.ApiLevel) []string { 109 var versions []android.ApiLevel 110 versionStrs := []string{} 111 for _, version := range ctx.Config().AllSupportedApiLevels() { 112 if version.GreaterThanOrEqualTo(from) { 113 versions = append(versions, version) 114 versionStrs = append(versionStrs, version.String()) 115 } 116 } 117 versionStrs = append(versionStrs, android.FutureApiLevel.String()) 118 119 return versionStrs 120} 121 122func (this *stubDecorator) stubsVersions(ctx android.BaseMutatorContext) []string { 123 if !ctx.Module().Enabled() { 124 return nil 125 } 126 firstVersion, err := nativeApiLevelFromUser(ctx, 127 String(this.properties.First_version)) 128 if err != nil { 129 ctx.PropertyErrorf("first_version", err.Error()) 130 return nil 131 } 132 return ndkLibraryVersions(ctx, firstVersion) 133} 134 135func (this *stubDecorator) initializeProperties(ctx BaseModuleContext) bool { 136 this.apiLevel = nativeApiLevelOrPanic(ctx, this.stubsVersion()) 137 138 var err error 139 this.firstVersion, err = nativeApiLevelFromUser(ctx, 140 String(this.properties.First_version)) 141 if err != nil { 142 ctx.PropertyErrorf("first_version", err.Error()) 143 return false 144 } 145 146 str := proptools.StringDefault(this.properties.Unversioned_until, "minimum") 147 this.unversionedUntil, err = nativeApiLevelFromUser(ctx, str) 148 if err != nil { 149 ctx.PropertyErrorf("unversioned_until", err.Error()) 150 return false 151 } 152 153 return true 154} 155 156func getNDKKnownLibs(config android.Config) *[]string { 157 return config.Once(ndkKnownLibsKey, func() interface{} { 158 return &[]string{} 159 }).(*[]string) 160} 161 162func (c *stubDecorator) compilerInit(ctx BaseModuleContext) { 163 c.baseCompiler.compilerInit(ctx) 164 165 name := ctx.baseModuleName() 166 if strings.HasSuffix(name, ndkLibrarySuffix) { 167 ctx.PropertyErrorf("name", "Do not append %q manually, just use the base name", ndkLibrarySuffix) 168 } 169 170 ndkKnownLibsLock.Lock() 171 defer ndkKnownLibsLock.Unlock() 172 ndkKnownLibs := getNDKKnownLibs(ctx.Config()) 173 for _, lib := range *ndkKnownLibs { 174 if lib == name { 175 return 176 } 177 } 178 *ndkKnownLibs = append(*ndkKnownLibs, name) 179} 180 181func addStubLibraryCompilerFlags(flags Flags) Flags { 182 flags.Global.CFlags = append(flags.Global.CFlags, 183 // We're knowingly doing some otherwise unsightly things with builtin 184 // functions here. We're just generating stub libraries, so ignore it. 185 "-Wno-incompatible-library-redeclaration", 186 "-Wno-incomplete-setjmp-declaration", 187 "-Wno-builtin-requires-header", 188 "-Wno-invalid-noreturn", 189 "-Wall", 190 "-Werror", 191 // These libraries aren't actually used. Don't worry about unwinding 192 // (avoids the need to link an unwinder into a fake library). 193 "-fno-unwind-tables", 194 ) 195 // All symbols in the stubs library should be visible. 196 if inList("-fvisibility=hidden", flags.Local.CFlags) { 197 flags.Local.CFlags = append(flags.Local.CFlags, "-fvisibility=default") 198 } 199 return flags 200} 201 202func (stub *stubDecorator) compilerFlags(ctx ModuleContext, flags Flags, deps PathDeps) Flags { 203 flags = stub.baseCompiler.compilerFlags(ctx, flags, deps) 204 return addStubLibraryCompilerFlags(flags) 205} 206 207func compileStubLibrary(ctx ModuleContext, flags Flags, symbolFile, apiLevel, genstubFlags string) (Objects, android.ModuleGenPath) { 208 arch := ctx.Arch().ArchType.String() 209 210 stubSrcPath := android.PathForModuleGen(ctx, "stub.c") 211 versionScriptPath := android.PathForModuleGen(ctx, "stub.map") 212 symbolFilePath := android.PathForModuleSrc(ctx, symbolFile) 213 apiLevelsJson := android.GetApiLevelsJson(ctx) 214 ctx.Build(pctx, android.BuildParams{ 215 Rule: genStubSrc, 216 Description: "generate stubs " + symbolFilePath.Rel(), 217 Outputs: []android.WritablePath{stubSrcPath, versionScriptPath}, 218 Input: symbolFilePath, 219 Implicits: []android.Path{apiLevelsJson}, 220 Args: map[string]string{ 221 "arch": arch, 222 "apiLevel": apiLevel, 223 "apiMap": apiLevelsJson.String(), 224 "flags": genstubFlags, 225 }, 226 }) 227 228 subdir := "" 229 srcs := []android.Path{stubSrcPath} 230 return compileObjs(ctx, flagsToBuilderFlags(flags), subdir, srcs, nil, nil), versionScriptPath 231} 232 233func parseSymbolFileForCoverage(ctx ModuleContext, symbolFile string) android.ModuleOutPath { 234 apiLevelsJson := android.GetApiLevelsJson(ctx) 235 symbolFilePath := android.PathForModuleSrc(ctx, symbolFile) 236 outputFileName := strings.Split(symbolFilePath.Base(), ".")[0] 237 parsedApiCoveragePath := android.PathForModuleOut(ctx, outputFileName+".xml") 238 ctx.Build(pctx, android.BuildParams{ 239 Rule: parseNdkApiRule, 240 Description: "parse ndk api symbol file for api coverage: " + symbolFilePath.Rel(), 241 Outputs: []android.WritablePath{parsedApiCoveragePath}, 242 Input: symbolFilePath, 243 Implicits: []android.Path{apiLevelsJson}, 244 Args: map[string]string{ 245 "apiMap": apiLevelsJson.String(), 246 }, 247 }) 248 return parsedApiCoveragePath 249} 250 251func (c *stubDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) Objects { 252 if !strings.HasSuffix(String(c.properties.Symbol_file), ".map.txt") { 253 ctx.PropertyErrorf("symbol_file", "must end with .map.txt") 254 } 255 256 if !c.buildStubs() { 257 // NDK libraries have no implementation variant, nothing to do 258 return Objects{} 259 } 260 261 if !c.initializeProperties(ctx) { 262 // Emits its own errors, so we don't need to. 263 return Objects{} 264 } 265 266 symbolFile := String(c.properties.Symbol_file) 267 objs, versionScript := compileStubLibrary(ctx, flags, symbolFile, 268 c.apiLevel.String(), "") 269 c.versionScriptPath = versionScript 270 if c.apiLevel.IsCurrent() && ctx.PrimaryArch() { 271 c.parsedCoverageXmlPath = parseSymbolFileForCoverage(ctx, symbolFile) 272 } 273 return objs 274} 275 276func (linker *stubDecorator) linkerDeps(ctx DepsContext, deps Deps) Deps { 277 return Deps{} 278} 279 280func (linker *stubDecorator) Name(name string) string { 281 return name + ndkLibrarySuffix 282} 283 284func (stub *stubDecorator) linkerFlags(ctx ModuleContext, flags Flags) Flags { 285 stub.libraryDecorator.libName = ctx.baseModuleName() 286 return stub.libraryDecorator.linkerFlags(ctx, flags) 287} 288 289func (stub *stubDecorator) link(ctx ModuleContext, flags Flags, deps PathDeps, 290 objs Objects) android.Path { 291 292 if !stub.buildStubs() { 293 // NDK libraries have no implementation variant, nothing to do 294 return nil 295 } 296 297 if shouldUseVersionScript(ctx, stub) { 298 linkerScriptFlag := "-Wl,--version-script," + stub.versionScriptPath.String() 299 flags.Local.LdFlags = append(flags.Local.LdFlags, linkerScriptFlag) 300 flags.LdFlagsDeps = append(flags.LdFlagsDeps, stub.versionScriptPath) 301 } 302 303 stub.libraryDecorator.skipAPIDefine = true 304 return stub.libraryDecorator.link(ctx, flags, deps, objs) 305} 306 307func (stub *stubDecorator) nativeCoverage() bool { 308 return false 309} 310 311func (stub *stubDecorator) install(ctx ModuleContext, path android.Path) { 312 arch := ctx.Target().Arch.ArchType.Name 313 // arm64 isn't actually a multilib toolchain, so unlike the other LP64 314 // architectures it's just installed to lib. 315 libDir := "lib" 316 if ctx.toolchain().Is64Bit() && arch != "arm64" { 317 libDir = "lib64" 318 } 319 320 installDir := getNdkInstallBase(ctx).Join(ctx, fmt.Sprintf( 321 "platforms/android-%s/arch-%s/usr/%s", stub.apiLevel, arch, libDir)) 322 stub.installPath = ctx.InstallFile(installDir, path.Base(), path) 323} 324 325func newStubLibrary() *Module { 326 module, library := NewLibrary(android.DeviceSupported) 327 library.BuildOnlyShared() 328 module.stl = nil 329 module.sanitize = nil 330 library.disableStripping() 331 332 stub := &stubDecorator{ 333 libraryDecorator: library, 334 } 335 module.compiler = stub 336 module.linker = stub 337 module.installer = stub 338 module.library = stub 339 340 module.Properties.AlwaysSdk = true 341 module.Properties.Sdk_version = StringPtr("current") 342 343 module.AddProperties(&stub.properties, &library.MutatedProperties) 344 345 return module 346} 347 348// ndk_library creates a library that exposes a stub implementation of functions 349// and variables for use at build time only. 350func NdkLibraryFactory() android.Module { 351 module := newStubLibrary() 352 android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibBoth) 353 return module 354} 355