| // Copyright 2016 Google Inc. All rights reserved. | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"); | |
| // you may not use this file except in compliance with the License. | |
| // You may obtain a copy of the License at | |
| // | |
| // http://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| package cc | |
| import ( | |
| "fmt" | |
| "path/filepath" | |
| "github.com/google/blueprint" | |
| "android/soong/android" | |
| ) | |
| var ( | |
| versionBionicHeaders = pctx.AndroidStaticRule("versionBionicHeaders", | |
| blueprint.RuleParams{ | |
| // The `&& touch $out` isn't really necessary, but Blueprint won't | |
| // let us have only implicit outputs. | |
| Command: "$versionerCmd -o $outDir $srcDir $depsPath && touch $out", | |
| CommandDeps: []string{"$versionerCmd"}, | |
| }, | |
| "depsPath", "srcDir", "outDir") | |
| preprocessNdkHeader = pctx.AndroidStaticRule("preprocessNdkHeader", | |
| blueprint.RuleParams{ | |
| Command: "$preprocessor -o $out $in", | |
| CommandDeps: []string{"$preprocessor"}, | |
| }, | |
| "preprocessor") | |
| ) | |
| func init() { | |
| pctx.SourcePathVariable("versionerCmd", "prebuilts/clang-tools/${config.HostPrebuiltTag}/bin/versioner") | |
| } | |
| // Returns the NDK base include path for use with sdk_version current. Usable with -I. | |
| func getCurrentIncludePath(ctx android.ModuleContext) android.InstallPath { | |
| return getNdkSysrootBase(ctx).Join(ctx, "usr/include") | |
| } | |
| type headerProperties struct { | |
| // Base directory of the headers being installed. As an example: | |
| // | |
| // ndk_headers { | |
| // name: "foo", | |
| // from: "include", | |
| // to: "", | |
| // srcs: ["include/foo/bar/baz.h"], | |
| // } | |
| // | |
| // Will install $SYSROOT/usr/include/foo/bar/baz.h. If `from` were instead | |
| // "include/foo", it would have installed $SYSROOT/usr/include/bar/baz.h. | |
| From *string | |
| // Install path within the sysroot. This is relative to usr/include. | |
| To *string | |
| // List of headers to install. Glob compatible. Common case is "include/**/*.h". | |
| Srcs []string `android:"path"` | |
| // Source paths that should be excluded from the srcs glob. | |
| Exclude_srcs []string `android:"path"` | |
| // Path to the NOTICE file associated with the headers. | |
| License *string `android:"path"` | |
| } | |
| type headerModule struct { | |
| android.ModuleBase | |
| properties headerProperties | |
| installPaths android.Paths | |
| licensePath android.Path | |
| } | |
| func getHeaderInstallDir(ctx android.ModuleContext, header android.Path, from string, | |
| to string) android.InstallPath { | |
| // Output path is the sysroot base + "usr/include" + to directory + directory component | |
| // of the file without the leading from directory stripped. | |
| // | |
| // Given: | |
| // sysroot base = "ndk/sysroot" | |
| // from = "include/foo" | |
| // to = "bar" | |
| // header = "include/foo/woodly/doodly.h" | |
| // output path = "ndk/sysroot/usr/include/bar/woodly/doodly.h" | |
| // full/platform/path/to/include/foo | |
| fullFromPath := android.PathForModuleSrc(ctx, from) | |
| // full/platform/path/to/include/foo/woodly | |
| headerDir := filepath.Dir(header.String()) | |
| // woodly | |
| strippedHeaderDir, err := filepath.Rel(fullFromPath.String(), headerDir) | |
| if err != nil { | |
| ctx.ModuleErrorf("filepath.Rel(%q, %q) failed: %s", headerDir, | |
| fullFromPath.String(), err) | |
| } | |
| // full/platform/path/to/sysroot/usr/include/bar/woodly | |
| installDir := getCurrentIncludePath(ctx).Join(ctx, to, strippedHeaderDir) | |
| // full/platform/path/to/sysroot/usr/include/bar/woodly/doodly.h | |
| return installDir | |
| } | |
| func (m *headerModule) GenerateAndroidBuildActions(ctx android.ModuleContext) { | |
| if String(m.properties.License) == "" { | |
| ctx.PropertyErrorf("license", "field is required") | |
| } | |
| m.licensePath = android.PathForModuleSrc(ctx, String(m.properties.License)) | |
| srcFiles := android.PathsForModuleSrcExcludes(ctx, m.properties.Srcs, m.properties.Exclude_srcs) | |
| for _, header := range srcFiles { | |
| installDir := getHeaderInstallDir(ctx, header, String(m.properties.From), | |
| String(m.properties.To)) | |
| installedPath := ctx.InstallFile(installDir, header.Base(), header) | |
| installPath := installDir.Join(ctx, header.Base()) | |
| if installPath != installedPath { | |
| panic(fmt.Sprintf( | |
| "expected header install path (%q) not equal to actual install path %q", | |
| installPath, installedPath)) | |
| } | |
| m.installPaths = append(m.installPaths, installPath) | |
| } | |
| if len(m.installPaths) == 0 { | |
| ctx.ModuleErrorf("srcs %q matched zero files", m.properties.Srcs) | |
| } | |
| } | |
| // ndk_headers installs the sets of ndk headers defined in the srcs property | |
| // to the sysroot base + "usr/include" + to directory + directory component. | |
| // ndk_headers requires the license file to be specified. Example: | |
| // | |
| // Given: | |
| // sysroot base = "ndk/sysroot" | |
| // from = "include/foo" | |
| // to = "bar" | |
| // header = "include/foo/woodly/doodly.h" | |
| // output path = "ndk/sysroot/usr/include/bar/woodly/doodly.h" | |
| func ndkHeadersFactory() android.Module { | |
| module := &headerModule{} | |
| module.AddProperties(&module.properties) | |
| android.InitAndroidModule(module) | |
| return module | |
| } | |
| type versionedHeaderProperties struct { | |
| // Base directory of the headers being installed. As an example: | |
| // | |
| // versioned_ndk_headers { | |
| // name: "foo", | |
| // from: "include", | |
| // to: "", | |
| // } | |
| // | |
| // Will install $SYSROOT/usr/include/foo/bar/baz.h. If `from` were instead | |
| // "include/foo", it would have installed $SYSROOT/usr/include/bar/baz.h. | |
| From *string | |
| // Install path within the sysroot. This is relative to usr/include. | |
| To *string | |
| // Path to the NOTICE file associated with the headers. | |
| License *string | |
| } | |
| // Like ndk_headers, but preprocesses the headers with the bionic versioner: | |
| // https://android.googlesource.com/platform/bionic/+/master/tools/versioner/README.md. | |
| // | |
| // Unlike ndk_headers, we don't operate on a list of sources but rather a whole directory, the | |
| // module does not have the srcs property, and operates on a full directory (the `from` property). | |
| // | |
| // Note that this is really only built to handle bionic/libc/include. | |
| type versionedHeaderModule struct { | |
| android.ModuleBase | |
| properties versionedHeaderProperties | |
| installPaths android.Paths | |
| licensePath android.Path | |
| } | |
| // Return the glob pattern to find all .h files beneath `dir` | |
| func headerGlobPattern(dir string) string { | |
| return filepath.Join(dir, "**", "*.h") | |
| } | |
| func (m *versionedHeaderModule) GenerateAndroidBuildActions(ctx android.ModuleContext) { | |
| if String(m.properties.License) == "" { | |
| ctx.PropertyErrorf("license", "field is required") | |
| } | |
| m.licensePath = android.PathForModuleSrc(ctx, String(m.properties.License)) | |
| fromSrcPath := android.PathForModuleSrc(ctx, String(m.properties.From)) | |
| toOutputPath := getCurrentIncludePath(ctx).Join(ctx, String(m.properties.To)) | |
| srcFiles := ctx.GlobFiles(headerGlobPattern(fromSrcPath.String()), nil) | |
| var installPaths []android.WritablePath | |
| for _, header := range srcFiles { | |
| installDir := getHeaderInstallDir(ctx, header, String(m.properties.From), String(m.properties.To)) | |
| installPath := installDir.Join(ctx, header.Base()) | |
| installPaths = append(installPaths, installPath) | |
| m.installPaths = append(m.installPaths, installPath) | |
| } | |
| if len(m.installPaths) == 0 { | |
| ctx.ModuleErrorf("glob %q matched zero files", String(m.properties.From)) | |
| } | |
| processHeadersWithVersioner(ctx, fromSrcPath, toOutputPath, srcFiles, installPaths) | |
| } | |
| func processHeadersWithVersioner(ctx android.ModuleContext, srcDir, outDir android.Path, | |
| srcFiles android.Paths, installPaths []android.WritablePath) android.Path { | |
| // The versioner depends on a dependencies directory to simplify determining include paths | |
| // when parsing headers. This directory contains architecture specific directories as well | |
| // as a common directory, each of which contains symlinks to the actually directories to | |
| // be included. | |
| // | |
| // ctx.Glob doesn't follow symlinks, so we need to do this ourselves so we correctly | |
| // depend on these headers. | |
| // TODO(http://b/35673191): Update the versioner to use a --sysroot. | |
| depsPath := android.PathForSource(ctx, "bionic/libc/versioner-dependencies") | |
| depsGlob := ctx.Glob(filepath.Join(depsPath.String(), "**/*"), nil) | |
| for i, path := range depsGlob { | |
| if ctx.IsSymlink(path) { | |
| dest := ctx.Readlink(path) | |
| // Additional .. to account for the symlink itself. | |
| depsGlob[i] = android.PathForSource( | |
| ctx, filepath.Clean(filepath.Join(path.String(), "..", dest))) | |
| } | |
| } | |
| timestampFile := android.PathForModuleOut(ctx, "versioner.timestamp") | |
| ctx.Build(pctx, android.BuildParams{ | |
| Rule: versionBionicHeaders, | |
| Description: "versioner preprocess " + srcDir.Rel(), | |
| Output: timestampFile, | |
| Implicits: append(srcFiles, depsGlob...), | |
| ImplicitOutputs: installPaths, | |
| Args: map[string]string{ | |
| "depsPath": depsPath.String(), | |
| "srcDir": srcDir.String(), | |
| "outDir": outDir.String(), | |
| }, | |
| }) | |
| return timestampFile | |
| } | |
| // versioned_ndk_headers preprocesses the headers with the bionic versioner: | |
| // https://android.googlesource.com/platform/bionic/+/master/tools/versioner/README.md. | |
| // Unlike the ndk_headers soong module, versioned_ndk_headers operates on a | |
| // directory level specified in `from` property. This is only used to process | |
| // the bionic/libc/include directory. | |
| func versionedNdkHeadersFactory() android.Module { | |
| module := &versionedHeaderModule{} | |
| module.AddProperties(&module.properties) | |
| android.InitAndroidModule(module) | |
| return module | |
| } | |
| // preprocessed_ndk_header { | |
| // | |
| // name: "foo", | |
| // preprocessor: "foo.sh", | |
| // srcs: [...], | |
| // to: "android", | |
| // | |
| // } | |
| // | |
| // Will invoke the preprocessor as: | |
| // | |
| // $preprocessor -o $SYSROOT/usr/include/android/needs_preproc.h $src | |
| // | |
| // For each src in srcs. | |
| type preprocessedHeadersProperties struct { | |
| // The preprocessor to run. Must be a program inside the source directory | |
| // with no dependencies. | |
| Preprocessor *string | |
| // Source path to the files to be preprocessed. | |
| Srcs []string | |
| // Source paths that should be excluded from the srcs glob. | |
| Exclude_srcs []string | |
| // Install path within the sysroot. This is relative to usr/include. | |
| To *string | |
| // Path to the NOTICE file associated with the headers. | |
| License *string | |
| } | |
| type preprocessedHeadersModule struct { | |
| android.ModuleBase | |
| properties preprocessedHeadersProperties | |
| installPaths android.Paths | |
| licensePath android.Path | |
| } | |
| func (m *preprocessedHeadersModule) GenerateAndroidBuildActions(ctx android.ModuleContext) { | |
| if String(m.properties.License) == "" { | |
| ctx.PropertyErrorf("license", "field is required") | |
| } | |
| preprocessor := android.PathForModuleSrc(ctx, String(m.properties.Preprocessor)) | |
| m.licensePath = android.PathForModuleSrc(ctx, String(m.properties.License)) | |
| srcFiles := android.PathsForModuleSrcExcludes(ctx, m.properties.Srcs, m.properties.Exclude_srcs) | |
| installDir := getCurrentIncludePath(ctx).Join(ctx, String(m.properties.To)) | |
| for _, src := range srcFiles { | |
| installPath := installDir.Join(ctx, src.Base()) | |
| m.installPaths = append(m.installPaths, installPath) | |
| ctx.Build(pctx, android.BuildParams{ | |
| Rule: preprocessNdkHeader, | |
| Description: "preprocess " + src.Rel(), | |
| Input: src, | |
| Output: installPath, | |
| Args: map[string]string{ | |
| "preprocessor": preprocessor.String(), | |
| }, | |
| }) | |
| } | |
| if len(m.installPaths) == 0 { | |
| ctx.ModuleErrorf("srcs %q matched zero files", m.properties.Srcs) | |
| } | |
| } | |
| // preprocessed_ndk_headers preprocesses all the ndk headers listed in the srcs | |
| // property by executing the command defined in the preprocessor property. | |
| func preprocessedNdkHeadersFactory() android.Module { | |
| module := &preprocessedHeadersModule{} | |
| module.AddProperties(&module.properties) | |
| android.InitAndroidModule(module) | |
| return module | |
| } |