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 "path/filepath" 20 "runtime" 21 "strings" 22 "sync" 23 24 "github.com/google/blueprint" 25 "github.com/google/blueprint/proptools" 26 27 "android/soong/android" 28 "android/soong/cc/config" 29) 30 31func init() { 32 pctx.HostBinToolVariable("ndkStubGenerator", "ndkstubgen") 33 pctx.HostBinToolVariable("stg", "stg") 34 pctx.HostBinToolVariable("stgdiff", "stgdiff") 35} 36 37var ( 38 genStubSrc = pctx.AndroidStaticRule("genStubSrc", 39 blueprint.RuleParams{ 40 Command: "$ndkStubGenerator --arch $arch --api $apiLevel " + 41 "--api-map $apiMap $flags $in $out", 42 CommandDeps: []string{"$ndkStubGenerator"}, 43 }, "arch", "apiLevel", "apiMap", "flags") 44 45 // $headersList should include paths to public headers. All types 46 // that are defined outside of public headers will be excluded from 47 // ABI monitoring. 48 // 49 // STG tool doesn't access content of files listed in $headersList, 50 // so there is no need to add them to dependencies. 51 stg = pctx.AndroidStaticRule("stg", 52 blueprint.RuleParams{ 53 Command: "$stg -S :$symbolList --file-filter :$headersList --elf $in -o $out", 54 CommandDeps: []string{"$stg"}, 55 }, "symbolList", "headersList") 56 57 stgdiff = pctx.AndroidStaticRule("stgdiff", 58 blueprint.RuleParams{ 59 // Need to create *some* output for ninja. We don't want to use tee 60 // because we don't want to spam the build output with "nothing 61 // changed" messages, so redirect output message to $out, and if 62 // changes were detected print the output and fail. 63 Command: "$stgdiff $args --stg $in -o $out || (cat $out && echo 'Run $$ANDROID_BUILD_TOP/development/tools/ndk/update_ndk_abi.sh to update the ABI dumps.' && false)", 64 CommandDeps: []string{"$stgdiff"}, 65 }, "args") 66 67 ndkLibrarySuffix = ".ndk" 68 69 ndkKnownLibsKey = android.NewOnceKey("ndkKnownLibsKey") 70 // protects ndkKnownLibs writes during parallel BeginMutator. 71 ndkKnownLibsLock sync.Mutex 72 73 stubImplementation = dependencyTag{name: "stubImplementation"} 74) 75 76// The First_version and Unversioned_until properties of this struct should not 77// be used directly, but rather through the ApiLevel returning methods 78// firstVersion() and unversionedUntil(). 79 80// Creates a stub shared library based on the provided version file. 81// 82// Example: 83// 84// ndk_library { 85// 86// name: "libfoo", 87// symbol_file: "libfoo.map.txt", 88// first_version: "9", 89// 90// } 91type libraryProperties struct { 92 // Relative path to the symbol map. 93 // An example file can be seen here: TODO(danalbert): Make an example. 94 Symbol_file *string `android:"path"` 95 96 // The first API level a library was available. A library will be generated 97 // for every API level beginning with this one. 98 First_version *string 99 100 // The first API level that library should have the version script applied. 101 // This defaults to the value of first_version, and should almost never be 102 // used. This is only needed to work around platform bugs like 103 // https://github.com/android-ndk/ndk/issues/265. 104 Unversioned_until *string 105 106 // Headers presented by this library to the Public API Surface 107 Export_header_libs []string 108} 109 110type stubDecorator struct { 111 *libraryDecorator 112 113 properties libraryProperties 114 115 versionScriptPath android.ModuleGenPath 116 parsedCoverageXmlPath android.ModuleOutPath 117 installPath android.Path 118 abiDumpPath android.OutputPath 119 abiDiffPaths android.Paths 120 121 apiLevel android.ApiLevel 122 firstVersion android.ApiLevel 123 unversionedUntil android.ApiLevel 124} 125 126var _ versionedInterface = (*stubDecorator)(nil) 127 128func shouldUseVersionScript(ctx BaseModuleContext, stub *stubDecorator) bool { 129 return stub.apiLevel.GreaterThanOrEqualTo(stub.unversionedUntil) 130} 131 132func (stub *stubDecorator) implementationModuleName(name string) string { 133 return strings.TrimSuffix(name, ndkLibrarySuffix) 134} 135 136func ndkLibraryVersions(ctx android.BaseMutatorContext, from android.ApiLevel) []string { 137 var versions []android.ApiLevel 138 versionStrs := []string{} 139 for _, version := range ctx.Config().AllSupportedApiLevels() { 140 if version.GreaterThanOrEqualTo(from) { 141 versions = append(versions, version) 142 versionStrs = append(versionStrs, version.String()) 143 } 144 } 145 versionStrs = append(versionStrs, android.FutureApiLevel.String()) 146 147 return versionStrs 148} 149 150func (this *stubDecorator) stubsVersions(ctx android.BaseMutatorContext) []string { 151 if !ctx.Module().Enabled(ctx) { 152 return nil 153 } 154 if ctx.Target().NativeBridge == android.NativeBridgeEnabled { 155 ctx.Module().Disable() 156 return nil 157 } 158 firstVersion, err := nativeApiLevelFromUser(ctx, 159 String(this.properties.First_version)) 160 if err != nil { 161 ctx.PropertyErrorf("first_version", err.Error()) 162 return nil 163 } 164 return ndkLibraryVersions(ctx, firstVersion) 165} 166 167func (this *stubDecorator) initializeProperties(ctx BaseModuleContext) bool { 168 this.apiLevel = nativeApiLevelOrPanic(ctx, this.stubsVersion()) 169 170 var err error 171 this.firstVersion, err = nativeApiLevelFromUser(ctx, 172 String(this.properties.First_version)) 173 if err != nil { 174 ctx.PropertyErrorf("first_version", err.Error()) 175 return false 176 } 177 178 str := proptools.StringDefault(this.properties.Unversioned_until, "minimum") 179 this.unversionedUntil, err = nativeApiLevelFromUser(ctx, str) 180 if err != nil { 181 ctx.PropertyErrorf("unversioned_until", err.Error()) 182 return false 183 } 184 185 return true 186} 187 188func getNDKKnownLibs(config android.Config) *[]string { 189 return config.Once(ndkKnownLibsKey, func() interface{} { 190 return &[]string{} 191 }).(*[]string) 192} 193 194func (c *stubDecorator) compilerInit(ctx BaseModuleContext) { 195 c.baseCompiler.compilerInit(ctx) 196 197 name := ctx.baseModuleName() 198 if strings.HasSuffix(name, ndkLibrarySuffix) { 199 ctx.PropertyErrorf("name", "Do not append %q manually, just use the base name", ndkLibrarySuffix) 200 } 201 202 ndkKnownLibsLock.Lock() 203 defer ndkKnownLibsLock.Unlock() 204 ndkKnownLibs := getNDKKnownLibs(ctx.Config()) 205 for _, lib := range *ndkKnownLibs { 206 if lib == name { 207 return 208 } 209 } 210 *ndkKnownLibs = append(*ndkKnownLibs, name) 211} 212 213var stubLibraryCompilerFlags = []string{ 214 // We're knowingly doing some otherwise unsightly things with builtin 215 // functions here. We're just generating stub libraries, so ignore it. 216 "-Wno-incompatible-library-redeclaration", 217 "-Wno-incomplete-setjmp-declaration", 218 "-Wno-builtin-requires-header", 219 "-Wno-invalid-noreturn", 220 "-Wall", 221 "-Werror", 222 // These libraries aren't actually used. Don't worry about unwinding 223 // (avoids the need to link an unwinder into a fake library). 224 "-fno-unwind-tables", 225} 226 227func init() { 228 pctx.StaticVariable("StubLibraryCompilerFlags", strings.Join(stubLibraryCompilerFlags, " ")) 229} 230 231func addStubLibraryCompilerFlags(flags Flags) Flags { 232 flags.Global.CFlags = append(flags.Global.CFlags, stubLibraryCompilerFlags...) 233 // All symbols in the stubs library should be visible. 234 if inList("-fvisibility=hidden", flags.Local.CFlags) { 235 flags.Local.CFlags = append(flags.Local.CFlags, "-fvisibility=default") 236 } 237 return flags 238} 239 240func (stub *stubDecorator) compilerFlags(ctx ModuleContext, flags Flags, deps PathDeps) Flags { 241 flags = stub.baseCompiler.compilerFlags(ctx, flags, deps) 242 return addStubLibraryCompilerFlags(flags) 243} 244 245type ndkApiOutputs struct { 246 stubSrc android.ModuleGenPath 247 versionScript android.ModuleGenPath 248 symbolList android.ModuleGenPath 249} 250 251func parseNativeAbiDefinition(ctx ModuleContext, symbolFile string, 252 apiLevel android.ApiLevel, genstubFlags string) ndkApiOutputs { 253 254 stubSrcPath := android.PathForModuleGen(ctx, "stub.c") 255 versionScriptPath := android.PathForModuleGen(ctx, "stub.map") 256 symbolFilePath := android.PathForModuleSrc(ctx, symbolFile) 257 symbolListPath := android.PathForModuleGen(ctx, "abi_symbol_list.txt") 258 apiLevelsJson := android.GetApiLevelsJson(ctx) 259 ctx.Build(pctx, android.BuildParams{ 260 Rule: genStubSrc, 261 Description: "generate stubs " + symbolFilePath.Rel(), 262 Outputs: []android.WritablePath{stubSrcPath, versionScriptPath, 263 symbolListPath}, 264 Input: symbolFilePath, 265 Implicits: []android.Path{apiLevelsJson}, 266 Args: map[string]string{ 267 "arch": ctx.Arch().ArchType.String(), 268 "apiLevel": apiLevel.String(), 269 "apiMap": apiLevelsJson.String(), 270 "flags": genstubFlags, 271 }, 272 }) 273 274 return ndkApiOutputs{ 275 stubSrc: stubSrcPath, 276 versionScript: versionScriptPath, 277 symbolList: symbolListPath, 278 } 279} 280 281func compileStubLibrary(ctx ModuleContext, flags Flags, src android.Path) Objects { 282 // libc/libm stubs libraries end up mismatching with clang's internal definition of these 283 // functions (which have noreturn attributes and other things). Because we just want to create a 284 // stub with symbol definitions, and types aren't important in C, ignore the mismatch. 285 flags.Local.ConlyFlags = append(flags.Local.ConlyFlags, "-fno-builtin") 286 return compileObjs(ctx, flagsToBuilderFlags(flags), "", 287 android.Paths{src}, nil, nil, nil, nil) 288} 289 290func (this *stubDecorator) findImplementationLibrary(ctx ModuleContext) android.Path { 291 dep := ctx.GetDirectDepWithTag(strings.TrimSuffix(ctx.ModuleName(), ndkLibrarySuffix), 292 stubImplementation) 293 if dep == nil { 294 ctx.ModuleErrorf("Could not find implementation for stub") 295 return nil 296 } 297 impl, ok := dep.(*Module) 298 if !ok { 299 ctx.ModuleErrorf("Implementation for stub is not correct module type") 300 return nil 301 } 302 output := impl.UnstrippedOutputFile() 303 if output == nil { 304 ctx.ModuleErrorf("implementation module (%s) has no output", impl) 305 return nil 306 } 307 308 return output 309} 310 311func (this *stubDecorator) libraryName(ctx ModuleContext) string { 312 return strings.TrimSuffix(ctx.ModuleName(), ndkLibrarySuffix) 313} 314 315func (this *stubDecorator) findPrebuiltAbiDump(ctx ModuleContext, 316 apiLevel android.ApiLevel) android.OptionalPath { 317 318 subpath := filepath.Join("prebuilts/abi-dumps/ndk", apiLevel.String(), 319 ctx.Arch().ArchType.String(), this.libraryName(ctx), "abi.stg") 320 return android.ExistentPathForSource(ctx, subpath) 321} 322 323// Feature flag. 324func canDumpAbi(config android.Config) bool { 325 if runtime.GOOS == "darwin" { 326 return false 327 } 328 // http://b/156513478 329 return config.ReleaseNdkAbiMonitored() 330} 331 332// Feature flag to disable diffing against prebuilts. 333func canDiffAbi(config android.Config) bool { 334 return config.ReleaseNdkAbiMonitored() 335} 336 337func (this *stubDecorator) dumpAbi(ctx ModuleContext, symbolList android.Path) { 338 implementationLibrary := this.findImplementationLibrary(ctx) 339 this.abiDumpPath = getNdkAbiDumpInstallBase(ctx).Join(ctx, 340 this.apiLevel.String(), ctx.Arch().ArchType.String(), 341 this.libraryName(ctx), "abi.stg") 342 headersList := getNdkABIHeadersFile(ctx) 343 ctx.Build(pctx, android.BuildParams{ 344 Rule: stg, 345 Description: fmt.Sprintf("stg %s", implementationLibrary), 346 Input: implementationLibrary, 347 Implicits: []android.Path{ 348 symbolList, 349 headersList, 350 }, 351 Output: this.abiDumpPath, 352 Args: map[string]string{ 353 "symbolList": symbolList.String(), 354 "headersList": headersList.String(), 355 }, 356 }) 357} 358 359func findNextApiLevel(ctx ModuleContext, apiLevel android.ApiLevel) *android.ApiLevel { 360 apiLevels := append(ctx.Config().AllSupportedApiLevels(), 361 android.FutureApiLevel) 362 for _, api := range apiLevels { 363 if api.GreaterThan(apiLevel) { 364 return &api 365 } 366 } 367 return nil 368} 369 370func (this *stubDecorator) diffAbi(ctx ModuleContext) { 371 // Catch any ABI changes compared to the checked-in definition of this API 372 // level. 373 abiDiffPath := android.PathForModuleOut(ctx, "stgdiff.timestamp") 374 prebuiltAbiDump := this.findPrebuiltAbiDump(ctx, this.apiLevel) 375 missingPrebuiltErrorTemplate := 376 "Did not find prebuilt ABI dump for %q (%q). Generate with " + 377 "//development/tools/ndk/update_ndk_abi.sh." 378 missingPrebuiltError := fmt.Sprintf( 379 missingPrebuiltErrorTemplate, this.libraryName(ctx), 380 prebuiltAbiDump.InvalidReason()) 381 if !prebuiltAbiDump.Valid() { 382 ctx.Build(pctx, android.BuildParams{ 383 Rule: android.ErrorRule, 384 Output: abiDiffPath, 385 Args: map[string]string{ 386 "error": missingPrebuiltError, 387 }, 388 }) 389 } else { 390 ctx.Build(pctx, android.BuildParams{ 391 Rule: stgdiff, 392 Description: fmt.Sprintf("Comparing ABI %s %s", prebuiltAbiDump, 393 this.abiDumpPath), 394 Output: abiDiffPath, 395 Inputs: android.Paths{prebuiltAbiDump.Path(), this.abiDumpPath}, 396 Args: map[string]string{ 397 "args": "--format=small", 398 }, 399 }) 400 } 401 this.abiDiffPaths = append(this.abiDiffPaths, abiDiffPath) 402 403 // Also ensure that the ABI of the next API level (if there is one) matches 404 // this API level. *New* ABI is allowed, but any changes to APIs that exist 405 // in this API level are disallowed. 406 if !this.apiLevel.IsCurrent() && prebuiltAbiDump.Valid() { 407 nextApiLevel := findNextApiLevel(ctx, this.apiLevel) 408 if nextApiLevel == nil { 409 panic(fmt.Errorf("could not determine which API level follows "+ 410 "non-current API level %s", this.apiLevel)) 411 } 412 nextAbiDiffPath := android.PathForModuleOut(ctx, 413 "abidiff_next.timestamp") 414 nextAbiDump := this.findPrebuiltAbiDump(ctx, *nextApiLevel) 415 missingNextPrebuiltError := fmt.Sprintf( 416 missingPrebuiltErrorTemplate, this.libraryName(ctx), 417 nextAbiDump.InvalidReason()) 418 if !nextAbiDump.Valid() { 419 ctx.Build(pctx, android.BuildParams{ 420 Rule: android.ErrorRule, 421 Output: nextAbiDiffPath, 422 Args: map[string]string{ 423 "error": missingNextPrebuiltError, 424 }, 425 }) 426 } else { 427 ctx.Build(pctx, android.BuildParams{ 428 Rule: stgdiff, 429 Description: fmt.Sprintf( 430 "Comparing ABI to the next API level %s %s", 431 prebuiltAbiDump, nextAbiDump), 432 Output: nextAbiDiffPath, 433 Inputs: android.Paths{ 434 prebuiltAbiDump.Path(), nextAbiDump.Path()}, 435 Args: map[string]string{ 436 "args": "--format=small --ignore=interface_addition", 437 }, 438 }) 439 } 440 this.abiDiffPaths = append(this.abiDiffPaths, nextAbiDiffPath) 441 } 442} 443 444func (c *stubDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) Objects { 445 if !strings.HasSuffix(String(c.properties.Symbol_file), ".map.txt") { 446 ctx.PropertyErrorf("symbol_file", "must end with .map.txt") 447 } 448 449 if !c.buildStubs() { 450 // NDK libraries have no implementation variant, nothing to do 451 return Objects{} 452 } 453 454 if !c.initializeProperties(ctx) { 455 // Emits its own errors, so we don't need to. 456 return Objects{} 457 } 458 459 symbolFile := String(c.properties.Symbol_file) 460 nativeAbiResult := parseNativeAbiDefinition(ctx, symbolFile, c.apiLevel, "") 461 objs := compileStubLibrary(ctx, flags, nativeAbiResult.stubSrc) 462 c.versionScriptPath = nativeAbiResult.versionScript 463 if canDumpAbi(ctx.Config()) { 464 c.dumpAbi(ctx, nativeAbiResult.symbolList) 465 if canDiffAbi(ctx.Config()) { 466 c.diffAbi(ctx) 467 } 468 } 469 if c.apiLevel.IsCurrent() && ctx.PrimaryArch() { 470 c.parsedCoverageXmlPath = parseSymbolFileForAPICoverage(ctx, symbolFile) 471 } 472 return objs 473} 474 475// Add a dependency on the header modules of this ndk_library 476func (linker *stubDecorator) linkerDeps(ctx DepsContext, deps Deps) Deps { 477 return Deps{ 478 HeaderLibs: linker.properties.Export_header_libs, 479 } 480} 481 482func (linker *stubDecorator) moduleInfoJSON(ctx ModuleContext, moduleInfoJSON *android.ModuleInfoJSON) { 483 linker.libraryDecorator.moduleInfoJSON(ctx, moduleInfoJSON) 484 // Overwrites the SubName computed by libraryDecorator 485 moduleInfoJSON.SubName = ndkLibrarySuffix + "." + linker.apiLevel.String() 486} 487 488func (linker *stubDecorator) Name(name string) string { 489 return name + ndkLibrarySuffix 490} 491 492func (stub *stubDecorator) linkerFlags(ctx ModuleContext, flags Flags) Flags { 493 stub.libraryDecorator.libName = ctx.baseModuleName() 494 return stub.libraryDecorator.linkerFlags(ctx, flags) 495} 496 497func (stub *stubDecorator) link(ctx ModuleContext, flags Flags, deps PathDeps, 498 objs Objects) android.Path { 499 500 if !stub.buildStubs() { 501 // NDK libraries have no implementation variant, nothing to do 502 return nil 503 } 504 505 if shouldUseVersionScript(ctx, stub) { 506 linkerScriptFlag := "-Wl,--version-script," + stub.versionScriptPath.String() 507 flags.Local.LdFlags = append(flags.Local.LdFlags, linkerScriptFlag) 508 flags.LdFlagsDeps = append(flags.LdFlagsDeps, stub.versionScriptPath) 509 } 510 511 stub.libraryDecorator.skipAPIDefine = true 512 return stub.libraryDecorator.link(ctx, flags, deps, objs) 513} 514 515func (stub *stubDecorator) nativeCoverage() bool { 516 return false 517} 518 519// Returns the install path for unversioned NDK libraries (currently only static 520// libraries). 521func getUnversionedLibraryInstallPath(ctx ModuleContext) android.OutputPath { 522 return getNdkSysrootBase(ctx).Join(ctx, "usr/lib", config.NDKTriple(ctx.toolchain())) 523} 524 525// Returns the install path for versioned NDK libraries. These are most often 526// stubs, but the same paths are used for CRT objects. 527func getVersionedLibraryInstallPath(ctx ModuleContext, apiLevel android.ApiLevel) android.OutputPath { 528 return getUnversionedLibraryInstallPath(ctx).Join(ctx, apiLevel.String()) 529} 530 531func (stub *stubDecorator) install(ctx ModuleContext, path android.Path) { 532 installDir := getVersionedLibraryInstallPath(ctx, stub.apiLevel) 533 out := installDir.Join(ctx, path.Base()) 534 ctx.Build(pctx, android.BuildParams{ 535 Rule: android.Cp, 536 Input: path, 537 Output: out, 538 }) 539 stub.installPath = out 540} 541 542func newStubLibrary() *Module { 543 module, library := NewLibrary(android.DeviceSupported) 544 library.BuildOnlyShared() 545 module.stl = nil 546 module.sanitize = nil 547 library.disableStripping() 548 549 stub := &stubDecorator{ 550 libraryDecorator: library, 551 } 552 module.compiler = stub 553 module.linker = stub 554 module.installer = stub 555 module.library = stub 556 557 module.Properties.AlwaysSdk = true 558 module.Properties.Sdk_version = StringPtr("current") 559 560 module.AddProperties(&stub.properties, &library.MutatedProperties) 561 562 return module 563} 564 565// ndk_library creates a library that exposes a stub implementation of functions 566// and variables for use at build time only. 567func NdkLibraryFactory() android.Module { 568 module := newStubLibrary() 569 android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibBoth) 570 return module 571} 572