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 "path/filepath" 19 20 "android/soong/android" 21 "github.com/google/blueprint" 22) 23 24var ( 25 versionBionicHeaders = pctx.AndroidStaticRule("versionBionicHeaders", 26 blueprint.RuleParams{ 27 // The `&& touch $out` isn't really necessary, but Blueprint won't 28 // let us have only implicit outputs. 29 Command: "$versionerCmd -o $outDir $srcDir $depsPath && touch $out", 30 CommandDeps: []string{"$versionerCmd"}, 31 }, 32 "depsPath", "srcDir", "outDir") 33 34 preprocessNdkHeader = pctx.AndroidStaticRule("preprocessNdkHeader", 35 blueprint.RuleParams{ 36 Command: "$preprocessor -o $out $in", 37 CommandDeps: []string{"$preprocessor"}, 38 }, 39 "preprocessor") 40) 41 42func init() { 43 pctx.SourcePathVariable("versionerCmd", "prebuilts/clang-tools/${config.HostPrebuiltTag}/bin/versioner") 44} 45 46// Returns the NDK base include path for use with sdk_version current. Usable with -I. 47func getCurrentIncludePath(ctx android.ModuleContext) android.OutputPath { 48 return getNdkSysrootBase(ctx).Join(ctx, "usr/include") 49} 50 51type headerProperties struct { 52 // Base directory of the headers being installed. As an example: 53 // 54 // ndk_headers { 55 // name: "foo", 56 // from: "include", 57 // to: "", 58 // srcs: ["include/foo/bar/baz.h"], 59 // } 60 // 61 // Will install $SYSROOT/usr/include/foo/bar/baz.h. If `from` were instead 62 // "include/foo", it would have installed $SYSROOT/usr/include/bar/baz.h. 63 From *string 64 65 // Install path within the sysroot. This is relative to usr/include. 66 To *string 67 68 // List of headers to install. Glob compatible. Common case is "include/**/*.h". 69 Srcs []string `android:"path"` 70 71 // Source paths that should be excluded from the srcs glob. 72 Exclude_srcs []string `android:"path"` 73 74 // Path to the NOTICE file associated with the headers. 75 License *string `android:"path"` 76} 77 78type headerModule struct { 79 android.ModuleBase 80 81 properties headerProperties 82 83 srcPaths android.Paths 84 installPaths android.Paths 85 licensePath android.Path 86} 87 88func getHeaderInstallDir(ctx android.ModuleContext, header android.Path, from string, 89 to string) android.OutputPath { 90 // Output path is the sysroot base + "usr/include" + to directory + directory component 91 // of the file without the leading from directory stripped. 92 // 93 // Given: 94 // sysroot base = "ndk/sysroot" 95 // from = "include/foo" 96 // to = "bar" 97 // header = "include/foo/woodly/doodly.h" 98 // output path = "ndk/sysroot/usr/include/bar/woodly/doodly.h" 99 100 // full/platform/path/to/include/foo 101 fullFromPath := android.PathForModuleSrc(ctx, from) 102 103 // full/platform/path/to/include/foo/woodly 104 headerDir := filepath.Dir(header.String()) 105 106 // woodly 107 strippedHeaderDir, err := filepath.Rel(fullFromPath.String(), headerDir) 108 if err != nil { 109 ctx.ModuleErrorf("filepath.Rel(%q, %q) failed: %s", headerDir, 110 fullFromPath.String(), err) 111 } 112 113 // full/platform/path/to/sysroot/usr/include/bar/woodly 114 installDir := getCurrentIncludePath(ctx).Join(ctx, to, strippedHeaderDir) 115 116 // full/platform/path/to/sysroot/usr/include/bar/woodly/doodly.h 117 return installDir 118} 119 120func (m *headerModule) GenerateAndroidBuildActions(ctx android.ModuleContext) { 121 if String(m.properties.License) == "" { 122 ctx.PropertyErrorf("license", "field is required") 123 } 124 125 m.licensePath = android.PathForModuleSrc(ctx, String(m.properties.License)) 126 127 m.srcPaths = android.PathsForModuleSrcExcludes(ctx, m.properties.Srcs, m.properties.Exclude_srcs) 128 for _, header := range m.srcPaths { 129 installDir := getHeaderInstallDir(ctx, header, String(m.properties.From), 130 String(m.properties.To)) 131 installPath := installDir.Join(ctx, header.Base()) 132 ctx.Build(pctx, android.BuildParams{ 133 Rule: android.Cp, 134 Input: header, 135 Output: installPath, 136 }) 137 m.installPaths = append(m.installPaths, installPath) 138 } 139 140 if len(m.installPaths) == 0 { 141 ctx.ModuleErrorf("srcs %q matched zero files", m.properties.Srcs) 142 } 143} 144 145// ndk_headers installs the sets of ndk headers defined in the srcs property 146// to the sysroot base + "usr/include" + to directory + directory component. 147// ndk_headers requires the license file to be specified. Example: 148// 149// Given: 150// sysroot base = "ndk/sysroot" 151// from = "include/foo" 152// to = "bar" 153// header = "include/foo/woodly/doodly.h" 154// output path = "ndk/sysroot/usr/include/bar/woodly/doodly.h" 155func NdkHeadersFactory() android.Module { 156 module := &headerModule{} 157 module.AddProperties(&module.properties) 158 android.InitAndroidModule(module) 159 return module 160} 161 162type versionedHeaderProperties struct { 163 // Base directory of the headers being installed. As an example: 164 // 165 // versioned_ndk_headers { 166 // name: "foo", 167 // from: "include", 168 // to: "", 169 // } 170 // 171 // Will install $SYSROOT/usr/include/foo/bar/baz.h. If `from` were instead 172 // "include/foo", it would have installed $SYSROOT/usr/include/bar/baz.h. 173 From *string 174 175 // Install path within the sysroot. This is relative to usr/include. 176 To *string 177 178 // Path to the NOTICE file associated with the headers. 179 License *string 180} 181 182// Like ndk_headers, but preprocesses the headers with the bionic versioner: 183// https://android.googlesource.com/platform/bionic/+/main/tools/versioner/README.md. 184// 185// Unlike ndk_headers, we don't operate on a list of sources but rather a whole directory, the 186// module does not have the srcs property, and operates on a full directory (the `from` property). 187// 188// Note that this is really only built to handle bionic/libc/include. 189type versionedHeaderModule struct { 190 android.ModuleBase 191 192 properties versionedHeaderProperties 193 194 srcPaths android.Paths 195 installPaths android.Paths 196 licensePath android.Path 197} 198 199// Return the glob pattern to find all .h files beneath `dir` 200func headerGlobPattern(dir string) string { 201 return filepath.Join(dir, "**", "*.h") 202} 203 204func (m *versionedHeaderModule) GenerateAndroidBuildActions(ctx android.ModuleContext) { 205 if String(m.properties.License) == "" { 206 ctx.PropertyErrorf("license", "field is required") 207 } 208 209 m.licensePath = android.PathForModuleSrc(ctx, String(m.properties.License)) 210 211 fromSrcPath := android.PathForModuleSrc(ctx, String(m.properties.From)) 212 toOutputPath := getCurrentIncludePath(ctx).Join(ctx, String(m.properties.To)) 213 m.srcPaths = ctx.GlobFiles(headerGlobPattern(fromSrcPath.String()), nil) 214 var installPaths []android.WritablePath 215 for _, header := range m.srcPaths { 216 installDir := getHeaderInstallDir(ctx, header, String(m.properties.From), String(m.properties.To)) 217 installPath := installDir.Join(ctx, header.Base()) 218 installPaths = append(installPaths, installPath) 219 m.installPaths = append(m.installPaths, installPath) 220 } 221 222 if len(m.installPaths) == 0 { 223 ctx.ModuleErrorf("glob %q matched zero files", String(m.properties.From)) 224 } 225 226 processHeadersWithVersioner(ctx, fromSrcPath, toOutputPath, m.srcPaths, installPaths) 227} 228 229func processHeadersWithVersioner(ctx android.ModuleContext, srcDir, outDir android.Path, 230 srcPaths android.Paths, installPaths []android.WritablePath) android.Path { 231 // The versioner depends on a dependencies directory to simplify determining include paths 232 // when parsing headers. This directory contains architecture specific directories as well 233 // as a common directory, each of which contains symlinks to the actually directories to 234 // be included. 235 // 236 // ctx.Glob doesn't follow symlinks, so we need to do this ourselves so we correctly 237 // depend on these headers. 238 // TODO(http://b/35673191): Update the versioner to use a --sysroot. 239 depsPath := android.PathForSource(ctx, "bionic/libc/versioner-dependencies") 240 depsGlob := ctx.Glob(filepath.Join(depsPath.String(), "**/*"), nil) 241 for i, path := range depsGlob { 242 if ctx.IsSymlink(path) { 243 dest := ctx.Readlink(path) 244 // Additional .. to account for the symlink itself. 245 depsGlob[i] = android.PathForSource( 246 ctx, filepath.Clean(filepath.Join(path.String(), "..", dest))) 247 } 248 } 249 250 timestampFile := android.PathForModuleOut(ctx, "versioner.timestamp") 251 ctx.Build(pctx, android.BuildParams{ 252 Rule: versionBionicHeaders, 253 Description: "versioner preprocess " + srcDir.Rel(), 254 Output: timestampFile, 255 Implicits: append(srcPaths, depsGlob...), 256 ImplicitOutputs: installPaths, 257 Args: map[string]string{ 258 "depsPath": depsPath.String(), 259 "srcDir": srcDir.String(), 260 "outDir": outDir.String(), 261 }, 262 }) 263 264 return timestampFile 265} 266 267// versioned_ndk_headers preprocesses the headers with the bionic versioner: 268// https://android.googlesource.com/platform/bionic/+/main/tools/versioner/README.md. 269// Unlike the ndk_headers soong module, versioned_ndk_headers operates on a 270// directory level specified in `from` property. This is only used to process 271// the bionic/libc/include directory. 272func VersionedNdkHeadersFactory() android.Module { 273 module := &versionedHeaderModule{} 274 275 module.AddProperties(&module.properties) 276 277 android.InitAndroidModule(module) 278 279 return module 280} 281 282// preprocessed_ndk_header { 283// 284// name: "foo", 285// preprocessor: "foo.sh", 286// srcs: [...], 287// to: "android", 288// 289// } 290// 291// Will invoke the preprocessor as: 292// 293// $preprocessor -o $SYSROOT/usr/include/android/needs_preproc.h $src 294// 295// For each src in srcs. 296type preprocessedHeadersProperties struct { 297 // The preprocessor to run. Must be a program inside the source directory 298 // with no dependencies. 299 Preprocessor *string 300 301 // Source path to the files to be preprocessed. 302 Srcs []string 303 304 // Source paths that should be excluded from the srcs glob. 305 Exclude_srcs []string 306 307 // Install path within the sysroot. This is relative to usr/include. 308 To *string 309 310 // Path to the NOTICE file associated with the headers. 311 License *string 312} 313 314type preprocessedHeadersModule struct { 315 android.ModuleBase 316 317 properties preprocessedHeadersProperties 318 319 srcPaths android.Paths 320 installPaths android.Paths 321 licensePath android.Path 322} 323 324func (m *preprocessedHeadersModule) GenerateAndroidBuildActions(ctx android.ModuleContext) { 325 if String(m.properties.License) == "" { 326 ctx.PropertyErrorf("license", "field is required") 327 } 328 329 preprocessor := android.PathForModuleSrc(ctx, String(m.properties.Preprocessor)) 330 m.licensePath = android.PathForModuleSrc(ctx, String(m.properties.License)) 331 332 m.srcPaths = android.PathsForModuleSrcExcludes(ctx, m.properties.Srcs, m.properties.Exclude_srcs) 333 installDir := getCurrentIncludePath(ctx).Join(ctx, String(m.properties.To)) 334 for _, src := range m.srcPaths { 335 installPath := installDir.Join(ctx, src.Base()) 336 m.installPaths = append(m.installPaths, installPath) 337 338 ctx.Build(pctx, android.BuildParams{ 339 Rule: preprocessNdkHeader, 340 Description: "preprocess " + src.Rel(), 341 Input: src, 342 Output: installPath, 343 Args: map[string]string{ 344 "preprocessor": preprocessor.String(), 345 }, 346 }) 347 } 348 349 if len(m.installPaths) == 0 { 350 ctx.ModuleErrorf("srcs %q matched zero files", m.properties.Srcs) 351 } 352} 353 354// preprocessed_ndk_headers preprocesses all the ndk headers listed in the srcs 355// property by executing the command defined in the preprocessor property. 356func preprocessedNdkHeadersFactory() android.Module { 357 module := &preprocessedHeadersModule{} 358 359 module.AddProperties(&module.properties) 360 361 android.InitAndroidModule(module) 362 363 return module 364} 365