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	// True if this API is not yet ready to be shipped in the NDK. It will be
80	// available in the platform for testing, but will be excluded from the
81	// sysroot provided to the NDK proper.
82	Draft bool
83}
84
85type headerModule struct {
86	android.ModuleBase
87
88	properties headerProperties
89
90	installPaths android.Paths
91	licensePath  android.Path
92}
93
94func getHeaderInstallDir(ctx android.ModuleContext, header android.Path, from string,
95	to string) android.InstallPath {
96	// Output path is the sysroot base + "usr/include" + to directory + directory component
97	// of the file without the leading from directory stripped.
98	//
99	// Given:
100	// sysroot base = "ndk/sysroot"
101	// from = "include/foo"
102	// to = "bar"
103	// header = "include/foo/woodly/doodly.h"
104	// output path = "ndk/sysroot/usr/include/bar/woodly/doodly.h"
105
106	// full/platform/path/to/include/foo
107	fullFromPath := android.PathForModuleSrc(ctx, from)
108
109	// full/platform/path/to/include/foo/woodly
110	headerDir := filepath.Dir(header.String())
111
112	// woodly
113	strippedHeaderDir, err := filepath.Rel(fullFromPath.String(), headerDir)
114	if err != nil {
115		ctx.ModuleErrorf("filepath.Rel(%q, %q) failed: %s", headerDir,
116			fullFromPath.String(), err)
117	}
118
119	// full/platform/path/to/sysroot/usr/include/bar/woodly
120	installDir := getCurrentIncludePath(ctx).Join(ctx, to, strippedHeaderDir)
121
122	// full/platform/path/to/sysroot/usr/include/bar/woodly/doodly.h
123	return installDir
124}
125
126func (m *headerModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
127	if String(m.properties.License) == "" {
128		ctx.PropertyErrorf("license", "field is required")
129	}
130
131	m.licensePath = android.PathForModuleSrc(ctx, String(m.properties.License))
132
133	srcFiles := android.PathsForModuleSrcExcludes(ctx, m.properties.Srcs, m.properties.Exclude_srcs)
134	for _, header := range srcFiles {
135		installDir := getHeaderInstallDir(ctx, header, String(m.properties.From),
136			String(m.properties.To))
137		installedPath := ctx.InstallFile(installDir, header.Base(), header)
138		installPath := installDir.Join(ctx, header.Base())
139		if installPath != installedPath {
140			panic(fmt.Sprintf(
141				"expected header install path (%q) not equal to actual install path %q",
142				installPath, installedPath))
143		}
144		m.installPaths = append(m.installPaths, installPath)
145	}
146
147	if len(m.installPaths) == 0 {
148		ctx.ModuleErrorf("srcs %q matched zero files", m.properties.Srcs)
149	}
150}
151
152// ndk_headers installs the sets of ndk headers defined in the srcs property
153// to the sysroot base + "usr/include" + to directory + directory component.
154// ndk_headers requires the license file to be specified. Example:
155//
156//    Given:
157//    sysroot base = "ndk/sysroot"
158//    from = "include/foo"
159//    to = "bar"
160//    header = "include/foo/woodly/doodly.h"
161//    output path = "ndk/sysroot/usr/include/bar/woodly/doodly.h"
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		if ctx.IsSymlink(path) {
249			dest := ctx.Readlink(path)
250			// Additional .. to account for the symlink itself.
251			depsGlob[i] = android.PathForSource(
252				ctx, filepath.Clean(filepath.Join(path.String(), "..", dest)))
253		}
254	}
255
256	timestampFile := android.PathForModuleOut(ctx, "versioner.timestamp")
257	ctx.Build(pctx, android.BuildParams{
258		Rule:            versionBionicHeaders,
259		Description:     "versioner preprocess " + srcDir.Rel(),
260		Output:          timestampFile,
261		Implicits:       append(srcFiles, depsGlob...),
262		ImplicitOutputs: installPaths,
263		Args: map[string]string{
264			"depsPath": depsPath.String(),
265			"srcDir":   srcDir.String(),
266			"outDir":   outDir.String(),
267		},
268	})
269
270	return timestampFile
271}
272
273// versioned_ndk_headers preprocesses the headers with the bionic versioner:
274// https://android.googlesource.com/platform/bionic/+/master/tools/versioner/README.md.
275// Unlike the ndk_headers soong module, versioned_ndk_headers operates on a
276// directory level specified in `from` property. This is only used to process
277// the bionic/libc/include directory.
278func versionedNdkHeadersFactory() android.Module {
279	module := &versionedHeaderModule{}
280
281	module.AddProperties(&module.properties)
282
283	android.InitAndroidModule(module)
284
285	return module
286}
287
288// preprocessed_ndk_header {
289//     name: "foo",
290//     preprocessor: "foo.sh",
291//     srcs: [...],
292//     to: "android",
293// }
294//
295// Will invoke the preprocessor as:
296//     $preprocessor -o $SYSROOT/usr/include/android/needs_preproc.h $src
297// For each src in srcs.
298type preprocessedHeadersProperties struct {
299	// The preprocessor to run. Must be a program inside the source directory
300	// with no dependencies.
301	Preprocessor *string
302
303	// Source path to the files to be preprocessed.
304	Srcs []string
305
306	// Source paths that should be excluded from the srcs glob.
307	Exclude_srcs []string
308
309	// Install path within the sysroot. This is relative to usr/include.
310	To *string
311
312	// Path to the NOTICE file associated with the headers.
313	License *string
314
315	// True if this API is not yet ready to be shipped in the NDK. It will be
316	// available in the platform for testing, but will be excluded from the
317	// sysroot provided to the NDK proper.
318	Draft bool
319}
320
321type preprocessedHeadersModule struct {
322	android.ModuleBase
323
324	properties preprocessedHeadersProperties
325
326	installPaths android.Paths
327	licensePath  android.Path
328}
329
330func (m *preprocessedHeadersModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
331	if String(m.properties.License) == "" {
332		ctx.PropertyErrorf("license", "field is required")
333	}
334
335	preprocessor := android.PathForModuleSrc(ctx, String(m.properties.Preprocessor))
336	m.licensePath = android.PathForModuleSrc(ctx, String(m.properties.License))
337
338	srcFiles := android.PathsForModuleSrcExcludes(ctx, m.properties.Srcs, m.properties.Exclude_srcs)
339	installDir := getCurrentIncludePath(ctx).Join(ctx, String(m.properties.To))
340	for _, src := range srcFiles {
341		installPath := installDir.Join(ctx, src.Base())
342		m.installPaths = append(m.installPaths, installPath)
343
344		ctx.Build(pctx, android.BuildParams{
345			Rule:        preprocessNdkHeader,
346			Description: "preprocess " + src.Rel(),
347			Input:       src,
348			Output:      installPath,
349			Args: map[string]string{
350				"preprocessor": preprocessor.String(),
351			},
352		})
353	}
354
355	if len(m.installPaths) == 0 {
356		ctx.ModuleErrorf("srcs %q matched zero files", m.properties.Srcs)
357	}
358}
359
360// preprocessed_ndk_headers preprocesses all the ndk headers listed in the srcs
361// property by executing the command defined in the preprocessor property.
362func preprocessedNdkHeadersFactory() android.Module {
363	module := &preprocessedHeadersModule{}
364
365	module.AddProperties(&module.properties)
366
367	android.InitAndroidModule(module)
368
369	return module
370}
371