// Copyright (C) 2018 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package vintf import ( "fmt" "io" "strings" "github.com/google/blueprint" "github.com/google/blueprint/proptools" "android/soong/android" "android/soong/kernel/configs" "android/soong/selinux" ) type dependencyTag struct { blueprint.BaseDependencyTag name string } var ( pctx = android.NewPackageContext("android/vintf") assembleVintfRule = pctx.AndroidStaticRule("assemble_vintf", blueprint.RuleParams{ Command: `${assembleVintfEnv} ${assembleVintfCmd} -i ${inputs} -o ${out} ${extraArgs}`, CommandDeps: []string{"${assembleVintfCmd}", "${AvbToolCmd}"}, Description: "assemble_vintf -i ${inputs}", }, "inputs", "extraArgs", "assembleVintfEnv") xmllintXsd = pctx.AndroidStaticRule("xmllint-xsd", blueprint.RuleParams{ Command: `$XmlLintCmd --quiet --schema $xsd $in > /dev/null && touch -a $out`, CommandDeps: []string{"$XmlLintCmd"}, Restat: true, }, "xsd") kernelConfigTag = dependencyTag{name: "kernel-config"} schemaTag = dependencyTag{name: "matrix-schema"} schemaModuleName = "compatibility_matrix_schema" ) const ( relpath = "vintf" emptyManifest = "hardware/interfaces/compatibility_matrices/manifest.empty.xml" compatibilityEmptyMatrix = "hardware/interfaces/compatibility_matrices/compatibility_matrix.empty.xml" deviceFcmType = "device_fcm" productFcmType = "product_fcm" ) type vintfCompatibilityMatrixProperties struct { // set the name of the output Stem *string // list of source compatibility matrix XML files Srcs []string // list of kernel_config modules to be combined to final output Kernel_configs []string // 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 Type *string } type vintfCompatibilityMatrixRule struct { android.ModuleBase properties vintfCompatibilityMatrixProperties genFile android.WritablePath additionalDependencies android.WritablePaths phonyOnly bool } func init() { pctx.HostBinToolVariable("assembleVintfCmd", "assemble_vintf") pctx.HostBinToolVariable("XmlLintCmd", "xmllint") pctx.HostBinToolVariable("AvbToolCmd", "avbtool") android.RegisterModuleType("vintf_compatibility_matrix", vintfCompatibilityMatrixFactory) } func vintfCompatibilityMatrixFactory() android.Module { g := &vintfCompatibilityMatrixRule{} g.AddProperties(&g.properties) android.InitAndroidArchModule(g, android.DeviceSupported, android.MultilibCommon) return g } var _ android.AndroidMkDataProvider = (*vintfCompatibilityMatrixRule)(nil) func (g *vintfCompatibilityMatrixRule) DepsMutator(ctx android.BottomUpMutatorContext) { android.ExtractSourcesDeps(ctx, g.properties.Srcs) ctx.AddDependency(ctx.Module(), kernelConfigTag, g.properties.Kernel_configs...) ctx.AddDependency(ctx.Module(), schemaTag, schemaModuleName) } func (g *vintfCompatibilityMatrixRule) timestampFilePath(ctx android.ModuleContext, path android.Path) android.WritablePath { return android.GenPathWithExt(ctx, "vintf-xmllint", path, "ts") } func (g *vintfCompatibilityMatrixRule) generateValidateBuildAction(ctx android.ModuleContext, path android.Path, schema android.Path) { timestamp := g.timestampFilePath(ctx, path) ctx.Build(pctx, android.BuildParams{ Rule: xmllintXsd, Description: "xmllint-xsd", Input: path, Output: timestamp, Implicit: schema, Args: map[string]string{ "xsd": schema.String(), }, }) g.additionalDependencies = append(g.additionalDependencies, timestamp) } func (g *vintfCompatibilityMatrixRule) getSchema(ctx android.ModuleContext) android.OptionalPath { schemaModule := ctx.GetDirectDepWithTag(schemaModuleName, schemaTag) sfp, ok := schemaModule.(android.SourceFileProducer) if !ok { ctx.ModuleErrorf("Implicit dependency %q has no srcs", ctx.OtherModuleName(schemaModule)) return android.OptionalPath{} } schemaSrcs := sfp.Srcs() if len(schemaSrcs) != 1 { ctx.PropertyErrorf(`srcs of implicit dependency %q has length %d != 1`, ctx.OtherModuleName(schemaModule), len(schemaSrcs)) return android.OptionalPath{} } return android.OptionalPathForPath(schemaSrcs[0]) } func (g *vintfCompatibilityMatrixRule) GenerateAndroidBuildActions(ctx android.ModuleContext) { // Types attribute only allow `device_fcm` or `product_fcm` if set and only restricted it being used under // `hardware/interfaces/compatibility_matrices` to prevent accidental external usages. matrixType := proptools.String(g.properties.Type) if matrixType != "" { if matrixType != deviceFcmType && matrixType != productFcmType { panic(fmt.Errorf("The attribute 'type' value must be either 'device_fcm' or 'product_fcm' if set!")) } if !strings.HasPrefix(android.PathForModuleSrc(ctx).String(), "hardware/interfaces/compatibility_matrices") { panic(fmt.Errorf("Attribute type can only be set for module under `hardware/interfaces/compatibility_matrices`!")) } if (len(g.properties.Srcs) + len(g.properties.Kernel_configs)) > 0 { 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.")) } } outputFilename := proptools.String(g.properties.Stem) if outputFilename == "" { outputFilename = g.Name() } schema := g.getSchema(ctx) if !schema.Valid() { return } inputPaths := android.PathsForModuleSrc(ctx, g.properties.Srcs) for _, srcPath := range inputPaths { g.generateValidateBuildAction(ctx, srcPath, schema.Path()) } // No need to validate matrices from kernel configs because they are generated by // assemble_vintf. ctx.VisitDirectDepsWithTag(kernelConfigTag, func(m android.Module) { if k, ok := m.(*configs.KernelConfigRule); ok { inputPaths = append(inputPaths, k.OutputPath()) } else { ctx.PropertyErrorf("kernel_configs", "module %q is not a kernel_config", ctx.OtherModuleName(m)) } }) // For product_compatibility_matrix.xml the source is from the product configuration // DEVICE_PRODUCT_COMPATIBILITY_MATRIX_FILE. extraArgs := []string{} if matrixType == productFcmType { productMatrixs := android.PathsForSource(ctx, ctx.Config().DeviceProductCompatibilityMatrixFile()) if len(productMatrixs) > 0 { inputPaths = append(inputPaths, productMatrixs...) extraArgs = append(extraArgs, "-c", android.PathForSource(ctx, emptyManifest).String()) } else { // For product_fcm, if DEVICE_PRODUCT_COMPATIBILITY_MATRIX_FILE not set, treat it as a phony target without any output generated. g.phonyOnly = true return } } // For framework_compatibility_matrix.device.xml the source may come from the product configuration // DEVICE_FRAMEWORK_COMPATIBILITY_MATRIX_FILE or use compatibilityEmptyMatrix if not set. We can't // use a phony target because we still need to install framework_compatibility_matrix.device.xml to // include sepolicy versions. frameworkRuleImplicits := []android.Path{} if matrixType == deviceFcmType { frameworkMatrixs := android.PathsForSource(ctx, ctx.Config().DeviceFrameworkCompatibilityMatrixFile()) if len(frameworkMatrixs) > 0 { inputPaths = append(inputPaths, frameworkMatrixs...) // Generate BuildAction for generating the check manifest. emptyManifestPath := android.PathForSource(ctx, emptyManifest) genCheckManifest := android.PathForModuleGen(ctx, "manifest.check.xml") checkManifestInputs := []android.Path{emptyManifestPath} genCheckManifestEnvs := []string{ "BOARD_SEPOLICY_VERS=" + ctx.DeviceConfig().BoardSepolicyVers(), "VINTF_IGNORE_TARGET_FCM_VERSION=true", } ctx.Build(pctx, android.BuildParams{ Rule: assembleVintfRule, Description: "Framework Check Manifest", Implicits: checkManifestInputs, Output: genCheckManifest, Args: map[string]string{ "inputs": android.PathForSource(ctx, emptyManifest).String(), "extraArgs": "", "assembleVintfEnv": strings.Join(genCheckManifestEnvs, " "), }, }) frameworkRuleImplicits = append(frameworkRuleImplicits, genCheckManifest) extraArgs = append(extraArgs, "-c", genCheckManifest.String()) } else { inputPaths = append(inputPaths, android.PathForSource(ctx, compatibilityEmptyMatrix)) } } g.genFile = android.PathForModuleGen(ctx, outputFilename) frameworkRuleImplicits = append(frameworkRuleImplicits, inputPaths...) ctx.Build(pctx, android.BuildParams{ Rule: assembleVintfRule, Description: "Framework Compatibility Matrix", Implicits: frameworkRuleImplicits, Output: g.genFile, Args: map[string]string{ "inputs": strings.Join(inputPaths.Strings(), ":"), "extraArgs": strings.Join(extraArgs, " "), "assembleVintfEnv": g.getAssembleVintfEnv(ctx), }, }) g.generateValidateBuildAction(ctx, g.genFile, schema.Path()) ctx.InstallFile(android.PathForModuleInstall(ctx, "etc", relpath), outputFilename, g.genFile) } func (g *vintfCompatibilityMatrixRule) getAssembleVintfEnv(ctx android.ModuleContext) string { if proptools.String(g.properties.Type) == deviceFcmType { assembleVintfEnvs := []string{ // POLICYVERS defined in system/sepolicy/build/soong/policy.go fmt.Sprintf("POLICYVERS=%d", selinux.PolicyVers), fmt.Sprintf("PLATFORM_SEPOLICY_VERSION=%s", ctx.DeviceConfig().PlatformSepolicyVersion()), fmt.Sprintf("PLATFORM_SEPOLICY_COMPAT_VERSIONS=\"%s\"", strings.Join(ctx.DeviceConfig().PlatformSepolicyCompatVersions(), " ")), } if ctx.Config().BoardAvbEnable() { assembleVintfEnvs = append(assembleVintfEnvs, fmt.Sprintf("FRAMEWORK_VBMETA_VERSION=\"$$(${AvbToolCmd} add_hashtree_footer --print_required_libavb_version %s)\"", strings.Join(ctx.Config().BoardAvbSystemAddHashtreeFooterArgs(), " "))) } else { assembleVintfEnvs = append(assembleVintfEnvs, "FRAMEWORK_VBMETA_VERSION=\"0.0\"") } return strings.Join(assembleVintfEnvs, " ") } return "" } func (g *vintfCompatibilityMatrixRule) AndroidMk() android.AndroidMkData { if g.phonyOnly { return android.AndroidMkData{ Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) { fmt.Fprintln(w, "\ninclude $(CLEAR_VARS)", " # vintf.vintf_compatibility_matrix") fmt.Fprintln(w, "LOCAL_PATH :=", moduleDir) fmt.Fprintln(w, "LOCAL_MODULE :=", name) fmt.Fprintln(w, "include $(BUILD_PHONY_PACKAGE)") }, } } return android.AndroidMkData{ Class: "ETC", OutputFile: android.OptionalPathForPath(g.genFile), Extra: []android.AndroidMkExtraFunc{ func(w io.Writer, outputFile android.Path) { fmt.Fprintln(w, "LOCAL_MODULE_RELATIVE_PATH :=", relpath) if proptools.String(g.properties.Stem) != "" { fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", proptools.String(g.properties.Stem)) } for _, path := range g.additionalDependencies { fmt.Fprintln(w, "LOCAL_ADDITIONAL_DEPENDENCIES +=", path.String()) } }, }, } }