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 17// The platform needs to provide the following artifacts for the NDK: 18// 1. Bionic headers. 19// 2. Platform API headers. 20// 3. NDK stub shared libraries. 21// 4. Bionic static libraries. 22// 23// TODO(danalbert): All of the above need to include NOTICE files. 24// 25// Components 1 and 2: Headers 26// The bionic and platform API headers are generalized into a single 27// `ndk_headers` rule. This rule has a `from` property that indicates a base 28// directory from which headers are to be taken, and a `to` property that 29// indicates where in the sysroot they should reside relative to usr/include. 30// There is also a `srcs` property that is glob compatible for specifying which 31// headers to include. 32// 33// Component 3: Stub Libraries 34// The shared libraries in the NDK are not the actual shared libraries they 35// refer to (to prevent people from accidentally loading them), but stub 36// libraries with placeholder implementations of everything for use at build time 37// only. 38// 39// Since we don't actually need to know anything about the stub libraries aside 40// from a list of functions and globals to be exposed, we can create these for 41// every platform level in the current tree. This is handled by the 42// ndk_library rule. 43// 44// Component 4: Static Libraries 45// The NDK only provides static libraries for bionic, not the platform APIs. 46// Since these need to be the actual implementation, we can't build old versions 47// in the current platform tree. As such, legacy versions are checked in 48// prebuilt to development/ndk, and a current version is built and archived as 49// part of the platform build. The platfrom already builds these libraries, our 50// NDK build rules only need to archive them for retrieval so they can be added 51// to the prebuilts. 52// 53// TODO(danalbert): Write `ndk_static_library` rule. 54 55import ( 56 "android/soong/android" 57 "strings" 58) 59 60func init() { 61 RegisterNdkModuleTypes(android.InitRegistrationContext) 62} 63 64func RegisterNdkModuleTypes(ctx android.RegistrationContext) { 65 ctx.RegisterModuleType("ndk_headers", NdkHeadersFactory) 66 ctx.RegisterModuleType("ndk_library", NdkLibraryFactory) 67 ctx.RegisterModuleType("versioned_ndk_headers", VersionedNdkHeadersFactory) 68 ctx.RegisterModuleType("preprocessed_ndk_headers", preprocessedNdkHeadersFactory) 69 ctx.RegisterParallelSingletonType("ndk", NdkSingleton) 70} 71 72func getNdkInstallBase(ctx android.PathContext) android.OutputPath { 73 return android.PathForNdkInstall(ctx) 74} 75 76// Returns the main install directory for the NDK sysroot. Usable with --sysroot. 77func getNdkSysrootBase(ctx android.PathContext) android.OutputPath { 78 return getNdkInstallBase(ctx).Join(ctx, "sysroot") 79} 80 81// The base timestamp file depends on the NDK headers and stub shared libraries, 82// but not the static libraries. This distinction is needed because the static 83// libraries themselves might need to depend on the base sysroot. 84func getNdkBaseTimestampFile(ctx android.PathContext) android.WritablePath { 85 return android.PathForOutput(ctx, "ndk_base.timestamp") 86} 87 88// The headers timestamp file depends only on the NDK headers. 89// This is used mainly for .tidy files that do not need any stub libraries. 90func getNdkHeadersTimestampFile(ctx android.PathContext) android.WritablePath { 91 return android.PathForOutput(ctx, "ndk_headers.timestamp") 92} 93 94// The full timestamp file depends on the base timestamp *and* the static 95// libraries. 96func getNdkFullTimestampFile(ctx android.PathContext) android.WritablePath { 97 return android.PathForOutput(ctx, "ndk.timestamp") 98} 99 100// The list of all NDK headers as they are located in the repo. 101// Used for ABI monitoring to track only structures defined in NDK headers. 102func getNdkABIHeadersFile(ctx android.PathContext) android.WritablePath { 103 return android.PathForOutput(ctx, "ndk_abi_headers.txt") 104} 105 106func NdkSingleton() android.Singleton { 107 return &ndkSingleton{} 108} 109 110// Collect all NDK exported headers paths into a file that is used to 111// detect public types that should be ABI monitored. 112// 113// Assume that we have the following code in exported header: 114// 115// typedef struct Context Context; 116// typedef struct Output { 117// ... 118// } Output; 119// void DoSomething(Context* ctx, Output* output); 120// 121// If none of public headers exported to end-users contain definition of 122// "struct Context", then "struct Context" layout and members shouldn't be 123// monitored. However we use DWARF information from a real library, which 124// may have access to the definition of "string Context" from 125// implementation headers, and it will leak to ABI. 126// 127// STG tool doesn't access source and header files, only DWARF information 128// from compiled library. And the DWARF contains file name where a type is 129// defined. So we need a rule to build a list of paths to public headers, 130// so STG can distinguish private types from public and do not monitor 131// private types that are not accessible to library users. 132func writeNdkAbiSrcFilter(ctx android.BuilderContext, 133 headerSrcPaths android.Paths, outputFile android.WritablePath) { 134 var filterBuilder strings.Builder 135 filterBuilder.WriteString("[decl_file_allowlist]\n") 136 for _, headerSrcPath := range headerSrcPaths { 137 filterBuilder.WriteString(headerSrcPath.String()) 138 filterBuilder.WriteString("\n") 139 } 140 141 android.WriteFileRule(ctx, outputFile, filterBuilder.String()) 142} 143 144type ndkSingleton struct{} 145 146func (n *ndkSingleton) GenerateBuildActions(ctx android.SingletonContext) { 147 var staticLibInstallPaths android.Paths 148 var headerSrcPaths android.Paths 149 var headerInstallPaths android.Paths 150 var installPaths android.Paths 151 var licensePaths android.Paths 152 ctx.VisitAllModules(func(module android.Module) { 153 if m, ok := module.(android.Module); ok && !m.Enabled(ctx) { 154 return 155 } 156 157 if m, ok := module.(*headerModule); ok { 158 headerSrcPaths = append(headerSrcPaths, m.srcPaths...) 159 headerInstallPaths = append(headerInstallPaths, m.installPaths...) 160 installPaths = append(installPaths, m.installPaths...) 161 licensePaths = append(licensePaths, m.licensePath) 162 } 163 164 if m, ok := module.(*versionedHeaderModule); ok { 165 headerSrcPaths = append(headerSrcPaths, m.srcPaths...) 166 headerInstallPaths = append(headerInstallPaths, m.installPaths...) 167 installPaths = append(installPaths, m.installPaths...) 168 licensePaths = append(licensePaths, m.licensePath) 169 } 170 171 if m, ok := module.(*preprocessedHeadersModule); ok { 172 headerSrcPaths = append(headerSrcPaths, m.srcPaths...) 173 headerInstallPaths = append(headerInstallPaths, m.installPaths...) 174 installPaths = append(installPaths, m.installPaths...) 175 licensePaths = append(licensePaths, m.licensePath) 176 } 177 178 if m, ok := module.(*Module); ok { 179 if installer, ok := m.installer.(*stubDecorator); ok && m.library.buildStubs() { 180 installPaths = append(installPaths, installer.installPath) 181 } 182 183 if library, ok := m.linker.(*libraryDecorator); ok { 184 if library.ndkSysrootPath != nil { 185 staticLibInstallPaths = append( 186 staticLibInstallPaths, library.ndkSysrootPath) 187 } 188 } 189 190 if object, ok := m.linker.(*objectLinker); ok { 191 if object.ndkSysrootPath != nil { 192 staticLibInstallPaths = append( 193 staticLibInstallPaths, object.ndkSysrootPath) 194 } 195 } 196 } 197 }) 198 199 // Include only a single copy of each license file. The Bionic NOTICE is 200 // long and is referenced by multiple Bionic modules. 201 licensePaths = android.FirstUniquePaths(licensePaths) 202 203 combinedLicense := getNdkInstallBase(ctx).Join(ctx, "NOTICE") 204 ctx.Build(pctx, android.BuildParams{ 205 Rule: android.Cat, 206 Description: "combine licenses", 207 Output: combinedLicense, 208 Inputs: licensePaths, 209 }) 210 211 baseDepPaths := append(installPaths, combinedLicense) 212 213 ctx.Build(pctx, android.BuildParams{ 214 Rule: android.Touch, 215 Output: getNdkBaseTimestampFile(ctx), 216 Implicits: baseDepPaths, 217 Validation: getNdkAbiDiffTimestampFile(ctx), 218 }) 219 220 ctx.Build(pctx, android.BuildParams{ 221 Rule: android.Touch, 222 Output: getNdkHeadersTimestampFile(ctx), 223 Implicits: headerInstallPaths, 224 }) 225 226 writeNdkAbiSrcFilter(ctx, headerSrcPaths, getNdkABIHeadersFile(ctx)) 227 228 fullDepPaths := append(staticLibInstallPaths, getNdkBaseTimestampFile(ctx)) 229 230 // There's a phony "ndk" rule defined in core/main.mk that depends on this. 231 // `m ndk` will build the sysroots for the architectures in the current 232 // lunch target. `build/soong/scripts/build-ndk-prebuilts.sh` will build the 233 // sysroots for all the NDK architectures and package them so they can be 234 // imported into the NDK's build. 235 ctx.Build(pctx, android.BuildParams{ 236 Rule: android.Touch, 237 Output: getNdkFullTimestampFile(ctx), 238 Implicits: fullDepPaths, 239 }) 240} 241