1// Copyright 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 snapshot 16 17import ( 18 "encoding/json" 19 "fmt" 20 "path/filepath" 21 "sort" 22 "strings" 23 24 "github.com/google/blueprint" 25 "github.com/google/blueprint/proptools" 26 27 "android/soong/android" 28) 29 30// 31// The host_snapshot module creates a snapshot of the modules defined in 32// the deps property. The modules within the deps property (host tools) 33// are ones that return a valid path via HostToolPath() of the 34// HostToolProvider. The created snapshot contains the binaries and any 35// transitive PackagingSpecs of the included host tools, along with a JSON 36// meta file. 37// 38// The snapshot is installed into a source tree via 39// development/vendor_snapshot/update.py, the included modules are 40// provided as preferred prebuilts. 41// 42// To determine which tools to include in the host snapshot see 43// host_fake_snapshot.go. 44 45func init() { 46 registerHostBuildComponents(android.InitRegistrationContext) 47} 48 49func registerHostBuildComponents(ctx android.RegistrationContext) { 50 ctx.RegisterModuleType("host_snapshot", hostSnapshotFactory) 51} 52 53// Relative installation path 54type RelativeInstallPath interface { 55 RelativeInstallPath() string 56} 57 58type hostSnapshot struct { 59 android.ModuleBase 60 android.PackagingBase 61 62 outputFile android.OutputPath 63 installDir android.InstallPath 64} 65 66type ProcMacro interface { 67 ProcMacro() bool 68 CrateName() string 69} 70 71func hostSnapshotFactory() android.Module { 72 module := &hostSnapshot{} 73 initHostToolsModule(module) 74 return module 75} 76func initHostToolsModule(module *hostSnapshot) { 77 android.InitPackageModule(module) 78 android.InitAndroidMultiTargetsArchModule(module, android.HostSupported, android.MultilibCommon) 79} 80 81var dependencyTag = struct { 82 blueprint.BaseDependencyTag 83 android.InstallAlwaysNeededDependencyTag 84 android.PackagingItemAlwaysDepTag 85}{} 86 87func (f *hostSnapshot) DepsMutator(ctx android.BottomUpMutatorContext) { 88 f.AddDeps(ctx, dependencyTag) 89} 90func (f *hostSnapshot) installFileName() string { 91 return f.Name() + ".zip" 92} 93 94// Create zipfile with JSON description, notice files... for dependent modules 95func (f *hostSnapshot) CreateMetaData(ctx android.ModuleContext, fileName string) android.OutputPath { 96 var jsonData []SnapshotJsonFlags 97 var metaPaths android.Paths 98 99 installedNotices := make(map[string]bool) 100 metaZipFile := android.PathForModuleOut(ctx, fileName).OutputPath 101 102 // Create JSON file based on the direct dependencies 103 ctx.VisitDirectDeps(func(dep android.Module) { 104 desc := hostJsonDesc(dep) 105 if desc != nil { 106 jsonData = append(jsonData, *desc) 107 } 108 for _, notice := range dep.EffectiveLicenseFiles() { 109 if _, ok := installedNotices[notice.String()]; !ok { 110 installedNotices[notice.String()] = true 111 noticeOut := android.PathForModuleOut(ctx, "NOTICE_FILES", notice.String()).OutputPath 112 CopyFileToOutputPathRule(pctx, ctx, notice, noticeOut) 113 metaPaths = append(metaPaths, noticeOut) 114 } 115 } 116 }) 117 // Sort notice paths and json data for repeatble build 118 sort.Slice(jsonData, func(i, j int) bool { 119 return (jsonData[i].ModuleName < jsonData[j].ModuleName) 120 }) 121 sort.Slice(metaPaths, func(i, j int) bool { 122 return (metaPaths[i].String() < metaPaths[j].String()) 123 }) 124 125 marsh, err := json.Marshal(jsonData) 126 if err != nil { 127 ctx.ModuleErrorf("host snapshot json marshal failure: %#v", err) 128 return android.OutputPath{} 129 } 130 131 jsonZipFile := android.PathForModuleOut(ctx, "host_snapshot.json").OutputPath 132 metaPaths = append(metaPaths, jsonZipFile) 133 rspFile := android.PathForModuleOut(ctx, "host_snapshot.rsp").OutputPath 134 android.WriteFileRule(ctx, jsonZipFile, string(marsh)) 135 136 builder := android.NewRuleBuilder(pctx, ctx) 137 138 builder.Command(). 139 BuiltTool("soong_zip"). 140 FlagWithArg("-C ", android.PathForModuleOut(ctx).OutputPath.String()). 141 FlagWithOutput("-o ", metaZipFile). 142 FlagWithRspFileInputList("-r ", rspFile, metaPaths) 143 builder.Build("zip_meta", fmt.Sprintf("zipping meta data for %s", ctx.ModuleName())) 144 145 return metaZipFile 146} 147 148// Create the host tool zip file 149func (f *hostSnapshot) GenerateAndroidBuildActions(ctx android.ModuleContext) { 150 // Create a zip file for the binaries, and a zip of the meta data, then merge zips 151 depsZipFile := android.PathForModuleOut(ctx, f.Name()+"_deps.zip").OutputPath 152 modsZipFile := android.PathForModuleOut(ctx, f.Name()+"_mods.zip").OutputPath 153 f.outputFile = android.PathForModuleOut(ctx, f.installFileName()).OutputPath 154 155 f.installDir = android.PathForModuleInstall(ctx) 156 157 f.CopyDepsToZip(ctx, f.GatherPackagingSpecs(ctx), depsZipFile) 158 159 builder := android.NewRuleBuilder(pctx, ctx) 160 builder.Command(). 161 BuiltTool("zip2zip"). 162 FlagWithInput("-i ", depsZipFile). 163 FlagWithOutput("-o ", modsZipFile). 164 Text("**/*:" + proptools.ShellEscape(f.installDir.String())) 165 166 metaZipFile := f.CreateMetaData(ctx, f.Name()+"_meta.zip") 167 168 builder.Command(). 169 BuiltTool("merge_zips"). 170 Output(f.outputFile). 171 Input(metaZipFile). 172 Input(modsZipFile) 173 174 builder.Build("manifest", fmt.Sprintf("Adding manifest %s", f.installFileName())) 175 ctx.InstallFile(f.installDir, f.installFileName(), f.outputFile) 176 177} 178 179// Implements android.AndroidMkEntriesProvider 180func (f *hostSnapshot) AndroidMkEntries() []android.AndroidMkEntries { 181 return []android.AndroidMkEntries{android.AndroidMkEntries{ 182 Class: "ETC", 183 OutputFile: android.OptionalPathForPath(f.outputFile), 184 DistFiles: android.MakeDefaultDistFiles(f.outputFile), 185 ExtraEntries: []android.AndroidMkExtraEntriesFunc{ 186 func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { 187 entries.SetString("LOCAL_MODULE_PATH", f.installDir.String()) 188 entries.SetString("LOCAL_INSTALLED_MODULE_STEM", f.installFileName()) 189 }, 190 }, 191 }} 192} 193 194// Get host tools path and relative install string helpers 195func hostToolPath(m android.Module) android.OptionalPath { 196 if provider, ok := m.(android.HostToolProvider); ok { 197 return provider.HostToolPath() 198 } 199 return android.OptionalPath{} 200 201} 202func hostRelativePathString(m android.Module) string { 203 var outString string 204 if rel, ok := m.(RelativeInstallPath); ok { 205 outString = rel.RelativeInstallPath() 206 } 207 return outString 208} 209 210// Create JSON description for given module, only create descriptions for binary modules 211// and rust_proc_macro modules which provide a valid HostToolPath 212func hostJsonDesc(m android.Module) *SnapshotJsonFlags { 213 path := hostToolPath(m) 214 relPath := hostRelativePathString(m) 215 procMacro := false 216 moduleStem := filepath.Base(path.String()) 217 crateName := "" 218 219 if pm, ok := m.(ProcMacro); ok && pm.ProcMacro() { 220 procMacro = pm.ProcMacro() 221 moduleStem = strings.TrimSuffix(moduleStem, filepath.Ext(moduleStem)) 222 crateName = pm.CrateName() 223 } 224 225 if path.Valid() && path.String() != "" { 226 props := &SnapshotJsonFlags{ 227 ModuleStemName: moduleStem, 228 Filename: path.String(), 229 Required: append(m.HostRequiredModuleNames(), m.RequiredModuleNames()...), 230 RelativeInstallPath: relPath, 231 RustProcMacro: procMacro, 232 CrateName: crateName, 233 } 234 props.InitBaseSnapshotProps(m) 235 return props 236 } 237 return nil 238} 239