|  | // 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" | 
|  | "os" | 
|  | "path/filepath" | 
|  | "strings" | 
|  |  | 
|  | "github.com/google/blueprint" | 
|  |  | 
|  | "android/soong/android" | 
|  | ) | 
|  |  | 
|  | var ( | 
|  | preprocessBionicHeaders = pctx.AndroidStaticRule("preprocessBionicHeaders", | 
|  | 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") | 
|  | ) | 
|  |  | 
|  | func init() { | 
|  | pctx.HostBinToolVariable("versionerCmd", "versioner") | 
|  | } | 
|  |  | 
|  | // Returns the NDK base include path for use with sdk_version current. Usable with -I. | 
|  | func getCurrentIncludePath(ctx android.ModuleContext) android.OutputPath { | 
|  | return getNdkSysrootBase(ctx).Join(ctx, "usr/include") | 
|  | } | 
|  |  | 
|  | type headerProperies 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 | 
|  |  | 
|  | // Path to the NOTICE file associated with the headers. | 
|  | License *string | 
|  | } | 
|  |  | 
|  | type headerModule struct { | 
|  | android.ModuleBase | 
|  |  | 
|  | properties headerProperies | 
|  |  | 
|  | installPaths android.Paths | 
|  | licensePath  android.ModuleSrcPath | 
|  | } | 
|  |  | 
|  | func (m *headerModule) DepsMutator(ctx android.BottomUpMutatorContext) { | 
|  | } | 
|  |  | 
|  | func getHeaderInstallDir(ctx android.ModuleContext, header android.Path, from string, | 
|  | to string) android.OutputPath { | 
|  | // 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)) | 
|  |  | 
|  | // When generating NDK prebuilts, skip installing MIPS headers, | 
|  | // but keep them when doing regular platform build. | 
|  | // Ndk_abis property is only set to true with build/soong/scripts/build-ndk-prebuilts.sh | 
|  | // TODO: Revert this once MIPS is supported in NDK again. | 
|  | if Bool(ctx.AConfig().Ndk_abis) && strings.Contains(ctx.ModuleName(), "mips") { | 
|  | return | 
|  | } | 
|  |  | 
|  | srcFiles := ctx.ExpandSources(m.properties.Srcs, nil) | 
|  | 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) | 
|  | } | 
|  | } | 
|  |  | 
|  | func ndkHeadersFactory() android.Module { | 
|  | module := &headerModule{} | 
|  | module.AddProperties(&module.properties) | 
|  | android.InitAndroidModule(module) | 
|  | return module | 
|  | } | 
|  |  | 
|  | type preprocessedHeaderProperies struct { | 
|  | // Base directory of the headers being installed. As an example: | 
|  | // | 
|  | // preprocessed_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 preprocessedHeaderModule struct { | 
|  | android.ModuleBase | 
|  |  | 
|  | properties preprocessedHeaderProperies | 
|  |  | 
|  | installPaths android.Paths | 
|  | licensePath  android.ModuleSrcPath | 
|  | } | 
|  |  | 
|  | func (m *preprocessedHeaderModule) DepsMutator(ctx android.BottomUpMutatorContext) { | 
|  | } | 
|  |  | 
|  | func (m *preprocessedHeaderModule) 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(filepath.Join(fromSrcPath.String(), "**/*.h"), 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 { | 
|  | fileInfo, err := os.Lstat(path.String()) | 
|  | if err != nil { | 
|  | ctx.ModuleErrorf("os.Lstat(%q) failed: %s", path.String, err) | 
|  | } | 
|  | if fileInfo.Mode()&os.ModeSymlink == os.ModeSymlink { | 
|  | dest, err := os.Readlink(path.String()) | 
|  | if err != nil { | 
|  | ctx.ModuleErrorf("os.Readlink(%q) failed: %s", | 
|  | path.String, err) | 
|  | } | 
|  | // 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:            preprocessBionicHeaders, | 
|  | 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 | 
|  | } | 
|  |  | 
|  | func preprocessedNdkHeadersFactory() android.Module { | 
|  | module := &preprocessedHeaderModule{} | 
|  |  | 
|  | module.AddProperties(&module.properties) | 
|  |  | 
|  | // Host module rather than device module because device module install steps | 
|  | // do not get run when embedded in make. We're not any of the existing | 
|  | // module types that can be exposed via the Android.mk exporter, so just use | 
|  | // a host module. | 
|  | android.InitAndroidArchModule(module, android.HostSupportedNoCross, android.MultilibFirst) | 
|  |  | 
|  | return module | 
|  | } |