• 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
22	"github.com/google/blueprint"
23
24	"android/soong/android"
25)
26
27var (
28	preprocessBionicHeaders = pctx.AndroidStaticRule("preprocessBionicHeaders",
29		blueprint.RuleParams{
30			// The `&& touch $out` isn't really necessary, but Blueprint won't
31			// let us have only implicit outputs.
32			Command:     "$versionerCmd -o $outDir $srcDir $depsPath && touch $out",
33			CommandDeps: []string{"$versionerCmd"},
34		},
35		"depsPath", "srcDir", "outDir")
36)
37
38func init() {
39	pctx.HostBinToolVariable("versionerCmd", "versioner")
40}
41
42// Returns the NDK base include path for use with sdk_version current. Usable with -I.
43func getCurrentIncludePath(ctx android.ModuleContext) android.OutputPath {
44	return getNdkSysrootBase(ctx).Join(ctx, "usr/include")
45}
46
47type headerProperies struct {
48	// Base directory of the headers being installed. As an example:
49	//
50	// ndk_headers {
51	//     name: "foo",
52	//     from: "include",
53	//     to: "",
54	//     srcs: ["include/foo/bar/baz.h"],
55	// }
56	//
57	// Will install $SYSROOT/usr/include/foo/bar/baz.h. If `from` were instead
58	// "include/foo", it would have installed $SYSROOT/usr/include/bar/baz.h.
59	From string
60
61	// Install path within the sysroot. This is relative to usr/include.
62	To string
63
64	// List of headers to install. Glob compatible. Common case is "include/**/*.h".
65	Srcs []string
66
67	// Path to the NOTICE file associated with the headers.
68	License string
69}
70
71type headerModule struct {
72	android.ModuleBase
73
74	properties headerProperies
75
76	installPaths []string
77	licensePath  android.ModuleSrcPath
78}
79
80func (m *headerModule) DepsMutator(ctx android.BottomUpMutatorContext) {
81}
82
83func getHeaderInstallDir(ctx android.ModuleContext, header android.Path, from string,
84	to string) android.OutputPath {
85	// Output path is the sysroot base + "usr/include" + to directory + directory component
86	// of the file without the leading from directory stripped.
87	//
88	// Given:
89	// sysroot base = "ndk/sysroot"
90	// from = "include/foo"
91	// to = "bar"
92	// header = "include/foo/woodly/doodly.h"
93	// output path = "ndk/sysroot/usr/include/bar/woodly/doodly.h"
94
95	// full/platform/path/to/include/foo
96	fullFromPath := android.PathForModuleSrc(ctx, from)
97
98	// full/platform/path/to/include/foo/woodly
99	headerDir := filepath.Dir(header.String())
100
101	// woodly
102	strippedHeaderDir, err := filepath.Rel(fullFromPath.String(), headerDir)
103	if err != nil {
104		ctx.ModuleErrorf("filepath.Rel(%q, %q) failed: %s", headerDir,
105			fullFromPath.String(), err)
106	}
107
108	// full/platform/path/to/sysroot/usr/include/bar/woodly
109	installDir := getCurrentIncludePath(ctx).Join(ctx, to, strippedHeaderDir)
110
111	// full/platform/path/to/sysroot/usr/include/bar/woodly/doodly.h
112	return installDir
113}
114
115func (m *headerModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
116	if m.properties.License == "" {
117		ctx.PropertyErrorf("license", "field is required")
118	}
119
120	m.licensePath = android.PathForModuleSrc(ctx, m.properties.License)
121
122	srcFiles := ctx.ExpandSources(m.properties.Srcs, nil)
123	for _, header := range srcFiles {
124		installDir := getHeaderInstallDir(ctx, header, m.properties.From, m.properties.To)
125		installedPath := ctx.InstallFile(installDir, header)
126		installPath := installDir.Join(ctx, header.Base())
127		if installPath != installedPath {
128			panic(fmt.Sprintf(
129				"expected header install path (%q) not equal to actual install path %q",
130				installPath, installedPath))
131		}
132		m.installPaths = append(m.installPaths, installPath.String())
133	}
134
135	if len(m.installPaths) == 0 {
136		ctx.ModuleErrorf("srcs %q matched zero files", m.properties.Srcs)
137	}
138}
139
140func ndkHeadersFactory() android.Module {
141	module := &headerModule{}
142	module.AddProperties(&module.properties)
143	android.InitAndroidModule(module)
144	return module
145}
146
147type preprocessedHeaderProperies struct {
148	// Base directory of the headers being installed. As an example:
149	//
150	// preprocessed_ndk_headers {
151	//     name: "foo",
152	//     from: "include",
153	//     to: "",
154	// }
155	//
156	// Will install $SYSROOT/usr/include/foo/bar/baz.h. If `from` were instead
157	// "include/foo", it would have installed $SYSROOT/usr/include/bar/baz.h.
158	From string
159
160	// Install path within the sysroot. This is relative to usr/include.
161	To string
162
163	// Path to the NOTICE file associated with the headers.
164	License string
165}
166
167// Like ndk_headers, but preprocesses the headers with the bionic versioner:
168// https://android.googlesource.com/platform/bionic/+/master/tools/versioner/README.md.
169//
170// Unlike ndk_headers, we don't operate on a list of sources but rather a whole directory, the
171// module does not have the srcs property, and operates on a full directory (the `from` property).
172//
173// Note that this is really only built to handle bionic/libc/include.
174type preprocessedHeaderModule struct {
175	android.ModuleBase
176
177	properties preprocessedHeaderProperies
178
179	installPaths []string
180	licensePath  android.ModuleSrcPath
181}
182
183func (m *preprocessedHeaderModule) DepsMutator(ctx android.BottomUpMutatorContext) {
184}
185
186func (m *preprocessedHeaderModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
187	if m.properties.License == "" {
188		ctx.PropertyErrorf("license", "field is required")
189	}
190
191	m.licensePath = android.PathForModuleSrc(ctx, m.properties.License)
192
193	fromSrcPath := android.PathForModuleSrc(ctx, m.properties.From)
194	toOutputPath := getCurrentIncludePath(ctx).Join(ctx, m.properties.To)
195	srcFiles := ctx.Glob(filepath.Join(fromSrcPath.String(), "**/*.h"), nil)
196	var installPaths []android.WritablePath
197	for _, header := range srcFiles {
198		installDir := getHeaderInstallDir(ctx, header, m.properties.From, m.properties.To)
199		installPath := installDir.Join(ctx, header.Base())
200		installPaths = append(installPaths, installPath)
201		m.installPaths = append(m.installPaths, installPath.String())
202	}
203
204	if len(m.installPaths) == 0 {
205		ctx.ModuleErrorf("glob %q matched zero files", m.properties.From)
206	}
207
208	processHeadersWithVersioner(ctx, fromSrcPath, toOutputPath, srcFiles, installPaths)
209}
210
211func processHeadersWithVersioner(ctx android.ModuleContext, srcDir, outDir android.Path, srcFiles android.Paths, installPaths []android.WritablePath) android.Path {
212	// The versioner depends on a dependencies directory to simplify determining include paths
213	// when parsing headers. This directory contains architecture specific directories as well
214	// as a common directory, each of which contains symlinks to the actually directories to
215	// be included.
216	//
217	// ctx.Glob doesn't follow symlinks, so we need to do this ourselves so we correctly
218	// depend on these headers.
219	// TODO(http://b/35673191): Update the versioner to use a --sysroot.
220	depsPath := android.PathForSource(ctx, "bionic/libc/versioner-dependencies")
221	depsGlob := ctx.Glob(filepath.Join(depsPath.String(), "**/*"), nil)
222	for i, path := range depsGlob {
223		fileInfo, err := os.Lstat(path.String())
224		if err != nil {
225			ctx.ModuleErrorf("os.Lstat(%q) failed: %s", path.String, err)
226		}
227		if fileInfo.Mode()&os.ModeSymlink == os.ModeSymlink {
228			dest, err := os.Readlink(path.String())
229			if err != nil {
230				ctx.ModuleErrorf("os.Readlink(%q) failed: %s",
231					path.String, err)
232			}
233			// Additional .. to account for the symlink itself.
234			depsGlob[i] = android.PathForSource(
235				ctx, filepath.Clean(filepath.Join(path.String(), "..", dest)))
236		}
237	}
238
239	timestampFile := android.PathForModuleOut(ctx, "versioner.timestamp")
240	ctx.ModuleBuild(pctx, android.ModuleBuildParams{
241		Rule:            preprocessBionicHeaders,
242		Description:     "versioner preprocess " + srcDir.Rel(),
243		Output:          timestampFile,
244		Implicits:       append(srcFiles, depsGlob...),
245		ImplicitOutputs: installPaths,
246		Args: map[string]string{
247			"depsPath": depsPath.String(),
248			"srcDir":   srcDir.String(),
249			"outDir":   outDir.String(),
250		},
251	})
252
253	return timestampFile
254}
255
256func preprocessedNdkHeadersFactory() android.Module {
257	module := &preprocessedHeaderModule{}
258
259	module.AddProperties(&module.properties)
260
261	// Host module rather than device module because device module install steps
262	// do not get run when embedded in make. We're not any of the existing
263	// module types that can be exposed via the Android.mk exporter, so just use
264	// a host module.
265	android.InitAndroidArchModule(module, android.HostSupportedNoCross, android.MultilibFirst)
266
267	return module
268}
269