ndk_library.go 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564
  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. package cc
  15. import (
  16. "fmt"
  17. "path/filepath"
  18. "runtime"
  19. "strings"
  20. "sync"
  21. "github.com/google/blueprint"
  22. "github.com/google/blueprint/proptools"
  23. "android/soong/android"
  24. "android/soong/cc/config"
  25. )
  26. func init() {
  27. pctx.HostBinToolVariable("ndkStubGenerator", "ndkstubgen")
  28. pctx.HostBinToolVariable("abidiff", "abidiff")
  29. pctx.HostBinToolVariable("abitidy", "abitidy")
  30. pctx.HostBinToolVariable("abidw", "abidw")
  31. }
  32. var (
  33. genStubSrc = pctx.AndroidStaticRule("genStubSrc",
  34. blueprint.RuleParams{
  35. Command: "$ndkStubGenerator --arch $arch --api $apiLevel " +
  36. "--api-map $apiMap $flags $in $out",
  37. CommandDeps: []string{"$ndkStubGenerator"},
  38. }, "arch", "apiLevel", "apiMap", "flags")
  39. abidw = pctx.AndroidStaticRule("abidw",
  40. blueprint.RuleParams{
  41. Command: "$abidw --type-id-style hash --no-corpus-path " +
  42. "--no-show-locs --no-comp-dir-path -w $symbolList " +
  43. "$in --out-file $out",
  44. CommandDeps: []string{"$abidw"},
  45. }, "symbolList")
  46. abitidy = pctx.AndroidStaticRule("abitidy",
  47. blueprint.RuleParams{
  48. Command: "$abitidy --all $flags -i $in -o $out",
  49. CommandDeps: []string{"$abitidy"},
  50. }, "flags")
  51. abidiff = pctx.AndroidStaticRule("abidiff",
  52. blueprint.RuleParams{
  53. // Need to create *some* output for ninja. We don't want to use tee
  54. // because we don't want to spam the build output with "nothing
  55. // changed" messages, so redirect output message to $out, and if
  56. // changes were detected print the output and fail.
  57. Command: "$abidiff $args $in > $out || (cat $out && false)",
  58. CommandDeps: []string{"$abidiff"},
  59. }, "args")
  60. ndkLibrarySuffix = ".ndk"
  61. ndkKnownLibsKey = android.NewOnceKey("ndkKnownLibsKey")
  62. // protects ndkKnownLibs writes during parallel BeginMutator.
  63. ndkKnownLibsLock sync.Mutex
  64. stubImplementation = dependencyTag{name: "stubImplementation"}
  65. )
  66. // The First_version and Unversioned_until properties of this struct should not
  67. // be used directly, but rather through the ApiLevel returning methods
  68. // firstVersion() and unversionedUntil().
  69. // Creates a stub shared library based on the provided version file.
  70. //
  71. // Example:
  72. //
  73. // ndk_library {
  74. // name: "libfoo",
  75. // symbol_file: "libfoo.map.txt",
  76. // first_version: "9",
  77. // }
  78. type libraryProperties struct {
  79. // Relative path to the symbol map.
  80. // An example file can be seen here: TODO(danalbert): Make an example.
  81. Symbol_file *string `android:"path"`
  82. // The first API level a library was available. A library will be generated
  83. // for every API level beginning with this one.
  84. First_version *string
  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. // If true, does not emit errors when APIs lacking type information are
  91. // found. This is false by default and should not be enabled outside bionic,
  92. // where it is enabled pending a fix for http://b/190554910 (no debug info
  93. // for asm implemented symbols).
  94. Allow_untyped_symbols *bool
  95. }
  96. type stubDecorator struct {
  97. *libraryDecorator
  98. properties libraryProperties
  99. versionScriptPath android.ModuleGenPath
  100. parsedCoverageXmlPath android.ModuleOutPath
  101. installPath android.Path
  102. abiDumpPath android.OutputPath
  103. abiDiffPaths android.Paths
  104. apiLevel android.ApiLevel
  105. firstVersion android.ApiLevel
  106. unversionedUntil android.ApiLevel
  107. }
  108. var _ versionedInterface = (*stubDecorator)(nil)
  109. func shouldUseVersionScript(ctx BaseModuleContext, stub *stubDecorator) bool {
  110. return stub.apiLevel.GreaterThanOrEqualTo(stub.unversionedUntil)
  111. }
  112. func (stub *stubDecorator) implementationModuleName(name string) string {
  113. return strings.TrimSuffix(name, ndkLibrarySuffix)
  114. }
  115. func ndkLibraryVersions(ctx android.BaseMutatorContext, from android.ApiLevel) []string {
  116. var versions []android.ApiLevel
  117. versionStrs := []string{}
  118. for _, version := range ctx.Config().AllSupportedApiLevels() {
  119. if version.GreaterThanOrEqualTo(from) {
  120. versions = append(versions, version)
  121. versionStrs = append(versionStrs, version.String())
  122. }
  123. }
  124. versionStrs = append(versionStrs, android.FutureApiLevel.String())
  125. return versionStrs
  126. }
  127. func (this *stubDecorator) stubsVersions(ctx android.BaseMutatorContext) []string {
  128. if !ctx.Module().Enabled() {
  129. return nil
  130. }
  131. if ctx.Target().NativeBridge == android.NativeBridgeEnabled {
  132. ctx.Module().Disable()
  133. return nil
  134. }
  135. firstVersion, err := nativeApiLevelFromUser(ctx,
  136. String(this.properties.First_version))
  137. if err != nil {
  138. ctx.PropertyErrorf("first_version", err.Error())
  139. return nil
  140. }
  141. return ndkLibraryVersions(ctx, firstVersion)
  142. }
  143. func (this *stubDecorator) initializeProperties(ctx BaseModuleContext) bool {
  144. this.apiLevel = nativeApiLevelOrPanic(ctx, this.stubsVersion())
  145. var err error
  146. this.firstVersion, err = nativeApiLevelFromUser(ctx,
  147. String(this.properties.First_version))
  148. if err != nil {
  149. ctx.PropertyErrorf("first_version", err.Error())
  150. return false
  151. }
  152. str := proptools.StringDefault(this.properties.Unversioned_until, "minimum")
  153. this.unversionedUntil, err = nativeApiLevelFromUser(ctx, str)
  154. if err != nil {
  155. ctx.PropertyErrorf("unversioned_until", err.Error())
  156. return false
  157. }
  158. return true
  159. }
  160. func getNDKKnownLibs(config android.Config) *[]string {
  161. return config.Once(ndkKnownLibsKey, func() interface{} {
  162. return &[]string{}
  163. }).(*[]string)
  164. }
  165. func (c *stubDecorator) compilerInit(ctx BaseModuleContext) {
  166. c.baseCompiler.compilerInit(ctx)
  167. name := ctx.baseModuleName()
  168. if strings.HasSuffix(name, ndkLibrarySuffix) {
  169. ctx.PropertyErrorf("name", "Do not append %q manually, just use the base name", ndkLibrarySuffix)
  170. }
  171. ndkKnownLibsLock.Lock()
  172. defer ndkKnownLibsLock.Unlock()
  173. ndkKnownLibs := getNDKKnownLibs(ctx.Config())
  174. for _, lib := range *ndkKnownLibs {
  175. if lib == name {
  176. return
  177. }
  178. }
  179. *ndkKnownLibs = append(*ndkKnownLibs, name)
  180. }
  181. var stubLibraryCompilerFlags = []string{
  182. // We're knowingly doing some otherwise unsightly things with builtin
  183. // functions here. We're just generating stub libraries, so ignore it.
  184. "-Wno-incompatible-library-redeclaration",
  185. "-Wno-incomplete-setjmp-declaration",
  186. "-Wno-builtin-requires-header",
  187. "-Wno-invalid-noreturn",
  188. "-Wall",
  189. "-Werror",
  190. // These libraries aren't actually used. Don't worry about unwinding
  191. // (avoids the need to link an unwinder into a fake library).
  192. "-fno-unwind-tables",
  193. }
  194. func init() {
  195. config.ExportStringList("StubLibraryCompilerFlags", stubLibraryCompilerFlags)
  196. }
  197. func addStubLibraryCompilerFlags(flags Flags) Flags {
  198. flags.Global.CFlags = append(flags.Global.CFlags, stubLibraryCompilerFlags...)
  199. // All symbols in the stubs library should be visible.
  200. if inList("-fvisibility=hidden", flags.Local.CFlags) {
  201. flags.Local.CFlags = append(flags.Local.CFlags, "-fvisibility=default")
  202. }
  203. return flags
  204. }
  205. func (stub *stubDecorator) compilerFlags(ctx ModuleContext, flags Flags, deps PathDeps) Flags {
  206. flags = stub.baseCompiler.compilerFlags(ctx, flags, deps)
  207. return addStubLibraryCompilerFlags(flags)
  208. }
  209. type ndkApiOutputs struct {
  210. stubSrc android.ModuleGenPath
  211. versionScript android.ModuleGenPath
  212. symbolList android.ModuleGenPath
  213. }
  214. func parseNativeAbiDefinition(ctx ModuleContext, symbolFile string,
  215. apiLevel android.ApiLevel, genstubFlags string) ndkApiOutputs {
  216. stubSrcPath := android.PathForModuleGen(ctx, "stub.c")
  217. versionScriptPath := android.PathForModuleGen(ctx, "stub.map")
  218. symbolFilePath := android.PathForModuleSrc(ctx, symbolFile)
  219. symbolListPath := android.PathForModuleGen(ctx, "abi_symbol_list.txt")
  220. apiLevelsJson := android.GetApiLevelsJson(ctx)
  221. ctx.Build(pctx, android.BuildParams{
  222. Rule: genStubSrc,
  223. Description: "generate stubs " + symbolFilePath.Rel(),
  224. Outputs: []android.WritablePath{stubSrcPath, versionScriptPath,
  225. symbolListPath},
  226. Input: symbolFilePath,
  227. Implicits: []android.Path{apiLevelsJson},
  228. Args: map[string]string{
  229. "arch": ctx.Arch().ArchType.String(),
  230. "apiLevel": apiLevel.String(),
  231. "apiMap": apiLevelsJson.String(),
  232. "flags": genstubFlags,
  233. },
  234. })
  235. return ndkApiOutputs{
  236. stubSrc: stubSrcPath,
  237. versionScript: versionScriptPath,
  238. symbolList: symbolListPath,
  239. }
  240. }
  241. func compileStubLibrary(ctx ModuleContext, flags Flags, src android.Path) Objects {
  242. // libc/libm stubs libraries end up mismatching with clang's internal definition of these
  243. // functions (which have noreturn attributes and other things). Because we just want to create a
  244. // stub with symbol definitions, and types aren't important in C, ignore the mismatch.
  245. flags.Local.ConlyFlags = append(flags.Local.ConlyFlags, "-fno-builtin")
  246. return compileObjs(ctx, flagsToBuilderFlags(flags), "",
  247. android.Paths{src}, nil, nil, nil, nil)
  248. }
  249. func (this *stubDecorator) findImplementationLibrary(ctx ModuleContext) android.Path {
  250. dep := ctx.GetDirectDepWithTag(strings.TrimSuffix(ctx.ModuleName(), ndkLibrarySuffix),
  251. stubImplementation)
  252. if dep == nil {
  253. ctx.ModuleErrorf("Could not find implementation for stub")
  254. return nil
  255. }
  256. impl, ok := dep.(*Module)
  257. if !ok {
  258. ctx.ModuleErrorf("Implementation for stub is not correct module type")
  259. }
  260. output := impl.UnstrippedOutputFile()
  261. if output == nil {
  262. ctx.ModuleErrorf("implementation module (%s) has no output", impl)
  263. return nil
  264. }
  265. return output
  266. }
  267. func (this *stubDecorator) libraryName(ctx ModuleContext) string {
  268. return strings.TrimSuffix(ctx.ModuleName(), ndkLibrarySuffix)
  269. }
  270. func (this *stubDecorator) findPrebuiltAbiDump(ctx ModuleContext,
  271. apiLevel android.ApiLevel) android.OptionalPath {
  272. subpath := filepath.Join("prebuilts/abi-dumps/ndk", apiLevel.String(),
  273. ctx.Arch().ArchType.String(), this.libraryName(ctx), "abi.xml")
  274. return android.ExistentPathForSource(ctx, subpath)
  275. }
  276. // Feature flag.
  277. func canDumpAbi(config android.Config) bool {
  278. if runtime.GOOS == "darwin" {
  279. return false
  280. }
  281. // abidw doesn't currently handle top-byte-ignore correctly. Disable ABI
  282. // dumping for those configs while we wait for a fix. We'll still have ABI
  283. // checking coverage from non-hwasan builds.
  284. // http://b/190554910
  285. if android.InList("hwaddress", config.SanitizeDevice()) {
  286. return false
  287. }
  288. return true
  289. }
  290. // Feature flag to disable diffing against prebuilts.
  291. func canDiffAbi() bool {
  292. return false
  293. }
  294. func (this *stubDecorator) dumpAbi(ctx ModuleContext, symbolList android.Path) {
  295. implementationLibrary := this.findImplementationLibrary(ctx)
  296. abiRawPath := getNdkAbiDumpInstallBase(ctx).Join(ctx,
  297. this.apiLevel.String(), ctx.Arch().ArchType.String(),
  298. this.libraryName(ctx), "abi.raw.xml")
  299. ctx.Build(pctx, android.BuildParams{
  300. Rule: abidw,
  301. Description: fmt.Sprintf("abidw %s", implementationLibrary),
  302. Input: implementationLibrary,
  303. Output: abiRawPath,
  304. Implicit: symbolList,
  305. Args: map[string]string{
  306. "symbolList": symbolList.String(),
  307. },
  308. })
  309. this.abiDumpPath = getNdkAbiDumpInstallBase(ctx).Join(ctx,
  310. this.apiLevel.String(), ctx.Arch().ArchType.String(),
  311. this.libraryName(ctx), "abi.xml")
  312. untypedFlag := "--abort-on-untyped-symbols"
  313. if proptools.BoolDefault(this.properties.Allow_untyped_symbols, false) {
  314. untypedFlag = ""
  315. }
  316. ctx.Build(pctx, android.BuildParams{
  317. Rule: abitidy,
  318. Description: fmt.Sprintf("abitidy %s", implementationLibrary),
  319. Input: abiRawPath,
  320. Output: this.abiDumpPath,
  321. Args: map[string]string{
  322. "flags": untypedFlag,
  323. },
  324. })
  325. }
  326. func findNextApiLevel(ctx ModuleContext, apiLevel android.ApiLevel) *android.ApiLevel {
  327. apiLevels := append(ctx.Config().AllSupportedApiLevels(),
  328. android.FutureApiLevel)
  329. for _, api := range apiLevels {
  330. if api.GreaterThan(apiLevel) {
  331. return &api
  332. }
  333. }
  334. return nil
  335. }
  336. func (this *stubDecorator) diffAbi(ctx ModuleContext) {
  337. missingPrebuiltError := fmt.Sprintf(
  338. "Did not find prebuilt ABI dump for %q. Generate with "+
  339. "//development/tools/ndk/update_ndk_abi.sh.", this.libraryName(ctx))
  340. // Catch any ABI changes compared to the checked-in definition of this API
  341. // level.
  342. abiDiffPath := android.PathForModuleOut(ctx, "abidiff.timestamp")
  343. prebuiltAbiDump := this.findPrebuiltAbiDump(ctx, this.apiLevel)
  344. if !prebuiltAbiDump.Valid() {
  345. ctx.Build(pctx, android.BuildParams{
  346. Rule: android.ErrorRule,
  347. Output: abiDiffPath,
  348. Args: map[string]string{
  349. "error": missingPrebuiltError,
  350. },
  351. })
  352. } else {
  353. ctx.Build(pctx, android.BuildParams{
  354. Rule: abidiff,
  355. Description: fmt.Sprintf("abidiff %s %s", prebuiltAbiDump,
  356. this.abiDumpPath),
  357. Output: abiDiffPath,
  358. Inputs: android.Paths{prebuiltAbiDump.Path(), this.abiDumpPath},
  359. })
  360. }
  361. this.abiDiffPaths = append(this.abiDiffPaths, abiDiffPath)
  362. // Also ensure that the ABI of the next API level (if there is one) matches
  363. // this API level. *New* ABI is allowed, but any changes to APIs that exist
  364. // in this API level are disallowed.
  365. if !this.apiLevel.IsCurrent() {
  366. nextApiLevel := findNextApiLevel(ctx, this.apiLevel)
  367. if nextApiLevel == nil {
  368. panic(fmt.Errorf("could not determine which API level follows "+
  369. "non-current API level %s", this.apiLevel))
  370. }
  371. nextAbiDiffPath := android.PathForModuleOut(ctx,
  372. "abidiff_next.timestamp")
  373. nextAbiDump := this.findPrebuiltAbiDump(ctx, *nextApiLevel)
  374. if !nextAbiDump.Valid() {
  375. ctx.Build(pctx, android.BuildParams{
  376. Rule: android.ErrorRule,
  377. Output: nextAbiDiffPath,
  378. Args: map[string]string{
  379. "error": missingPrebuiltError,
  380. },
  381. })
  382. } else {
  383. ctx.Build(pctx, android.BuildParams{
  384. Rule: abidiff,
  385. Description: fmt.Sprintf("abidiff %s %s", this.abiDumpPath,
  386. nextAbiDump),
  387. Output: nextAbiDiffPath,
  388. Inputs: android.Paths{this.abiDumpPath, nextAbiDump.Path()},
  389. Args: map[string]string{
  390. "args": "--no-added-syms",
  391. },
  392. })
  393. }
  394. this.abiDiffPaths = append(this.abiDiffPaths, nextAbiDiffPath)
  395. }
  396. }
  397. func (c *stubDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) Objects {
  398. if !strings.HasSuffix(String(c.properties.Symbol_file), ".map.txt") {
  399. ctx.PropertyErrorf("symbol_file", "must end with .map.txt")
  400. }
  401. if !c.buildStubs() {
  402. // NDK libraries have no implementation variant, nothing to do
  403. return Objects{}
  404. }
  405. if !c.initializeProperties(ctx) {
  406. // Emits its own errors, so we don't need to.
  407. return Objects{}
  408. }
  409. symbolFile := String(c.properties.Symbol_file)
  410. nativeAbiResult := parseNativeAbiDefinition(ctx, symbolFile, c.apiLevel, "")
  411. objs := compileStubLibrary(ctx, flags, nativeAbiResult.stubSrc)
  412. c.versionScriptPath = nativeAbiResult.versionScript
  413. if canDumpAbi(ctx.Config()) {
  414. c.dumpAbi(ctx, nativeAbiResult.symbolList)
  415. if canDiffAbi() {
  416. c.diffAbi(ctx)
  417. }
  418. }
  419. if c.apiLevel.IsCurrent() && ctx.PrimaryArch() {
  420. c.parsedCoverageXmlPath = parseSymbolFileForAPICoverage(ctx, symbolFile)
  421. }
  422. return objs
  423. }
  424. func (linker *stubDecorator) linkerDeps(ctx DepsContext, deps Deps) Deps {
  425. return Deps{}
  426. }
  427. func (linker *stubDecorator) Name(name string) string {
  428. return name + ndkLibrarySuffix
  429. }
  430. func (stub *stubDecorator) linkerFlags(ctx ModuleContext, flags Flags) Flags {
  431. stub.libraryDecorator.libName = ctx.baseModuleName()
  432. return stub.libraryDecorator.linkerFlags(ctx, flags)
  433. }
  434. func (stub *stubDecorator) link(ctx ModuleContext, flags Flags, deps PathDeps,
  435. objs Objects) android.Path {
  436. if !stub.buildStubs() {
  437. // NDK libraries have no implementation variant, nothing to do
  438. return nil
  439. }
  440. if shouldUseVersionScript(ctx, stub) {
  441. linkerScriptFlag := "-Wl,--version-script," + stub.versionScriptPath.String()
  442. flags.Local.LdFlags = append(flags.Local.LdFlags, linkerScriptFlag)
  443. flags.LdFlagsDeps = append(flags.LdFlagsDeps, stub.versionScriptPath)
  444. }
  445. stub.libraryDecorator.skipAPIDefine = true
  446. return stub.libraryDecorator.link(ctx, flags, deps, objs)
  447. }
  448. func (stub *stubDecorator) nativeCoverage() bool {
  449. return false
  450. }
  451. func (stub *stubDecorator) install(ctx ModuleContext, path android.Path) {
  452. arch := ctx.Target().Arch.ArchType.Name
  453. // arm64 isn't actually a multilib toolchain, so unlike the other LP64
  454. // architectures it's just installed to lib.
  455. libDir := "lib"
  456. if ctx.toolchain().Is64Bit() && arch != "arm64" {
  457. libDir = "lib64"
  458. }
  459. installDir := getNdkInstallBase(ctx).Join(ctx, fmt.Sprintf(
  460. "platforms/android-%s/arch-%s/usr/%s", stub.apiLevel, arch, libDir))
  461. stub.installPath = ctx.InstallFile(installDir, path.Base(), path)
  462. }
  463. func newStubLibrary() *Module {
  464. module, library := NewLibrary(android.DeviceSupported)
  465. library.BuildOnlyShared()
  466. module.stl = nil
  467. module.sanitize = nil
  468. library.disableStripping()
  469. stub := &stubDecorator{
  470. libraryDecorator: library,
  471. }
  472. module.compiler = stub
  473. module.linker = stub
  474. module.installer = stub
  475. module.library = stub
  476. module.Properties.AlwaysSdk = true
  477. module.Properties.Sdk_version = StringPtr("current")
  478. module.AddProperties(&stub.properties, &library.MutatedProperties)
  479. return module
  480. }
  481. // ndk_library creates a library that exposes a stub implementation of functions
  482. // and variables for use at build time only.
  483. func NdkLibraryFactory() android.Module {
  484. module := newStubLibrary()
  485. android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibBoth)
  486. return module
  487. }