1// Copyright (C) 2021 The Android Open Source Project 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 android_sdk 16 17import ( 18 "fmt" 19 "io" 20 "path/filepath" 21 "strings" 22 23 "github.com/google/blueprint" 24 "github.com/google/blueprint/pathtools" 25 "github.com/google/blueprint/proptools" 26 27 "android/soong/android" 28 "android/soong/cc/config" 29) 30 31var pctx = android.NewPackageContext("android/soong/android_sdk") 32 33func init() { 34 registerBuildComponents(android.InitRegistrationContext) 35} 36 37func registerBuildComponents(ctx android.RegistrationContext) { 38 ctx.RegisterModuleType("android_sdk_repo_host", SdkRepoHostFactory) 39} 40 41type sdkRepoHost struct { 42 android.ModuleBase 43 android.PackagingBase 44 45 properties sdkRepoHostProperties 46 47 outputBaseName string 48 outputFile android.OptionalPath 49 50 // TODO(b/357908583): Temp field, remove this once we support Android Mk providers 51 installFile android.InstallPath 52} 53 54type remapProperties struct { 55 From string 56 To string 57} 58 59type sdkRepoHostProperties struct { 60 // The top level directory to use for the SDK repo. 61 Base_dir *string 62 63 // List of src:dst mappings to rename files from `deps`. 64 Deps_remap []remapProperties `android:"arch_variant"` 65 66 // List of zip files to merge into the SDK repo. 67 Merge_zips []string `android:"arch_variant,path"` 68 69 // List of sources to include into the SDK repo. These are usually raw files, filegroups, 70 // or genrules, as most built modules should be referenced via `deps`. 71 Srcs []string `android:"arch_variant,path"` 72 73 // List of files to strip. This should be a list of files, not modules. This happens after 74 // `deps_remap` and `merge_zips` are applied, but before the `base_dir` is added. 75 Strip_files []string `android:"arch_variant"` 76} 77 78// android_sdk_repo_host defines an Android SDK repo containing host tools. 79// 80// This implementation is trying to be a faithful reproduction of how these sdk-repos were produced 81// in the Make system, which may explain some of the oddities (like `strip_files` not being 82// automatic) 83func SdkRepoHostFactory() android.Module { 84 return newSdkRepoHostModule() 85} 86 87func newSdkRepoHostModule() *sdkRepoHost { 88 s := &sdkRepoHost{} 89 s.AddProperties(&s.properties) 90 android.InitPackageModule(s) 91 android.InitAndroidMultiTargetsArchModule(s, android.HostSupported, android.MultilibCommon) 92 return s 93} 94 95type dependencyTag struct { 96 blueprint.BaseDependencyTag 97 android.PackagingItemAlwaysDepTag 98} 99 100// TODO(b/201696252): Evaluate whether licenses should be propagated through this dependency. 101func (d dependencyTag) PropagateLicenses() bool { 102 return false 103} 104 105var depTag = dependencyTag{} 106 107func (s *sdkRepoHost) DepsMutator(ctx android.BottomUpMutatorContext) { 108 s.AddDeps(ctx, depTag) 109} 110 111func (s *sdkRepoHost) GenerateAndroidBuildActions(ctx android.ModuleContext) { 112 dir := android.PathForModuleOut(ctx, "zip") 113 outputZipFile := dir.Join(ctx, "output.zip") 114 builder := android.NewRuleBuilder(pctx, ctx). 115 Sbox(dir, android.PathForModuleOut(ctx, "out.sbox.textproto")). 116 SandboxInputs() 117 118 // Get files from modules listed in `deps` 119 packageSpecs := s.GatherPackagingSpecs(ctx) 120 121 // Handle `deps_remap` renames 122 err := remapPackageSpecs(packageSpecs, s.properties.Deps_remap) 123 if err != nil { 124 ctx.PropertyErrorf("deps_remap", "%s", err.Error()) 125 } 126 127 s.CopySpecsToDir(ctx, builder, packageSpecs, dir) 128 129 noticeFile := android.PathForModuleOut(ctx, "NOTICES.txt") 130 android.BuildNoticeTextOutputFromLicenseMetadata( 131 ctx, noticeFile, "", "", 132 []string{ 133 android.PathForModuleInstall(ctx, "sdk-repo").String() + "/", 134 outputZipFile.String(), 135 }) 136 builder.Command().Text("cp"). 137 Input(noticeFile). 138 Text(filepath.Join(dir.String(), "NOTICE.txt")) 139 140 // Handle `merge_zips` by extracting their contents into our tmpdir 141 for _, zip := range android.PathsForModuleSrc(ctx, s.properties.Merge_zips) { 142 builder.Command(). 143 Text("unzip"). 144 Flag("-DD"). 145 Flag("-q"). 146 FlagWithArg("-d ", dir.String()). 147 Input(zip) 148 } 149 150 // Copy files from `srcs` into our tmpdir 151 for _, src := range android.PathsForModuleSrc(ctx, s.properties.Srcs) { 152 builder.Command(). 153 Text("cp").Input(src).Flag(dir.Join(ctx, src.Rel()).String()) 154 } 155 156 // Handle `strip_files` by calling the necessary strip commands 157 // 158 // Note: this stripping logic was copied over from the old Make implementation 159 // It's not using the same flags as the regular stripping support, nor does it 160 // support the array of per-module stripping options. It would be nice if we 161 // pulled the stripped versions from the CC modules, but that doesn't exist 162 // for host tools today. (And not all the things we strip are CC modules today) 163 if ctx.Darwin() { 164 macStrip := config.MacStripPath(ctx) 165 for _, strip := range s.properties.Strip_files { 166 builder.Command(). 167 Text(macStrip).Flag("-x"). 168 Flag(dir.Join(ctx, strip).String()) 169 } 170 } else { 171 llvmObjCopy := config.ClangPath(ctx, "bin/llvm-objcopy") 172 llvmStrip := config.ClangPath(ctx, "bin/llvm-strip") 173 llvmLib := config.ClangPath(ctx, "lib/x86_64-unknown-linux-gnu/libc++.so") 174 for _, strip := range s.properties.Strip_files { 175 cmd := builder.Command().Tool(llvmStrip).ImplicitTool(llvmLib).ImplicitTool(llvmObjCopy) 176 if !ctx.Windows() { 177 cmd.Flag("-x") 178 } 179 cmd.Flag(dir.Join(ctx, strip).String()) 180 } 181 } 182 183 // Fix up the line endings of all text files. This also removes executable permissions. 184 builder.Command(). 185 Text("find"). 186 Flag(dir.String()). 187 Flag("-name '*.aidl' -o -name '*.css' -o -name '*.html' -o -name '*.java'"). 188 Flag("-o -name '*.js' -o -name '*.prop' -o -name '*.template'"). 189 Flag("-o -name '*.txt' -o -name '*.windows' -o -name '*.xml' -print0"). 190 // Using -n 500 for xargs to limit the max number of arguments per call to line_endings 191 // to 500. This avoids line_endings failing with "arguments too long". 192 Text("| xargs -0 -n 500 "). 193 BuiltTool("line_endings"). 194 Flag("unix") 195 196 // Exclude some file types (roughly matching sdk.exclude.atree) 197 builder.Command(). 198 Text("find"). 199 Flag(dir.String()). 200 Flag("'('"). 201 Flag("-name '.*' -o -name '*~' -o -name 'Makefile' -o -name 'Android.mk' -o"). 202 Flag("-name '.*.swp' -o -name '.DS_Store' -o -name '*.pyc' -o -name 'OWNERS' -o"). 203 Flag("-name 'MODULE_LICENSE_*' -o -name '*.ezt' -o -name 'Android.bp'"). 204 Flag("')' -print0"). 205 Text("| xargs -0 -r rm -rf") 206 builder.Command(). 207 Text("find"). 208 Flag(dir.String()). 209 Flag("-name '_*' ! -name '__*' -print0"). 210 Text("| xargs -0 -r rm -rf") 211 212 if ctx.Windows() { 213 // Fix EOL chars to make window users happy 214 builder.Command(). 215 Text("find"). 216 Flag(dir.String()). 217 Flag("-maxdepth 2 -name '*.bat' -type f -print0"). 218 Text("| xargs -0 -r unix2dos") 219 } 220 221 // Zip up our temporary directory as the sdk-repo 222 builder.Command(). 223 BuiltTool("soong_zip"). 224 FlagWithOutput("-o ", outputZipFile). 225 FlagWithArg("-P ", proptools.StringDefault(s.properties.Base_dir, ".")). 226 FlagWithArg("-C ", dir.String()). 227 FlagWithArg("-D ", dir.String()) 228 builder.Command().Text("rm").Flag("-rf").Text(dir.String()) 229 230 builder.Build("build_sdk_repo", "Creating sdk-repo-"+s.BaseModuleName()) 231 232 osName := ctx.Os().String() 233 if osName == "linux_glibc" { 234 osName = "linux" 235 } 236 name := fmt.Sprintf("sdk-repo-%s-%s", osName, s.BaseModuleName()) 237 238 s.outputBaseName = name 239 s.outputFile = android.OptionalPathForPath(outputZipFile) 240 installPath := android.PathForModuleInstall(ctx, "sdk-repo") 241 name = name + ".zip" 242 ctx.InstallFile(installPath, name, outputZipFile) 243 // TODO(b/357908583): Temp field, remove this once we support Android Mk providers 244 s.installFile = installPath.Join(ctx, name) 245} 246 247func (s *sdkRepoHost) AndroidMk() android.AndroidMkData { 248 return android.AndroidMkData{ 249 Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) { 250 fmt.Fprintln(w, ".PHONY:", name, "sdk_repo", "sdk-repo-"+name) 251 fmt.Fprintln(w, "sdk_repo", "sdk-repo-"+name+":", s.installFile.String()) 252 253 fmt.Fprintf(w, "$(call dist-for-goals,sdk_repo sdk-repo-%s,%s:%s-FILE_NAME_TAG_PLACEHOLDER.zip)\n\n", s.BaseModuleName(), s.outputFile.String(), s.outputBaseName) 254 }, 255 } 256} 257 258func remapPackageSpecs(specs map[string]android.PackagingSpec, remaps []remapProperties) error { 259 for _, remap := range remaps { 260 for path, spec := range specs { 261 if match, err := pathtools.Match(remap.From, path); err != nil { 262 return fmt.Errorf("Error parsing %q: %v", remap.From, err) 263 } else if match { 264 newPath := remap.To 265 if pathtools.IsGlob(remap.From) { 266 rel, err := filepath.Rel(constantPartOfPattern(remap.From), path) 267 if err != nil { 268 return fmt.Errorf("Error handling %q", path) 269 } 270 newPath = filepath.Join(remap.To, rel) 271 } 272 delete(specs, path) 273 spec.SetRelPathInPackage(newPath) 274 specs[newPath] = spec 275 } 276 } 277 } 278 return nil 279} 280 281func constantPartOfPattern(pattern string) string { 282 ret := "" 283 for pattern != "" { 284 var first string 285 first, pattern = splitFirst(pattern) 286 if pathtools.IsGlob(first) { 287 return ret 288 } 289 ret = filepath.Join(ret, first) 290 } 291 return ret 292} 293 294func splitFirst(path string) (string, string) { 295 i := strings.IndexRune(path, filepath.Separator) 296 if i < 0 { 297 return path, "" 298 } 299 return path[:i], path[i+1:] 300} 301