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