1// Copyright (C) 2018 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 vintf 16 17import ( 18 "fmt" 19 "io" 20 "strings" 21 22 "github.com/google/blueprint" 23 "github.com/google/blueprint/proptools" 24 25 "android/soong/android" 26 "android/soong/kernel/configs" 27 "android/soong/selinux" 28) 29 30type dependencyTag struct { 31 blueprint.BaseDependencyTag 32 name string 33} 34 35var ( 36 pctx = android.NewPackageContext("android/vintf") 37 38 assembleVintfRule = pctx.AndroidStaticRule("assemble_vintf", blueprint.RuleParams{ 39 Command: `${assembleVintfEnv} ${assembleVintfCmd} -i ${inputs} -o ${out} ${extraArgs}`, 40 CommandDeps: []string{"${assembleVintfCmd}", "${AvbToolCmd}"}, 41 Description: "assemble_vintf -i ${inputs}", 42 }, "inputs", "extraArgs", "assembleVintfEnv") 43 44 xmllintXsd = pctx.AndroidStaticRule("xmllint-xsd", blueprint.RuleParams{ 45 Command: `$XmlLintCmd --quiet --schema $xsd $in > /dev/null && touch -a $out`, 46 CommandDeps: []string{"$XmlLintCmd"}, 47 Restat: true, 48 }, "xsd") 49 50 kernelConfigTag = dependencyTag{name: "kernel-config"} 51 schemaTag = dependencyTag{name: "matrix-schema"} 52 schemaModuleName = "compatibility_matrix_schema" 53) 54 55const ( 56 relpath = "vintf" 57 emptyManifest = "hardware/interfaces/compatibility_matrices/manifest.empty.xml" 58 compatibilityEmptyMatrix = "hardware/interfaces/compatibility_matrices/compatibility_matrix.empty.xml" 59 deviceFcmType = "device_fcm" 60 productFcmType = "product_fcm" 61) 62 63type vintfCompatibilityMatrixProperties struct { 64 // set the name of the output 65 Stem *string 66 67 // list of source compatibility matrix XML files 68 Srcs []string 69 70 // list of kernel_config modules to be combined to final output 71 Kernel_configs []string 72 73 // Type of the FCM type, the allowed type are device_fcm and product_fcm and it should only be used under hardware/interfaces/compatibility_matrices 74 Type *string 75} 76 77type vintfCompatibilityMatrixRule struct { 78 android.ModuleBase 79 properties vintfCompatibilityMatrixProperties 80 81 genFile android.WritablePath 82 additionalDependencies android.WritablePaths 83 phonyOnly bool 84} 85 86func init() { 87 pctx.HostBinToolVariable("assembleVintfCmd", "assemble_vintf") 88 pctx.HostBinToolVariable("XmlLintCmd", "xmllint") 89 pctx.HostBinToolVariable("AvbToolCmd", "avbtool") 90 android.RegisterModuleType("vintf_compatibility_matrix", vintfCompatibilityMatrixFactory) 91} 92 93func vintfCompatibilityMatrixFactory() android.Module { 94 g := &vintfCompatibilityMatrixRule{} 95 g.AddProperties(&g.properties) 96 android.InitAndroidArchModule(g, android.DeviceSupported, android.MultilibCommon) 97 return g 98} 99 100var _ android.AndroidMkDataProvider = (*vintfCompatibilityMatrixRule)(nil) 101 102func (g *vintfCompatibilityMatrixRule) DepsMutator(ctx android.BottomUpMutatorContext) { 103 android.ExtractSourcesDeps(ctx, g.properties.Srcs) 104 ctx.AddDependency(ctx.Module(), kernelConfigTag, g.properties.Kernel_configs...) 105 ctx.AddDependency(ctx.Module(), schemaTag, schemaModuleName) 106} 107 108func (g *vintfCompatibilityMatrixRule) timestampFilePath(ctx android.ModuleContext, path android.Path) android.WritablePath { 109 return android.GenPathWithExt(ctx, "vintf-xmllint", path, "ts") 110} 111 112func (g *vintfCompatibilityMatrixRule) generateValidateBuildAction(ctx android.ModuleContext, path android.Path, schema android.Path) { 113 timestamp := g.timestampFilePath(ctx, path) 114 ctx.Build(pctx, android.BuildParams{ 115 Rule: xmllintXsd, 116 Description: "xmllint-xsd", 117 Input: path, 118 Output: timestamp, 119 Implicit: schema, 120 Args: map[string]string{ 121 "xsd": schema.String(), 122 }, 123 }) 124 g.additionalDependencies = append(g.additionalDependencies, timestamp) 125} 126 127func (g *vintfCompatibilityMatrixRule) getSchema(ctx android.ModuleContext) android.OptionalPath { 128 schemaModule := ctx.GetDirectDepWithTag(schemaModuleName, schemaTag) 129 sfp, ok := schemaModule.(android.SourceFileProducer) 130 if !ok { 131 ctx.ModuleErrorf("Implicit dependency %q has no srcs", ctx.OtherModuleName(schemaModule)) 132 return android.OptionalPath{} 133 } 134 135 schemaSrcs := sfp.Srcs() 136 if len(schemaSrcs) != 1 { 137 ctx.PropertyErrorf(`srcs of implicit dependency %q has length %d != 1`, ctx.OtherModuleName(schemaModule), len(schemaSrcs)) 138 return android.OptionalPath{} 139 } 140 return android.OptionalPathForPath(schemaSrcs[0]) 141} 142 143func (g *vintfCompatibilityMatrixRule) GenerateAndroidBuildActions(ctx android.ModuleContext) { 144 // Types attribute only allow `device_fcm` or `product_fcm` if set and only restricted it being used under 145 // `hardware/interfaces/compatibility_matrices` to prevent accidental external usages. 146 matrixType := proptools.String(g.properties.Type) 147 if matrixType != "" { 148 if matrixType != deviceFcmType && matrixType != productFcmType { 149 panic(fmt.Errorf("The attribute 'type' value must be either 'device_fcm' or 'product_fcm' if set!")) 150 } 151 if !strings.HasPrefix(android.PathForModuleSrc(ctx).String(), "hardware/interfaces/compatibility_matrices") { 152 panic(fmt.Errorf("Attribute type can only be set for module under `hardware/interfaces/compatibility_matrices`!")) 153 } 154 if (len(g.properties.Srcs) + len(g.properties.Kernel_configs)) > 0 { 155 panic(fmt.Errorf("Attribute 'type' and 'srcs' or 'kernel_configs' should not set simultaneously! To update inputs for this rule, edit vintf_compatibility_matrix.go directly.")) 156 } 157 } 158 159 outputFilename := proptools.String(g.properties.Stem) 160 if outputFilename == "" { 161 outputFilename = g.Name() 162 } 163 164 schema := g.getSchema(ctx) 165 if !schema.Valid() { 166 return 167 } 168 169 inputPaths := android.PathsForModuleSrc(ctx, g.properties.Srcs) 170 for _, srcPath := range inputPaths { 171 g.generateValidateBuildAction(ctx, srcPath, schema.Path()) 172 } 173 174 // No need to validate matrices from kernel configs because they are generated by 175 // assemble_vintf. 176 ctx.VisitDirectDepsWithTag(kernelConfigTag, func(m android.Module) { 177 if k, ok := m.(*configs.KernelConfigRule); ok { 178 inputPaths = append(inputPaths, k.OutputPath()) 179 } else { 180 ctx.PropertyErrorf("kernel_configs", 181 "module %q is not a kernel_config", ctx.OtherModuleName(m)) 182 } 183 }) 184 185 // For product_compatibility_matrix.xml the source is from the product configuration 186 // DEVICE_PRODUCT_COMPATIBILITY_MATRIX_FILE. 187 extraArgs := []string{} 188 if matrixType == productFcmType { 189 productMatrixs := android.PathsForSource(ctx, ctx.Config().DeviceProductCompatibilityMatrixFile()) 190 if len(productMatrixs) > 0 { 191 inputPaths = append(inputPaths, productMatrixs...) 192 extraArgs = append(extraArgs, "-c", android.PathForSource(ctx, emptyManifest).String()) 193 } else { 194 // For product_fcm, if DEVICE_PRODUCT_COMPATIBILITY_MATRIX_FILE not set, treat it as a phony target without any output generated. 195 g.phonyOnly = true 196 return 197 } 198 } 199 200 // For framework_compatibility_matrix.device.xml the source may come from the product configuration 201 // DEVICE_FRAMEWORK_COMPATIBILITY_MATRIX_FILE or use compatibilityEmptyMatrix if not set. We can't 202 // use a phony target because we still need to install framework_compatibility_matrix.device.xml to 203 // include sepolicy versions. 204 frameworkRuleImplicits := []android.Path{} 205 206 if matrixType == deviceFcmType { 207 frameworkMatrixs := android.PathsForSource(ctx, ctx.Config().DeviceFrameworkCompatibilityMatrixFile()) 208 if len(frameworkMatrixs) > 0 { 209 inputPaths = append(inputPaths, frameworkMatrixs...) 210 211 // Generate BuildAction for generating the check manifest. 212 emptyManifestPath := android.PathForSource(ctx, emptyManifest) 213 genCheckManifest := android.PathForModuleGen(ctx, "manifest.check.xml") 214 checkManifestInputs := []android.Path{emptyManifestPath} 215 genCheckManifestEnvs := []string{ 216 "BOARD_SEPOLICY_VERS=" + ctx.DeviceConfig().BoardSepolicyVers(), 217 "VINTF_IGNORE_TARGET_FCM_VERSION=true", 218 } 219 220 ctx.Build(pctx, android.BuildParams{ 221 Rule: assembleVintfRule, 222 Description: "Framework Check Manifest", 223 Implicits: checkManifestInputs, 224 Output: genCheckManifest, 225 Args: map[string]string{ 226 "inputs": android.PathForSource(ctx, emptyManifest).String(), 227 "extraArgs": "", 228 "assembleVintfEnv": strings.Join(genCheckManifestEnvs, " "), 229 }, 230 }) 231 232 frameworkRuleImplicits = append(frameworkRuleImplicits, genCheckManifest) 233 extraArgs = append(extraArgs, "-c", genCheckManifest.String()) 234 } else { 235 inputPaths = append(inputPaths, android.PathForSource(ctx, compatibilityEmptyMatrix)) 236 } 237 } 238 239 g.genFile = android.PathForModuleGen(ctx, outputFilename) 240 frameworkRuleImplicits = append(frameworkRuleImplicits, inputPaths...) 241 242 ctx.Build(pctx, android.BuildParams{ 243 Rule: assembleVintfRule, 244 Description: "Framework Compatibility Matrix", 245 Implicits: frameworkRuleImplicits, 246 Output: g.genFile, 247 Args: map[string]string{ 248 "inputs": strings.Join(inputPaths.Strings(), ":"), 249 "extraArgs": strings.Join(extraArgs, " "), 250 "assembleVintfEnv": g.getAssembleVintfEnv(ctx), 251 }, 252 }) 253 g.generateValidateBuildAction(ctx, g.genFile, schema.Path()) 254 255 ctx.InstallFile(android.PathForModuleInstall(ctx, "etc", relpath), outputFilename, g.genFile) 256} 257 258func (g *vintfCompatibilityMatrixRule) getAssembleVintfEnv(ctx android.ModuleContext) string { 259 if proptools.String(g.properties.Type) == deviceFcmType { 260 assembleVintfEnvs := []string{ 261 // POLICYVERS defined in system/sepolicy/build/soong/policy.go 262 fmt.Sprintf("POLICYVERS=%d", selinux.PolicyVers), 263 fmt.Sprintf("PLATFORM_SEPOLICY_VERSION=%s", ctx.DeviceConfig().PlatformSepolicyVersion()), 264 fmt.Sprintf("PLATFORM_SEPOLICY_COMPAT_VERSIONS=\"%s\"", strings.Join(ctx.DeviceConfig().PlatformSepolicyCompatVersions(), " ")), 265 } 266 267 if ctx.Config().BoardAvbEnable() { 268 assembleVintfEnvs = append(assembleVintfEnvs, fmt.Sprintf("FRAMEWORK_VBMETA_VERSION=\"$$(${AvbToolCmd} add_hashtree_footer --print_required_libavb_version %s)\"", strings.Join(ctx.Config().BoardAvbSystemAddHashtreeFooterArgs(), " "))) 269 } else { 270 assembleVintfEnvs = append(assembleVintfEnvs, "FRAMEWORK_VBMETA_VERSION=\"0.0\"") 271 } 272 273 return strings.Join(assembleVintfEnvs, " ") 274 } 275 276 return "" 277} 278 279func (g *vintfCompatibilityMatrixRule) AndroidMk() android.AndroidMkData { 280 if g.phonyOnly { 281 return android.AndroidMkData{ 282 Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) { 283 fmt.Fprintln(w, "\ninclude $(CLEAR_VARS)", " # vintf.vintf_compatibility_matrix") 284 fmt.Fprintln(w, "LOCAL_PATH :=", moduleDir) 285 fmt.Fprintln(w, "LOCAL_MODULE :=", name) 286 fmt.Fprintln(w, "include $(BUILD_PHONY_PACKAGE)") 287 }, 288 } 289 } 290 291 return android.AndroidMkData{ 292 Class: "ETC", 293 OutputFile: android.OptionalPathForPath(g.genFile), 294 Extra: []android.AndroidMkExtraFunc{ 295 func(w io.Writer, outputFile android.Path) { 296 fmt.Fprintln(w, "LOCAL_MODULE_RELATIVE_PATH :=", relpath) 297 if proptools.String(g.properties.Stem) != "" { 298 fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", proptools.String(g.properties.Stem)) 299 } 300 for _, path := range g.additionalDependencies { 301 fmt.Fprintln(w, "LOCAL_ADDITIONAL_DEPENDENCIES +=", path.String()) 302 } 303 }, 304 }, 305 } 306} 307