• 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
17import (
18	"fmt"
19	"os"
20	"path/filepath"
21	"strings"
22
23	"github.com/google/blueprint"
24
25	"android/soong/android"
26)
27
28var (
29	versionBionicHeaders = pctx.AndroidStaticRule("versionBionicHeaders",
30		blueprint.RuleParams{
31			// The `&& touch $out` isn't really necessary, but Blueprint won't
32			// let us have only implicit outputs.
33			Command:     "$versionerCmd -o $outDir $srcDir $depsPath && touch $out",
34			CommandDeps: []string{"$versionerCmd"},
35		},
36		"depsPath", "srcDir", "outDir")
37
38	preprocessNdkHeader = pctx.AndroidStaticRule("preprocessNdkHeader",
39		blueprint.RuleParams{
40			Command:     "$preprocessor -o $out $in",
41			CommandDeps: []string{"$preprocessor"},
42		},
43		"preprocessor")
44)
45
46func init() {
47	pctx.SourcePathVariable("versionerCmd", "prebuilts/clang-tools/${config.HostPrebuiltTag}/bin/versioner")
48}
49
50// Returns the NDK base include path for use with sdk_version current. Usable with -I.
51func getCurrentIncludePath(ctx android.ModuleContext) android.OutputPath {
52	return getNdkSysrootBase(ctx).Join(ctx, "usr/include")
53}
54
55type headerProperties struct {
56	// Base directory of the headers being installed. As an example:
57	//
58	// ndk_headers {
59	//     name: "foo",
60	//     from: "include",
61	//     to: "",
62	//     srcs: ["include/foo/bar/baz.h"],
63	// }
64	//
65	// Will install $SYSROOT/usr/include/foo/bar/baz.h. If `from` were instead
66	// "include/foo", it would have installed $SYSROOT/usr/include/bar/baz.h.
67	From *string
68
69	// Install path within the sysroot. This is relative to usr/include.
70	To *string
71
72	// List of headers to install. Glob compatible. Common case is "include/**/*.h".
73	Srcs []string `android:"path"`
74
75	// Source paths that should be excluded from the srcs glob.
76	Exclude_srcs []string `android:"path"`
77
78	// Path to the NOTICE file associated with the headers.
79	License *string `android:"path"`
80
81	// True if this API is not yet ready to be shipped in the NDK. It will be
82	// available in the platform for testing, but will be excluded from the
83	// sysroot provided to the NDK proper.
84	Draft bool
85}
86
87type headerModule struct {
88	android.ModuleBase
89
90	properties headerProperties
91
92	installPaths android.Paths
93	licensePath  android.Path
94}
95
96func getHeaderInstallDir(ctx android.ModuleContext, header android.Path, from string,
97	to string) android.OutputPath {
98	// Output path is the sysroot base + "usr/include" + to directory + directory component
99	// of the file without the leading from directory stripped.
100	//
101	// Given:
102	// sysroot base = "ndk/sysroot"
103	// from = "include/foo"
104	// to = "bar"
105	// header = "include/foo/woodly/doodly.h"
106	// output path = "ndk/sysroot/usr/include/bar/woodly/doodly.h"
107
108	// full/platform/path/to/include/foo
109	fullFromPath := android.PathForModuleSrc(ctx, from)
110
111	// full/platform/path/to/include/foo/woodly
112	headerDir := filepath.Dir(header.String())
113
114	// woodly
115	strippedHeaderDir, err := filepath.Rel(fullFromPath.String(), headerDir)
116	if err != nil {
117		ctx.ModuleErrorf("filepath.Rel(%q, %q) failed: %s", headerDir,
118			fullFromPath.String(), err)
119	}
120
121	// full/platform/path/to/sysroot/usr/include/bar/woodly
122	installDir := getCurrentIncludePath(ctx).Join(ctx, to, strippedHeaderDir)
123
124	// full/platform/path/to/sysroot/usr/include/bar/woodly/doodly.h
125	return installDir
126}
127
128func (m *headerModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
129	if String(m.properties.License) == "" {
130		ctx.PropertyErrorf("license", "field is required")
131	}
132
133	m.licensePath = android.PathForModuleSrc(ctx, String(m.properties.License))
134
135	// When generating NDK prebuilts, skip installing MIPS headers,
136	// but keep them when doing regular platform build.
137	// Ndk_abis property is only set to true with build/soong/scripts/build-ndk-prebuilts.sh
138	// TODO: Revert this once MIPS is supported in NDK again.
139	if ctx.Config().NdkAbis() && strings.Contains(ctx.ModuleName(), "mips") {
140		return
141	}
142
143	srcFiles := android.PathsForModuleSrcExcludes(ctx, m.properties.Srcs, m.properties.Exclude_srcs)
144	for _, header := range srcFiles {
145		installDir := getHeaderInstallDir(ctx, header, String(m.properties.From),
146			String(m.properties.To))
147		installedPath := ctx.InstallFile(installDir, header.Base(), header)
148		installPath := installDir.Join(ctx, header.Base())
149		if installPath != installedPath {
150			panic(fmt.Sprintf(
151				"expected header install path (%q) not equal to actual install path %q",
152				installPath, installedPath))
153		}
154		m.installPaths = append(m.installPaths, installPath)
155	}
156
157	if len(m.installPaths) == 0 {
158		ctx.ModuleErrorf("srcs %q matched zero files", m.properties.Srcs)
159	}
160}
161
162func ndkHeadersFactory() android.Module {
163	module := &headerModule{}
164	module.AddProperties(&module.properties)
165	android.InitAndroidModule(module)
166	return module
167}
168
169type versionedHeaderProperties struct {
170	// Base directory of the headers being installed. As an example:
171	//
172	// versioned_ndk_headers {
173	//     name: "foo",
174	//     from: "include",
175	//     to: "",
176	// }
177	//
178	// Will install $SYSROOT/usr/include/foo/bar/baz.h. If `from` were instead
179	// "include/foo", it would have installed $SYSROOT/usr/include/bar/baz.h.
180	From *string
181
182	// Install path within the sysroot. This is relative to usr/include.
183	To *string
184
185	// Path to the NOTICE file associated with the headers.
186	License *string
187
188	// True if this API is not yet ready to be shipped in the NDK. It will be
189	// available in the platform for testing, but will be excluded from the
190	// sysroot provided to the NDK proper.
191	Draft bool
192}
193
194// Like ndk_headers, but preprocesses the headers with the bionic versioner:
195// https://android.googlesource.com/platform/bionic/+/master/tools/versioner/README.md.
196//
197// Unlike ndk_headers, we don't operate on a list of sources but rather a whole directory, the
198// module does not have the srcs property, and operates on a full directory (the `from` property).
199//
200// Note that this is really only built to handle bionic/libc/include.
201type versionedHeaderModule struct {
202	android.ModuleBase
203
204	properties versionedHeaderProperties
205
206	installPaths android.Paths
207	licensePath  android.Path
208}
209
210func (m *versionedHeaderModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
211	if String(m.properties.License) == "" {
212		ctx.PropertyErrorf("license", "field is required")
213	}
214
215	m.licensePath = android.PathForModuleSrc(ctx, String(m.properties.License))
216
217	fromSrcPath := android.PathForModuleSrc(ctx, String(m.properties.From))
218	toOutputPath := getCurrentIncludePath(ctx).Join(ctx, String(m.properties.To))
219	srcFiles := ctx.GlobFiles(filepath.Join(fromSrcPath.String(), "**/*.h"), nil)
220	var installPaths []android.WritablePath
221	for _, header := range srcFiles {
222		installDir := getHeaderInstallDir(ctx, header, String(m.properties.From), String(m.properties.To))
223		installPath := installDir.Join(ctx, header.Base())
224		installPaths = append(installPaths, installPath)
225		m.installPaths = append(m.installPaths, installPath)
226	}
227
228	if len(m.installPaths) == 0 {
229		ctx.ModuleErrorf("glob %q matched zero files", String(m.properties.From))
230	}
231
232	processHeadersWithVersioner(ctx, fromSrcPath, toOutputPath, srcFiles, installPaths)
233}
234
235func processHeadersWithVersioner(ctx android.ModuleContext, srcDir, outDir android.Path,
236	srcFiles android.Paths, installPaths []android.WritablePath) android.Path {
237	// The versioner depends on a dependencies directory to simplify determining include paths
238	// when parsing headers. This directory contains architecture specific directories as well
239	// as a common directory, each of which contains symlinks to the actually directories to
240	// be included.
241	//
242	// ctx.Glob doesn't follow symlinks, so we need to do this ourselves so we correctly
243	// depend on these headers.
244	// TODO(http://b/35673191): Update the versioner to use a --sysroot.
245	depsPath := android.PathForSource(ctx, "bionic/libc/versioner-dependencies")
246	depsGlob := ctx.Glob(filepath.Join(depsPath.String(), "**/*"), nil)
247	for i, path := range depsGlob {
248		fileInfo, err := os.Lstat(path.String())
249		if err != nil {
250			ctx.ModuleErrorf("os.Lstat(%q) failed: %s", path.String, err)
251		}
252		if fileInfo.Mode()&os.ModeSymlink == os.ModeSymlink {
253			dest, err := os.Readlink(path.String())
254			if err != nil {
255				ctx.ModuleErrorf("os.Readlink(%q) failed: %s",
256					path.String, err)
257			}
258			// Additional .. to account for the symlink itself.
259			depsGlob[i] = android.PathForSource(
260				ctx, filepath.Clean(filepath.Join(path.String(), "..", dest)))
261		}
262	}
263
264	timestampFile := android.PathForModuleOut(ctx, "versioner.timestamp")
265	ctx.Build(pctx, android.BuildParams{
266		Rule:            versionBionicHeaders,
267		Description:     "versioner preprocess " + srcDir.Rel(),
268		Output:          timestampFile,
269		Implicits:       append(srcFiles, depsGlob...),
270		ImplicitOutputs: installPaths,
271		Args: map[string]string{
272			"depsPath": depsPath.String(),
273			"srcDir":   srcDir.String(),
274			"outDir":   outDir.String(),
275		},
276	})
277
278	return timestampFile
279}
280
281func versionedNdkHeadersFactory() android.Module {
282	module := &versionedHeaderModule{}
283
284	module.AddProperties(&module.properties)
285
286	android.InitAndroidModule(module)
287
288	return module
289}
290
291// preprocessed_ndk_header {
292//     name: "foo",
293//     preprocessor: "foo.sh",
294//     srcs: [...],
295//     to: "android",
296// }
297//
298// Will invoke the preprocessor as:
299//     $preprocessor -o $SYSROOT/usr/include/android/needs_preproc.h $src
300// For each src in srcs.
301type preprocessedHeadersProperties struct {
302	// The preprocessor to run. Must be a program inside the source directory
303	// with no dependencies.
304	Preprocessor *string
305
306	// Source path to the files to be preprocessed.
307	Srcs []string
308
309	// Source paths that should be excluded from the srcs glob.
310	Exclude_srcs []string
311
312	// Install path within the sysroot. This is relative to usr/include.
313	To *string
314
315	// Path to the NOTICE file associated with the headers.
316	License *string
317
318	// True if this API is not yet ready to be shipped in the NDK. It will be
319	// available in the platform for testing, but will be excluded from the
320	// sysroot provided to the NDK proper.
321	Draft bool
322}
323
324type preprocessedHeadersModule struct {
325	android.ModuleBase
326
327	properties preprocessedHeadersProperties
328
329	installPaths android.Paths
330	licensePath  android.Path
331}
332
333func (m *preprocessedHeadersModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
334	if String(m.properties.License) == "" {
335		ctx.PropertyErrorf("license", "field is required")
336	}
337
338	preprocessor := android.PathForModuleSrc(ctx, String(m.properties.Preprocessor))
339	m.licensePath = android.PathForModuleSrc(ctx, String(m.properties.License))
340
341	srcFiles := android.PathsForModuleSrcExcludes(ctx, m.properties.Srcs, m.properties.Exclude_srcs)
342	installDir := getCurrentIncludePath(ctx).Join(ctx, String(m.properties.To))
343	for _, src := range srcFiles {
344		installPath := installDir.Join(ctx, src.Base())
345		m.installPaths = append(m.installPaths, installPath)
346
347		ctx.Build(pctx, android.BuildParams{
348			Rule:        preprocessNdkHeader,
349			Description: "preprocess " + src.Rel(),
350			Input:       src,
351			Output:      installPath,
352			Args: map[string]string{
353				"preprocessor": preprocessor.String(),
354			},
355		})
356	}
357
358	if len(m.installPaths) == 0 {
359		ctx.ModuleErrorf("srcs %q matched zero files", m.properties.Srcs)
360	}
361}
362
363func preprocessedNdkHeadersFactory() android.Module {
364	module := &preprocessedHeadersModule{}
365
366	module.AddProperties(&module.properties)
367
368	android.InitAndroidModule(module)
369
370	return module
371}
372