ndk_library.go 17 KB

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