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