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 // DO NOT USE THIS 107 // NDK libraries should not export their headers. Headers belonging to NDK 108 // libraries should be added to the NDK with an ndk_headers module. 109 Export_header_libs []string 110 111 // Do not add other export_* properties without consulting with danalbert@. 112 // Consumers of ndk_library modules should emulate the typical NDK build 113 // behavior as closely as possible (that is, all NDK APIs are exposed to 114 // builds via --sysroot). Export behaviors used in Soong will not be present 115 // for app developers as they don't use Soong, and reliance on these export 116 // behaviors can mask issues with the NDK sysroot. 117} 118 119type stubDecorator struct { 120 *libraryDecorator 121 122 properties libraryProperties 123 124 versionScriptPath android.ModuleGenPath 125 parsedCoverageXmlPath android.ModuleOutPath 126 installPath android.Path 127 abiDumpPath android.OutputPath 128 hasAbiDump bool 129 abiDiffPaths android.Paths 130 131 apiLevel android.ApiLevel 132 firstVersion android.ApiLevel 133 unversionedUntil android.ApiLevel 134} 135 136var _ versionedInterface = (*stubDecorator)(nil) 137 138func shouldUseVersionScript(ctx BaseModuleContext, stub *stubDecorator) bool { 139 return stub.apiLevel.GreaterThanOrEqualTo(stub.unversionedUntil) 140} 141 142func (stub *stubDecorator) implementationModuleName(name string) string { 143 return strings.TrimSuffix(name, ndkLibrarySuffix) 144} 145 146func ndkLibraryVersions(ctx android.BaseModuleContext, from android.ApiLevel) []string { 147 versionStrs := []string{} 148 for _, version := range ctx.Config().FinalApiLevels() { 149 if version.GreaterThanOrEqualTo(from) { 150 versionStrs = append(versionStrs, version.String()) 151 } 152 } 153 versionStrs = append(versionStrs, android.FutureApiLevel.String()) 154 155 return versionStrs 156} 157 158func (this *stubDecorator) stubsVersions(ctx android.BaseModuleContext) []string { 159 if !ctx.Module().Enabled(ctx) { 160 return nil 161 } 162 if ctx.Target().NativeBridge == android.NativeBridgeEnabled { 163 ctx.Module().Disable() 164 return nil 165 } 166 firstVersion, err := nativeApiLevelFromUser(ctx, 167 String(this.properties.First_version)) 168 if err != nil { 169 ctx.PropertyErrorf("first_version", err.Error()) 170 return nil 171 } 172 return ndkLibraryVersions(ctx, firstVersion) 173} 174 175func (this *stubDecorator) initializeProperties(ctx BaseModuleContext) bool { 176 this.apiLevel = nativeApiLevelOrPanic(ctx, this.stubsVersion()) 177 178 var err error 179 this.firstVersion, err = nativeApiLevelFromUser(ctx, 180 String(this.properties.First_version)) 181 if err != nil { 182 ctx.PropertyErrorf("first_version", err.Error()) 183 return false 184 } 185 186 str := proptools.StringDefault(this.properties.Unversioned_until, "minimum") 187 this.unversionedUntil, err = nativeApiLevelFromUser(ctx, str) 188 if err != nil { 189 ctx.PropertyErrorf("unversioned_until", err.Error()) 190 return false 191 } 192 193 return true 194} 195 196func getNDKKnownLibs(config android.Config) *[]string { 197 return config.Once(ndkKnownLibsKey, func() interface{} { 198 return &[]string{} 199 }).(*[]string) 200} 201 202func (c *stubDecorator) compilerInit(ctx BaseModuleContext) { 203 c.baseCompiler.compilerInit(ctx) 204 205 name := ctx.baseModuleName() 206 if strings.HasSuffix(name, ndkLibrarySuffix) { 207 ctx.PropertyErrorf("name", "Do not append %q manually, just use the base name", ndkLibrarySuffix) 208 } 209 210 ndkKnownLibsLock.Lock() 211 defer ndkKnownLibsLock.Unlock() 212 ndkKnownLibs := getNDKKnownLibs(ctx.Config()) 213 for _, lib := range *ndkKnownLibs { 214 if lib == name { 215 return 216 } 217 } 218 *ndkKnownLibs = append(*ndkKnownLibs, name) 219} 220 221var stubLibraryCompilerFlags = []string{ 222 // We're knowingly doing some otherwise unsightly things with builtin 223 // functions here. We're just generating stub libraries, so ignore it. 224 "-Wno-incompatible-library-redeclaration", 225 "-Wno-incomplete-setjmp-declaration", 226 "-Wno-builtin-requires-header", 227 "-Wno-invalid-noreturn", 228 "-Wall", 229 "-Werror", 230 // These libraries aren't actually used. Don't worry about unwinding 231 // (avoids the need to link an unwinder into a fake library). 232 "-fno-unwind-tables", 233} 234 235func init() { 236 pctx.StaticVariable("StubLibraryCompilerFlags", strings.Join(stubLibraryCompilerFlags, " ")) 237} 238 239func addStubLibraryCompilerFlags(flags Flags) Flags { 240 flags.Global.CFlags = append(flags.Global.CFlags, stubLibraryCompilerFlags...) 241 // All symbols in the stubs library should be visible. 242 if inList("-fvisibility=hidden", flags.Local.CFlags) { 243 flags.Local.CFlags = append(flags.Local.CFlags, "-fvisibility=default") 244 } 245 return flags 246} 247 248func (stub *stubDecorator) compilerFlags(ctx ModuleContext, flags Flags, deps PathDeps) Flags { 249 flags = stub.baseCompiler.compilerFlags(ctx, flags, deps) 250 return addStubLibraryCompilerFlags(flags) 251} 252 253type ndkApiOutputs struct { 254 stubSrc android.ModuleGenPath 255 versionScript android.ModuleGenPath 256 symbolList android.ModuleGenPath 257} 258 259func parseNativeAbiDefinition(ctx ModuleContext, symbolFile string, 260 apiLevel android.ApiLevel, genstubFlags string) ndkApiOutputs { 261 262 stubSrcPath := android.PathForModuleGen(ctx, "stub.c") 263 versionScriptPath := android.PathForModuleGen(ctx, "stub.map") 264 symbolFilePath := android.PathForModuleSrc(ctx, symbolFile) 265 symbolListPath := android.PathForModuleGen(ctx, "abi_symbol_list.txt") 266 apiLevelsJson := android.GetApiLevelsJson(ctx) 267 ctx.Build(pctx, android.BuildParams{ 268 Rule: genStubSrc, 269 Description: "generate stubs " + symbolFilePath.Rel(), 270 Outputs: []android.WritablePath{stubSrcPath, versionScriptPath, 271 symbolListPath}, 272 Input: symbolFilePath, 273 Implicits: []android.Path{apiLevelsJson}, 274 Args: map[string]string{ 275 "arch": ctx.Arch().ArchType.String(), 276 "apiLevel": apiLevel.String(), 277 "apiMap": apiLevelsJson.String(), 278 "flags": genstubFlags, 279 }, 280 }) 281 282 return ndkApiOutputs{ 283 stubSrc: stubSrcPath, 284 versionScript: versionScriptPath, 285 symbolList: symbolListPath, 286 } 287} 288 289func compileStubLibrary(ctx ModuleContext, flags Flags, src android.Path) Objects { 290 // libc/libm stubs libraries end up mismatching with clang's internal definition of these 291 // functions (which have noreturn attributes and other things). Because we just want to create a 292 // stub with symbol definitions, and types aren't important in C, ignore the mismatch. 293 flags.Local.ConlyFlags = append(flags.Local.ConlyFlags, "-fno-builtin") 294 return compileObjs(ctx, flagsToBuilderFlags(flags), "", 295 android.Paths{src}, nil, nil, nil, nil) 296} 297 298func (this *stubDecorator) findImplementationLibrary(ctx ModuleContext) android.Path { 299 dep := ctx.GetDirectDepWithTag(strings.TrimSuffix(ctx.ModuleName(), ndkLibrarySuffix), 300 stubImplementation) 301 if dep == nil { 302 ctx.ModuleErrorf("Could not find implementation for stub") 303 return nil 304 } 305 impl, ok := dep.(*Module) 306 if !ok { 307 ctx.ModuleErrorf("Implementation for stub is not correct module type") 308 return nil 309 } 310 output := impl.UnstrippedOutputFile() 311 if output == nil { 312 ctx.ModuleErrorf("implementation module (%s) has no output", impl) 313 return nil 314 } 315 316 return output 317} 318 319func (this *stubDecorator) libraryName(ctx ModuleContext) string { 320 return strings.TrimSuffix(ctx.ModuleName(), ndkLibrarySuffix) 321} 322 323func (this *stubDecorator) findPrebuiltAbiDump(ctx ModuleContext, 324 apiLevel android.ApiLevel) android.OptionalPath { 325 326 subpath := filepath.Join("prebuilts/abi-dumps/ndk", apiLevel.String(), 327 ctx.Arch().ArchType.String(), this.libraryName(ctx), "abi.stg") 328 return android.ExistentPathForSource(ctx, subpath) 329} 330 331func (this *stubDecorator) builtAbiDumpLocation(ctx ModuleContext, apiLevel android.ApiLevel) android.OutputPath { 332 return getNdkAbiDumpInstallBase(ctx).Join(ctx, 333 apiLevel.String(), ctx.Arch().ArchType.String(), 334 this.libraryName(ctx), "abi.stg") 335} 336 337// Feature flag. 338func (this *stubDecorator) canDumpAbi(ctx ModuleContext) bool { 339 if runtime.GOOS == "darwin" { 340 return false 341 } 342 if strings.HasPrefix(ctx.ModuleDir(), "bionic/") { 343 // Bionic has enough uncommon implementation details like ifuncs and asm 344 // code that the ABI tracking here has a ton of false positives. That's 345 // causing pretty extreme friction for development there, so disabling 346 // it until the workflow can be improved. 347 // 348 // http://b/358653811 349 return false 350 } 351 352 // http://b/156513478 353 return ctx.Config().ReleaseNdkAbiMonitored() 354} 355 356// Feature flag to disable diffing against prebuilts. 357func (this *stubDecorator) canDiffAbi(config android.Config) bool { 358 if this.apiLevel.IsCurrent() { 359 // Diffs are performed from this to next, and there's nothing after 360 // current. 361 return false 362 } 363 364 return config.ReleaseNdkAbiMonitored() 365} 366 367func (this *stubDecorator) dumpAbi(ctx ModuleContext, symbolList android.Path) { 368 implementationLibrary := this.findImplementationLibrary(ctx) 369 this.abiDumpPath = this.builtAbiDumpLocation(ctx, this.apiLevel) 370 this.hasAbiDump = true 371 headersList := getNdkABIHeadersFile(ctx) 372 ctx.Build(pctx, android.BuildParams{ 373 Rule: stg, 374 Description: fmt.Sprintf("stg %s", implementationLibrary), 375 Input: implementationLibrary, 376 Implicits: []android.Path{ 377 symbolList, 378 headersList, 379 }, 380 Output: this.abiDumpPath, 381 Args: map[string]string{ 382 "symbolList": symbolList.String(), 383 "headersList": headersList.String(), 384 }, 385 }) 386} 387 388func findNextApiLevel(ctx ModuleContext, apiLevel android.ApiLevel) *android.ApiLevel { 389 apiLevels := append(ctx.Config().FinalApiLevels(), 390 android.FutureApiLevel) 391 for _, api := range apiLevels { 392 if api.GreaterThan(apiLevel) { 393 return &api 394 } 395 } 396 return nil 397} 398 399func (this *stubDecorator) diffAbi(ctx ModuleContext) { 400 // Catch any ABI changes compared to the checked-in definition of this API 401 // level. 402 abiDiffPath := android.PathForModuleOut(ctx, "stgdiff.timestamp") 403 prebuiltAbiDump := this.findPrebuiltAbiDump(ctx, this.apiLevel) 404 missingPrebuiltErrorTemplate := 405 "Did not find prebuilt ABI dump for %q (%q). Generate with " + 406 "//development/tools/ndk/update_ndk_abi.sh." 407 missingPrebuiltError := fmt.Sprintf( 408 missingPrebuiltErrorTemplate, this.libraryName(ctx), 409 prebuiltAbiDump.InvalidReason()) 410 if !prebuiltAbiDump.Valid() { 411 ctx.Build(pctx, android.BuildParams{ 412 Rule: android.ErrorRule, 413 Output: abiDiffPath, 414 Args: map[string]string{ 415 "error": missingPrebuiltError, 416 }, 417 }) 418 } else { 419 ctx.Build(pctx, android.BuildParams{ 420 Rule: stgdiff, 421 Description: fmt.Sprintf("Comparing ABI %s %s", prebuiltAbiDump, 422 this.abiDumpPath), 423 Output: abiDiffPath, 424 Inputs: android.Paths{prebuiltAbiDump.Path(), this.abiDumpPath}, 425 Args: map[string]string{ 426 "args": "--format=small", 427 }, 428 }) 429 } 430 this.abiDiffPaths = append(this.abiDiffPaths, abiDiffPath) 431 432 // Also ensure that the ABI of the next API level (if there is one) matches 433 // this API level. *New* ABI is allowed, but any changes to APIs that exist 434 // in this API level are disallowed. 435 if prebuiltAbiDump.Valid() { 436 nextApiLevel := findNextApiLevel(ctx, this.apiLevel) 437 if nextApiLevel == nil { 438 panic(fmt.Errorf("could not determine which API level follows "+ 439 "non-current API level %s", this.apiLevel)) 440 } 441 442 // Preview ABI levels are not recorded in prebuilts. ABI compatibility 443 // for preview APIs is still monitored via "current" so we have early 444 // warning rather than learning about an ABI break during finalization, 445 // but is only checked against the "current" API dumps in the out 446 // directory. 447 nextAbiDiffPath := android.PathForModuleOut(ctx, 448 "abidiff_next.timestamp") 449 450 var nextAbiDump android.OptionalPath 451 if nextApiLevel.IsCurrent() { 452 nextAbiDump = android.OptionalPathForPath( 453 this.builtAbiDumpLocation(ctx, *nextApiLevel), 454 ) 455 } else { 456 nextAbiDump = this.findPrebuiltAbiDump(ctx, *nextApiLevel) 457 } 458 459 if !nextAbiDump.Valid() { 460 missingNextPrebuiltError := fmt.Sprintf( 461 missingPrebuiltErrorTemplate, this.libraryName(ctx), 462 nextAbiDump.InvalidReason()) 463 ctx.Build(pctx, android.BuildParams{ 464 Rule: android.ErrorRule, 465 Output: nextAbiDiffPath, 466 Args: map[string]string{ 467 "error": missingNextPrebuiltError, 468 }, 469 }) 470 } else { 471 ctx.Build(pctx, android.BuildParams{ 472 Rule: stgdiff, 473 Description: fmt.Sprintf( 474 "Comparing ABI to the next API level %s %s", 475 prebuiltAbiDump, nextAbiDump), 476 Output: nextAbiDiffPath, 477 Inputs: android.Paths{ 478 prebuiltAbiDump.Path(), nextAbiDump.Path()}, 479 Args: map[string]string{ 480 "args": "--format=small --ignore=interface_addition", 481 }, 482 }) 483 } 484 this.abiDiffPaths = append(this.abiDiffPaths, nextAbiDiffPath) 485 } 486} 487 488func (c *stubDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) Objects { 489 if !strings.HasSuffix(String(c.properties.Symbol_file), ".map.txt") { 490 ctx.PropertyErrorf("symbol_file", "must end with .map.txt") 491 } 492 493 if !c.buildStubs() { 494 // NDK libraries have no implementation variant, nothing to do 495 return Objects{} 496 } 497 498 if !c.initializeProperties(ctx) { 499 // Emits its own errors, so we don't need to. 500 return Objects{} 501 } 502 503 symbolFile := String(c.properties.Symbol_file) 504 nativeAbiResult := parseNativeAbiDefinition(ctx, symbolFile, c.apiLevel, "") 505 objs := compileStubLibrary(ctx, flags, nativeAbiResult.stubSrc) 506 c.versionScriptPath = nativeAbiResult.versionScript 507 if c.canDumpAbi(ctx) { 508 c.dumpAbi(ctx, nativeAbiResult.symbolList) 509 if c.canDiffAbi(ctx.Config()) { 510 c.diffAbi(ctx) 511 } 512 } 513 if c.apiLevel.IsCurrent() && ctx.PrimaryArch() { 514 c.parsedCoverageXmlPath = parseSymbolFileForAPICoverage(ctx, symbolFile) 515 } 516 return objs 517} 518 519// Add a dependency on the header modules of this ndk_library 520func (linker *stubDecorator) linkerDeps(ctx DepsContext, deps Deps) Deps { 521 return Deps{ 522 ReexportHeaderLibHeaders: linker.properties.Export_header_libs, 523 HeaderLibs: linker.properties.Export_header_libs, 524 } 525} 526 527func (linker *stubDecorator) moduleInfoJSON(ctx ModuleContext, moduleInfoJSON *android.ModuleInfoJSON) { 528 linker.libraryDecorator.moduleInfoJSON(ctx, moduleInfoJSON) 529 // Overwrites the SubName computed by libraryDecorator 530 moduleInfoJSON.SubName = ndkLibrarySuffix + "." + linker.apiLevel.String() 531} 532 533func (linker *stubDecorator) Name(name string) string { 534 return name + ndkLibrarySuffix 535} 536 537func (stub *stubDecorator) linkerFlags(ctx ModuleContext, flags Flags) Flags { 538 stub.libraryDecorator.libName = ctx.baseModuleName() 539 return stub.libraryDecorator.linkerFlags(ctx, flags) 540} 541 542func (stub *stubDecorator) link(ctx ModuleContext, flags Flags, deps PathDeps, 543 objs Objects) android.Path { 544 545 if !stub.buildStubs() { 546 // NDK libraries have no implementation variant, nothing to do 547 return nil 548 } 549 550 if shouldUseVersionScript(ctx, stub) { 551 linkerScriptFlag := "-Wl,--version-script," + stub.versionScriptPath.String() 552 flags.Local.LdFlags = append(flags.Local.LdFlags, linkerScriptFlag) 553 flags.LdFlagsDeps = append(flags.LdFlagsDeps, stub.versionScriptPath) 554 } 555 556 stub.libraryDecorator.skipAPIDefine = true 557 return stub.libraryDecorator.link(ctx, flags, deps, objs) 558} 559 560func (stub *stubDecorator) nativeCoverage() bool { 561 return false 562} 563 564// Returns the install path for unversioned NDK libraries (currently only static 565// libraries). 566func getUnversionedLibraryInstallPath(ctx ModuleContext) android.OutputPath { 567 return getNdkSysrootBase(ctx).Join(ctx, "usr/lib", config.NDKTriple(ctx.toolchain())) 568} 569 570// Returns the install path for versioned NDK libraries. These are most often 571// stubs, but the same paths are used for CRT objects. 572func getVersionedLibraryInstallPath(ctx ModuleContext, apiLevel android.ApiLevel) android.OutputPath { 573 return getUnversionedLibraryInstallPath(ctx).Join(ctx, apiLevel.String()) 574} 575 576func (stub *stubDecorator) install(ctx ModuleContext, path android.Path) { 577 installDir := getVersionedLibraryInstallPath(ctx, stub.apiLevel) 578 out := installDir.Join(ctx, path.Base()) 579 ctx.Build(pctx, android.BuildParams{ 580 Rule: android.Cp, 581 Input: path, 582 Output: out, 583 }) 584 stub.installPath = out 585} 586 587func newStubLibrary() *Module { 588 module, library := NewLibrary(android.DeviceSupported) 589 library.BuildOnlyShared() 590 module.stl = nil 591 module.sanitize = nil 592 library.disableStripping() 593 594 stub := &stubDecorator{ 595 libraryDecorator: library, 596 } 597 module.compiler = stub 598 module.linker = stub 599 module.installer = stub 600 module.library = stub 601 602 module.Properties.AlwaysSdk = true 603 module.Properties.Sdk_version = StringPtr("current") 604 605 module.AddProperties(&stub.properties, &library.MutatedProperties) 606 607 return module 608} 609 610// ndk_library creates a library that exposes a stub implementation of functions 611// and variables for use at build time only. 612func NdkLibraryFactory() android.Module { 613 module := newStubLibrary() 614 android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibBoth) 615 return module 616} 617