• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
17// The platform needs to provide the following artifacts for the NDK:
18// 1. Bionic headers.
19// 2. Platform API headers.
20// 3. NDK stub shared libraries.
21// 4. Bionic static libraries.
22//
23// TODO(danalbert): All of the above need to include NOTICE files.
24//
25// Components 1 and 2: Headers
26// The bionic and platform API headers are generalized into a single
27// `ndk_headers` rule. This rule has a `from` property that indicates a base
28// directory from which headers are to be taken, and a `to` property that
29// indicates where in the sysroot they should reside relative to usr/include.
30// There is also a `srcs` property that is glob compatible for specifying which
31// headers to include.
32//
33// Component 3: Stub Libraries
34// The shared libraries in the NDK are not the actual shared libraries they
35// refer to (to prevent people from accidentally loading them), but stub
36// libraries with placeholder implementations of everything for use at build time
37// only.
38//
39// Since we don't actually need to know anything about the stub libraries aside
40// from a list of functions and globals to be exposed, we can create these for
41// every platform level in the current tree. This is handled by the
42// ndk_library rule.
43//
44// Component 4: Static Libraries
45// The NDK only provides static libraries for bionic, not the platform APIs.
46// Since these need to be the actual implementation, we can't build old versions
47// in the current platform tree. As such, legacy versions are checked in
48// prebuilt to development/ndk, and a current version is built and archived as
49// part of the platform build. The platfrom already builds these libraries, our
50// NDK build rules only need to archive them for retrieval so they can be added
51// to the prebuilts.
52//
53// TODO(danalbert): Write `ndk_static_library` rule.
54
55import (
56	"fmt"
57	"path/filepath"
58	"strings"
59
60	"android/soong/android"
61
62	"github.com/google/blueprint"
63)
64
65var (
66	verifyCCompat = pctx.AndroidStaticRule("verifyCCompat",
67		blueprint.RuleParams{
68			Command:     "$ccCmd -x c -fsyntax-only $flags $in && touch $out",
69			CommandDeps: []string{"$ccCmd"},
70		},
71		"ccCmd",
72		"flags",
73	)
74)
75
76func init() {
77	RegisterNdkModuleTypes(android.InitRegistrationContext)
78}
79
80func RegisterNdkModuleTypes(ctx android.RegistrationContext) {
81	ctx.RegisterModuleType("ndk_headers", NdkHeadersFactory)
82	ctx.RegisterModuleType("ndk_library", NdkLibraryFactory)
83	ctx.RegisterModuleType("preprocessed_ndk_headers", preprocessedNdkHeadersFactory)
84	ctx.RegisterParallelSingletonType("ndk", NdkSingleton)
85}
86
87func getNdkInstallBase(ctx android.PathContext) android.OutputPath {
88	return android.PathForNdkInstall(ctx)
89}
90
91// Returns the main install directory for the NDK sysroot. Usable with --sysroot.
92func getNdkSysrootBase(ctx android.PathContext) android.OutputPath {
93	return getNdkInstallBase(ctx).Join(ctx, "sysroot")
94}
95
96// The base timestamp file depends on the NDK headers and stub shared libraries,
97// but not the static libraries. This distinction is needed because the static
98// libraries themselves might need to depend on the base sysroot.
99func getNdkBaseTimestampFile(ctx android.PathContext) android.WritablePath {
100	return android.PathForOutput(ctx, "ndk_base.timestamp")
101}
102
103// The headers timestamp file depends only on the NDK headers.
104// This is used mainly for .tidy files that do not need any stub libraries.
105func getNdkHeadersTimestampFile(ctx android.PathContext) android.WritablePath {
106	return android.PathForOutput(ctx, "ndk_headers.timestamp")
107}
108
109// The full timestamp file depends on the base timestamp *and* the static
110// libraries.
111func getNdkFullTimestampFile(ctx android.PathContext) android.WritablePath {
112	return android.PathForOutput(ctx, "ndk.timestamp")
113}
114
115// The list of all NDK headers as they are located in the repo.
116// Used for ABI monitoring to track only structures defined in NDK headers.
117func getNdkABIHeadersFile(ctx android.PathContext) android.WritablePath {
118	return android.PathForOutput(ctx, "ndk_abi_headers.txt")
119}
120
121func verifyNdkHeaderIsCCompatible(ctx android.SingletonContext,
122	src android.Path, dest android.Path) android.Path {
123	sysrootInclude := getCurrentIncludePath(ctx)
124	baseOutputDir := android.PathForOutput(ctx, "c-compat-verification")
125	installRelPath, err := filepath.Rel(sysrootInclude.String(), dest.String())
126	if err != nil {
127		ctx.Errorf("filepath.Rel(%q, %q) failed: %s", dest, sysrootInclude, err)
128	}
129	output := baseOutputDir.Join(ctx, installRelPath)
130	ctx.Build(pctx, android.BuildParams{
131		Rule:        verifyCCompat,
132		Description: fmt.Sprintf("Verifying C compatibility of %s", src),
133		Output:      output,
134		Input:       dest,
135		// Ensures that all the headers in the sysroot are already installed
136		// before testing any of the headers for C compatibility, and also that
137		// the check will be re-run whenever the sysroot changes. This is
138		// necessary because many of the NDK headers depend on other NDK
139		// headers, but we don't have explicit dependency tracking for that.
140		Implicits: []android.Path{getNdkHeadersTimestampFile(ctx)},
141		Args: map[string]string{
142			"ccCmd": "${config.ClangBin}/clang",
143			"flags": fmt.Sprintf(
144				// Ideally we'd check each ABI, multiple API levels,
145				// fortify/non-fortify, and a handful of other variations. It's
146				// a lot more difficult to do that though, and would eat up more
147				// build time. All the problems we've seen so far that this
148				// check would catch have been in arch-generic and
149				// minSdkVersion-generic code in frameworks though, so this is a
150				// good place to start.
151				"-target aarch64-linux-android%d --sysroot %s",
152				android.FutureApiLevel.FinalOrFutureInt(),
153				getNdkSysrootBase(ctx).String(),
154			),
155		},
156	})
157	return output
158}
159
160func NdkSingleton() android.Singleton {
161	return &ndkSingleton{}
162}
163
164// Collect all NDK exported headers paths into a file that is used to
165// detect public types that should be ABI monitored.
166//
167// Assume that we have the following code in exported header:
168//
169//	typedef struct Context Context;
170//	typedef struct Output {
171//	    ...
172//	} Output;
173//	void DoSomething(Context* ctx, Output* output);
174//
175// If none of public headers exported to end-users contain definition of
176// "struct Context", then "struct Context" layout and members shouldn't be
177// monitored. However we use DWARF information from a real library, which
178// may have access to the definition of "string Context" from
179// implementation headers, and it will leak to ABI.
180//
181// STG tool doesn't access source and header files, only DWARF information
182// from compiled library. And the DWARF contains file name where a type is
183// defined. So we need a rule to build a list of paths to public headers,
184// so STG can distinguish private types from public and do not monitor
185// private types that are not accessible to library users.
186func writeNdkAbiSrcFilter(ctx android.BuilderContext,
187	headerSrcPaths android.Paths, outputFile android.WritablePath) {
188	var filterBuilder strings.Builder
189	filterBuilder.WriteString("[decl_file_allowlist]\n")
190	for _, headerSrcPath := range headerSrcPaths {
191		filterBuilder.WriteString(headerSrcPath.String())
192		filterBuilder.WriteString("\n")
193	}
194
195	android.WriteFileRule(ctx, outputFile, filterBuilder.String())
196}
197
198type ndkSingleton struct{}
199
200type srcDestPair struct {
201	src  android.Path
202	dest android.Path
203}
204
205func (n *ndkSingleton) GenerateBuildActions(ctx android.SingletonContext) {
206	var staticLibInstallPaths android.Paths
207	var headerSrcPaths android.Paths
208	var headerInstallPaths android.Paths
209	var headersToVerify []srcDestPair
210	var headerCCompatVerificationTimestampPaths android.Paths
211	var installPaths android.Paths
212	var licensePaths android.Paths
213	ctx.VisitAllModuleProxies(func(module android.ModuleProxy) {
214
215		if !android.OtherModulePointerProviderOrDefault(ctx, module, android.CommonModuleInfoProvider).Enabled {
216			return
217		}
218
219		if m, ok := android.OtherModuleProvider(ctx, module, NdkHeaderInfoProvider); ok {
220			headerSrcPaths = append(headerSrcPaths, m.SrcPaths...)
221			headerInstallPaths = append(headerInstallPaths, m.InstallPaths...)
222			if !m.SkipVerification {
223				for i, installPath := range m.InstallPaths {
224					headersToVerify = append(headersToVerify, srcDestPair{
225						src:  m.SrcPaths[i],
226						dest: installPath,
227					})
228				}
229			}
230			installPaths = append(installPaths, m.InstallPaths...)
231			licensePaths = append(licensePaths, m.LicensePath)
232		}
233
234		if m, ok := android.OtherModuleProvider(ctx, module, NdkPreprocessedHeaderInfoProvider); ok {
235			headerSrcPaths = append(headerSrcPaths, m.SrcPaths...)
236			headerInstallPaths = append(headerInstallPaths, m.InstallPaths...)
237			if !m.SkipVerification {
238				for i, installPath := range m.InstallPaths {
239					headersToVerify = append(headersToVerify, srcDestPair{
240						src:  m.SrcPaths[i],
241						dest: installPath,
242					})
243				}
244			}
245			installPaths = append(installPaths, m.InstallPaths...)
246			licensePaths = append(licensePaths, m.LicensePath)
247		}
248
249		if ccInfo, ok := android.OtherModuleProvider(ctx, module, CcInfoProvider); ok {
250			if installer := ccInfo.InstallerInfo; installer != nil && installer.StubDecoratorInfo != nil &&
251				ccInfo.LibraryInfo != nil && ccInfo.LibraryInfo.BuildStubs {
252				installPaths = append(installPaths, installer.StubDecoratorInfo.InstallPath)
253			}
254
255			if ccInfo.LinkerInfo != nil {
256				if library := ccInfo.LinkerInfo.LibraryDecoratorInfo; library != nil {
257					if library.NdkSysrootPath != nil {
258						staticLibInstallPaths = append(
259							staticLibInstallPaths, library.NdkSysrootPath)
260					}
261				}
262
263				if object := ccInfo.LinkerInfo.ObjectLinkerInfo; object != nil {
264					if object.NdkSysrootPath != nil {
265						staticLibInstallPaths = append(
266							staticLibInstallPaths, object.NdkSysrootPath)
267					}
268				}
269			}
270		}
271	})
272
273	// Include only a single copy of each license file. The Bionic NOTICE is
274	// long and is referenced by multiple Bionic modules.
275	licensePaths = android.FirstUniquePaths(licensePaths)
276
277	combinedLicense := getNdkInstallBase(ctx).Join(ctx, "NOTICE")
278	ctx.Build(pctx, android.BuildParams{
279		Rule:        android.Cat,
280		Description: "combine licenses",
281		Output:      combinedLicense,
282		Inputs:      licensePaths,
283	})
284
285	baseDepPaths := append(installPaths, combinedLicense)
286
287	ctx.Build(pctx, android.BuildParams{
288		Rule:       android.Touch,
289		Output:     getNdkBaseTimestampFile(ctx),
290		Implicits:  baseDepPaths,
291		Validation: getNdkAbiDiffTimestampFile(ctx),
292	})
293
294	ctx.Build(pctx, android.BuildParams{
295		Rule:      android.Touch,
296		Output:    getNdkHeadersTimestampFile(ctx),
297		Implicits: headerInstallPaths,
298	})
299
300	for _, srcDestPair := range headersToVerify {
301		headerCCompatVerificationTimestampPaths = append(
302			headerCCompatVerificationTimestampPaths,
303			verifyNdkHeaderIsCCompatible(ctx, srcDestPair.src, srcDestPair.dest))
304	}
305
306	writeNdkAbiSrcFilter(ctx, headerSrcPaths, getNdkABIHeadersFile(ctx))
307
308	fullDepPaths := append(staticLibInstallPaths, getNdkBaseTimestampFile(ctx))
309
310	// There's a phony "ndk" rule defined in core/main.mk that depends on this.
311	// `m ndk` will build the sysroots for the architectures in the current
312	// lunch target. `build/soong/scripts/build-ndk-prebuilts.sh` will build the
313	// sysroots for all the NDK architectures and package them so they can be
314	// imported into the NDK's build.
315	ctx.Build(pctx, android.BuildParams{
316		Rule:      android.Touch,
317		Output:    getNdkFullTimestampFile(ctx),
318		Implicits: append(fullDepPaths, headerCCompatVerificationTimestampPaths...),
319	})
320}
321