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 "os" 20 "path/filepath" 21 "strings" 22 23 "github.com/google/blueprint" 24 25 "android/soong/android" 26) 27 28var ( 29 versionBionicHeaders = pctx.AndroidStaticRule("versionBionicHeaders", 30 blueprint.RuleParams{ 31 // The `&& touch $out` isn't really necessary, but Blueprint won't 32 // let us have only implicit outputs. 33 Command: "$versionerCmd -o $outDir $srcDir $depsPath && touch $out", 34 CommandDeps: []string{"$versionerCmd"}, 35 }, 36 "depsPath", "srcDir", "outDir") 37 38 preprocessNdkHeader = pctx.AndroidStaticRule("preprocessNdkHeader", 39 blueprint.RuleParams{ 40 Command: "$preprocessor -o $out $in", 41 CommandDeps: []string{"$preprocessor"}, 42 }, 43 "preprocessor") 44) 45 46func init() { 47 pctx.SourcePathVariable("versionerCmd", "prebuilts/clang-tools/${config.HostPrebuiltTag}/bin/versioner") 48} 49 50// Returns the NDK base include path for use with sdk_version current. Usable with -I. 51func getCurrentIncludePath(ctx android.ModuleContext) android.OutputPath { 52 return getNdkSysrootBase(ctx).Join(ctx, "usr/include") 53} 54 55type headerProperties struct { 56 // Base directory of the headers being installed. As an example: 57 // 58 // ndk_headers { 59 // name: "foo", 60 // from: "include", 61 // to: "", 62 // srcs: ["include/foo/bar/baz.h"], 63 // } 64 // 65 // Will install $SYSROOT/usr/include/foo/bar/baz.h. If `from` were instead 66 // "include/foo", it would have installed $SYSROOT/usr/include/bar/baz.h. 67 From *string 68 69 // Install path within the sysroot. This is relative to usr/include. 70 To *string 71 72 // List of headers to install. Glob compatible. Common case is "include/**/*.h". 73 Srcs []string `android:"path"` 74 75 // Source paths that should be excluded from the srcs glob. 76 Exclude_srcs []string `android:"path"` 77 78 // Path to the NOTICE file associated with the headers. 79 License *string `android:"path"` 80 81 // True if this API is not yet ready to be shipped in the NDK. It will be 82 // available in the platform for testing, but will be excluded from the 83 // sysroot provided to the NDK proper. 84 Draft bool 85} 86 87type headerModule struct { 88 android.ModuleBase 89 90 properties headerProperties 91 92 installPaths android.Paths 93 licensePath android.Path 94} 95 96func getHeaderInstallDir(ctx android.ModuleContext, header android.Path, from string, 97 to string) android.OutputPath { 98 // Output path is the sysroot base + "usr/include" + to directory + directory component 99 // of the file without the leading from directory stripped. 100 // 101 // Given: 102 // sysroot base = "ndk/sysroot" 103 // from = "include/foo" 104 // to = "bar" 105 // header = "include/foo/woodly/doodly.h" 106 // output path = "ndk/sysroot/usr/include/bar/woodly/doodly.h" 107 108 // full/platform/path/to/include/foo 109 fullFromPath := android.PathForModuleSrc(ctx, from) 110 111 // full/platform/path/to/include/foo/woodly 112 headerDir := filepath.Dir(header.String()) 113 114 // woodly 115 strippedHeaderDir, err := filepath.Rel(fullFromPath.String(), headerDir) 116 if err != nil { 117 ctx.ModuleErrorf("filepath.Rel(%q, %q) failed: %s", headerDir, 118 fullFromPath.String(), err) 119 } 120 121 // full/platform/path/to/sysroot/usr/include/bar/woodly 122 installDir := getCurrentIncludePath(ctx).Join(ctx, to, strippedHeaderDir) 123 124 // full/platform/path/to/sysroot/usr/include/bar/woodly/doodly.h 125 return installDir 126} 127 128func (m *headerModule) GenerateAndroidBuildActions(ctx android.ModuleContext) { 129 if String(m.properties.License) == "" { 130 ctx.PropertyErrorf("license", "field is required") 131 } 132 133 m.licensePath = android.PathForModuleSrc(ctx, String(m.properties.License)) 134 135 // When generating NDK prebuilts, skip installing MIPS headers, 136 // but keep them when doing regular platform build. 137 // Ndk_abis property is only set to true with build/soong/scripts/build-ndk-prebuilts.sh 138 // TODO: Revert this once MIPS is supported in NDK again. 139 if ctx.Config().NdkAbis() && strings.Contains(ctx.ModuleName(), "mips") { 140 return 141 } 142 143 srcFiles := android.PathsForModuleSrcExcludes(ctx, m.properties.Srcs, m.properties.Exclude_srcs) 144 for _, header := range srcFiles { 145 installDir := getHeaderInstallDir(ctx, header, String(m.properties.From), 146 String(m.properties.To)) 147 installedPath := ctx.InstallFile(installDir, header.Base(), header) 148 installPath := installDir.Join(ctx, header.Base()) 149 if installPath != installedPath { 150 panic(fmt.Sprintf( 151 "expected header install path (%q) not equal to actual install path %q", 152 installPath, installedPath)) 153 } 154 m.installPaths = append(m.installPaths, installPath) 155 } 156 157 if len(m.installPaths) == 0 { 158 ctx.ModuleErrorf("srcs %q matched zero files", m.properties.Srcs) 159 } 160} 161 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 fileInfo, err := os.Lstat(path.String()) 249 if err != nil { 250 ctx.ModuleErrorf("os.Lstat(%q) failed: %s", path.String, err) 251 } 252 if fileInfo.Mode()&os.ModeSymlink == os.ModeSymlink { 253 dest, err := os.Readlink(path.String()) 254 if err != nil { 255 ctx.ModuleErrorf("os.Readlink(%q) failed: %s", 256 path.String, err) 257 } 258 // Additional .. to account for the symlink itself. 259 depsGlob[i] = android.PathForSource( 260 ctx, filepath.Clean(filepath.Join(path.String(), "..", dest))) 261 } 262 } 263 264 timestampFile := android.PathForModuleOut(ctx, "versioner.timestamp") 265 ctx.Build(pctx, android.BuildParams{ 266 Rule: versionBionicHeaders, 267 Description: "versioner preprocess " + srcDir.Rel(), 268 Output: timestampFile, 269 Implicits: append(srcFiles, depsGlob...), 270 ImplicitOutputs: installPaths, 271 Args: map[string]string{ 272 "depsPath": depsPath.String(), 273 "srcDir": srcDir.String(), 274 "outDir": outDir.String(), 275 }, 276 }) 277 278 return timestampFile 279} 280 281func versionedNdkHeadersFactory() android.Module { 282 module := &versionedHeaderModule{} 283 284 module.AddProperties(&module.properties) 285 286 android.InitAndroidModule(module) 287 288 return module 289} 290 291// preprocessed_ndk_header { 292// name: "foo", 293// preprocessor: "foo.sh", 294// srcs: [...], 295// to: "android", 296// } 297// 298// Will invoke the preprocessor as: 299// $preprocessor -o $SYSROOT/usr/include/android/needs_preproc.h $src 300// For each src in srcs. 301type preprocessedHeadersProperties struct { 302 // The preprocessor to run. Must be a program inside the source directory 303 // with no dependencies. 304 Preprocessor *string 305 306 // Source path to the files to be preprocessed. 307 Srcs []string 308 309 // Source paths that should be excluded from the srcs glob. 310 Exclude_srcs []string 311 312 // Install path within the sysroot. This is relative to usr/include. 313 To *string 314 315 // Path to the NOTICE file associated with the headers. 316 License *string 317 318 // True if this API is not yet ready to be shipped in the NDK. It will be 319 // available in the platform for testing, but will be excluded from the 320 // sysroot provided to the NDK proper. 321 Draft bool 322} 323 324type preprocessedHeadersModule struct { 325 android.ModuleBase 326 327 properties preprocessedHeadersProperties 328 329 installPaths android.Paths 330 licensePath android.Path 331} 332 333func (m *preprocessedHeadersModule) GenerateAndroidBuildActions(ctx android.ModuleContext) { 334 if String(m.properties.License) == "" { 335 ctx.PropertyErrorf("license", "field is required") 336 } 337 338 preprocessor := android.PathForModuleSrc(ctx, String(m.properties.Preprocessor)) 339 m.licensePath = android.PathForModuleSrc(ctx, String(m.properties.License)) 340 341 srcFiles := android.PathsForModuleSrcExcludes(ctx, m.properties.Srcs, m.properties.Exclude_srcs) 342 installDir := getCurrentIncludePath(ctx).Join(ctx, String(m.properties.To)) 343 for _, src := range srcFiles { 344 installPath := installDir.Join(ctx, src.Base()) 345 m.installPaths = append(m.installPaths, installPath) 346 347 ctx.Build(pctx, android.BuildParams{ 348 Rule: preprocessNdkHeader, 349 Description: "preprocess " + src.Rel(), 350 Input: src, 351 Output: installPath, 352 Args: map[string]string{ 353 "preprocessor": preprocessor.String(), 354 }, 355 }) 356 } 357 358 if len(m.installPaths) == 0 { 359 ctx.ModuleErrorf("srcs %q matched zero files", m.properties.Srcs) 360 } 361} 362 363func preprocessedNdkHeadersFactory() android.Module { 364 module := &preprocessedHeadersModule{} 365 366 module.AddProperties(&module.properties) 367 368 android.InitAndroidModule(module) 369 370 return module 371} 372