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