Export provenance metadata for prebuilt APKs and APEXes.

Bug: 217434690
Test: atest --host gen_provenance_metadata_test
Test: m provenance_metadata

Change-Id: I91c184b6e6fe5ccfc3fc65b55b09e7a3da9502a0
This commit is contained in:
Wei Li 2022-03-18 17:33:24 -07:00
parent 85b935eff2
commit 340ee8e699
15 changed files with 672 additions and 41 deletions

View file

@ -14,6 +14,7 @@ bootstrap_go_package {
"soong-cc",
"soong-filesystem",
"soong-java",
"soong-provenance",
"soong-python",
"soong-rust",
"soong-sh",

View file

@ -3889,7 +3889,7 @@ func TestVndkApexWithBinder32(t *testing.T) {
}),
withBinder32bit,
withTargets(map[android.OsType][]android.Target{
android.Android: []android.Target{
android.Android: {
{Os: android.Android, Arch: android.Arch{ArchType: android.Arm, ArchVariant: "armv7-a-neon", Abi: []string{"armeabi-v7a"}},
NativeBridge: android.NativeBridgeDisabled, NativeBridgeHostArchName: "", NativeBridgeRelativePath: ""},
},
@ -4570,12 +4570,20 @@ func TestPrebuilt(t *testing.T) {
}
`)
prebuilt := ctx.ModuleForTests("myapex", "android_common_myapex").Module().(*Prebuilt)
testingModule := ctx.ModuleForTests("myapex", "android_common_myapex")
prebuilt := testingModule.Module().(*Prebuilt)
expectedInput := "myapex-arm64.apex"
if prebuilt.inputApex.String() != expectedInput {
t.Errorf("inputApex invalid. expected: %q, actual: %q", expectedInput, prebuilt.inputApex.String())
}
android.AssertStringDoesContain(t, "Invalid provenance metadata file",
prebuilt.ProvenanceMetaDataFile().String(), "soong/.intermediates/provenance_metadata/myapex/provenance_metadata.textproto")
rule := testingModule.Rule("genProvenanceMetaData")
android.AssertStringEquals(t, "Invalid input", "myapex-arm64.apex", rule.Inputs[0].String())
android.AssertStringEquals(t, "Invalid output", "out/soong/.intermediates/provenance_metadata/myapex/provenance_metadata.textproto", rule.Output.String())
android.AssertStringEquals(t, "Invalid args", "myapex", rule.Args["module_name"])
android.AssertStringEquals(t, "Invalid args", "/system/apex/myapex.apex", rule.Args["install_path"])
}
func TestPrebuiltMissingSrc(t *testing.T) {
@ -4595,12 +4603,18 @@ func TestPrebuiltFilenameOverride(t *testing.T) {
}
`)
p := ctx.ModuleForTests("myapex", "android_common_myapex").Module().(*Prebuilt)
testingModule := ctx.ModuleForTests("myapex", "android_common_myapex")
p := testingModule.Module().(*Prebuilt)
expected := "notmyapex.apex"
if p.installFilename != expected {
t.Errorf("installFilename invalid. expected: %q, actual: %q", expected, p.installFilename)
}
rule := testingModule.Rule("genProvenanceMetaData")
android.AssertStringEquals(t, "Invalid input", "myapex-arm.apex", rule.Inputs[0].String())
android.AssertStringEquals(t, "Invalid output", "out/soong/.intermediates/provenance_metadata/myapex/provenance_metadata.textproto", rule.Output.String())
android.AssertStringEquals(t, "Invalid args", "myapex", rule.Args["module_name"])
android.AssertStringEquals(t, "Invalid args", "/system/apex/notmyapex.apex", rule.Args["install_path"])
}
func TestApexSetFilenameOverride(t *testing.T) {
@ -4643,13 +4657,19 @@ func TestPrebuiltOverrides(t *testing.T) {
}
`)
p := ctx.ModuleForTests("myapex.prebuilt", "android_common_myapex.prebuilt").Module().(*Prebuilt)
testingModule := ctx.ModuleForTests("myapex.prebuilt", "android_common_myapex.prebuilt")
p := testingModule.Module().(*Prebuilt)
expected := []string{"myapex"}
actual := android.AndroidMkEntriesForTest(t, ctx, p)[0].EntryMap["LOCAL_OVERRIDES_MODULES"]
if !reflect.DeepEqual(actual, expected) {
t.Errorf("Incorrect LOCAL_OVERRIDES_MODULES value '%s', expected '%s'", actual, expected)
}
rule := testingModule.Rule("genProvenanceMetaData")
android.AssertStringEquals(t, "Invalid input", "myapex-arm.apex", rule.Inputs[0].String())
android.AssertStringEquals(t, "Invalid output", "out/soong/.intermediates/provenance_metadata/myapex.prebuilt/provenance_metadata.textproto", rule.Output.String())
android.AssertStringEquals(t, "Invalid args", "myapex.prebuilt", rule.Args["module_name"])
android.AssertStringEquals(t, "Invalid args", "/system/apex/myapex.prebuilt.apex", rule.Args["install_path"])
}
func TestPrebuiltApexName(t *testing.T) {

View file

@ -23,7 +23,7 @@ import (
"android/soong/android"
"android/soong/java"
"android/soong/provenance"
"github.com/google/blueprint"
"github.com/google/blueprint/proptools"
)
@ -482,6 +482,8 @@ type Prebuilt struct {
properties PrebuiltProperties
inputApex android.Path
provenanceMetaDataFile android.OutputPath
}
type ApexFileProperties struct {
@ -778,9 +780,14 @@ func (p *Prebuilt) GenerateAndroidBuildActions(ctx android.ModuleContext) {
if p.installable() {
p.installedFile = ctx.InstallFile(p.installDir, p.installFilename, p.inputApex, p.compatSymlinks.Paths()...)
p.provenanceMetaDataFile = provenance.GenerateArtifactProvenanceMetaData(ctx, p.inputApex, p.installedFile)
}
}
func (p *Prebuilt) ProvenanceMetaDataFile() android.OutputPath {
return p.provenanceMetaDataFile
}
// prebuiltApexExtractorModule is a private module type that is only created by the prebuilt_apex
// module. It extracts the correct apex to use and makes it available for use by apex_set.
type prebuiltApexExtractorModule struct {

View file

@ -25,6 +25,7 @@ blueprint_go_binary {
"golang-protobuf-android",
"soong",
"soong-android",
"soong-provenance",
"soong-bp2build",
"soong-ui-metrics_proto",
],

View file

@ -15,6 +15,7 @@ bootstrap_go_package {
"soong-dexpreopt",
"soong-genrule",
"soong-java-config",
"soong-provenance",
"soong-python",
"soong-remoteexec",
"soong-tradefed",

View file

@ -22,6 +22,7 @@ import (
"github.com/google/blueprint/proptools"
"android/soong/android"
"android/soong/provenance"
)
func init() {
@ -57,6 +58,8 @@ type AndroidAppImport struct {
installPath android.InstallPath
hideApexVariantFromMake bool
provenanceMetaDataFile android.OutputPath
}
type AndroidAppImportProperties struct {
@ -343,6 +346,8 @@ func (a *AndroidAppImport) generateAndroidBuildActions(ctx android.ModuleContext
if apexInfo.IsForPlatform() {
a.installPath = ctx.InstallFile(installDir, apkFilename, a.outputFile)
artifactPath := android.PathForModuleSrc(ctx, *a.properties.Apk)
a.provenanceMetaDataFile = provenance.GenerateArtifactProvenanceMetaData(ctx, artifactPath, a.installPath)
}
// TODO: androidmk converter jni libs
@ -368,6 +373,10 @@ func (a *AndroidAppImport) Certificate() Certificate {
return a.certificate
}
func (a *AndroidAppImport) ProvenanceMetaDataFile() android.OutputPath {
return a.provenanceMetaDataFile
}
var dpiVariantGroupType reflect.Type
var archVariantGroupType reflect.Type
var supportedDpis = []string{"ldpi", "mdpi", "hdpi", "xhdpi", "xxhdpi", "xxxhdpi"}

View file

@ -53,6 +53,11 @@ func TestAndroidAppImport(t *testing.T) {
if expected != signingFlag {
t.Errorf("Incorrect signing flags, expected: %q, got: %q", expected, signingFlag)
}
rule := variant.Rule("genProvenanceMetaData")
android.AssertStringEquals(t, "Invalid input", "prebuilts/apk/app.apk", rule.Inputs[0].String())
android.AssertStringEquals(t, "Invalid output", "out/soong/.intermediates/provenance_metadata/foo/provenance_metadata.textproto", rule.Output.String())
android.AssertStringEquals(t, "Invalid args", "foo", rule.Args["module_name"])
android.AssertStringEquals(t, "Invalid args", "/system/app/foo/foo.apk", rule.Args["install_path"])
}
func TestAndroidAppImport_NoDexPreopt(t *testing.T) {
@ -74,6 +79,12 @@ func TestAndroidAppImport_NoDexPreopt(t *testing.T) {
variant.MaybeOutput("dexpreopt/oat/arm64/package.odex").Rule != nil {
t.Errorf("dexpreopt shouldn't have run.")
}
rule := variant.Rule("genProvenanceMetaData")
android.AssertStringEquals(t, "Invalid input", "prebuilts/apk/app.apk", rule.Inputs[0].String())
android.AssertStringEquals(t, "Invalid output", "out/soong/.intermediates/provenance_metadata/foo/provenance_metadata.textproto", rule.Output.String())
android.AssertStringEquals(t, "Invalid args", "foo", rule.Args["module_name"])
android.AssertStringEquals(t, "Invalid args", "/system/app/foo/foo.apk", rule.Args["install_path"])
}
func TestAndroidAppImport_Presigned(t *testing.T) {
@ -102,6 +113,12 @@ func TestAndroidAppImport_Presigned(t *testing.T) {
if variant.MaybeOutput("zip-aligned/foo.apk").Rule == nil {
t.Errorf("can't find aligning rule")
}
rule := variant.Rule("genProvenanceMetaData")
android.AssertStringEquals(t, "Invalid input", "prebuilts/apk/app.apk", rule.Inputs[0].String())
android.AssertStringEquals(t, "Invalid output", "out/soong/.intermediates/provenance_metadata/foo/provenance_metadata.textproto", rule.Output.String())
android.AssertStringEquals(t, "Invalid args", "foo", rule.Args["module_name"])
android.AssertStringEquals(t, "Invalid args", "/system/app/foo/foo.apk", rule.Args["install_path"])
}
func TestAndroidAppImport_SigningLineage(t *testing.T) {
@ -137,6 +154,12 @@ func TestAndroidAppImport_SigningLineage(t *testing.T) {
if expected != signingFlag {
t.Errorf("Incorrect signing flags, expected: %q, got: %q", expected, signingFlag)
}
rule := variant.Rule("genProvenanceMetaData")
android.AssertStringEquals(t, "Invalid input", "prebuilts/apk/app.apk", rule.Inputs[0].String())
android.AssertStringEquals(t, "Invalid output", "out/soong/.intermediates/provenance_metadata/foo/provenance_metadata.textproto", rule.Output.String())
android.AssertStringEquals(t, "Invalid args", "foo", rule.Args["module_name"])
android.AssertStringEquals(t, "Invalid args", "/system/app/foo/foo.apk", rule.Args["install_path"])
}
func TestAndroidAppImport_SigningLineageFilegroup(t *testing.T) {
@ -163,6 +186,12 @@ func TestAndroidAppImport_SigningLineageFilegroup(t *testing.T) {
if expected != signingFlag {
t.Errorf("Incorrect signing flags, expected: %q, got: %q", expected, signingFlag)
}
rule := variant.Rule("genProvenanceMetaData")
android.AssertStringEquals(t, "Invalid input", "prebuilts/apk/app.apk", rule.Inputs[0].String())
android.AssertStringEquals(t, "Invalid output", "out/soong/.intermediates/provenance_metadata/foo/provenance_metadata.textproto", rule.Output.String())
android.AssertStringEquals(t, "Invalid args", "foo", rule.Args["module_name"])
android.AssertStringEquals(t, "Invalid args", "/system/app/foo/foo.apk", rule.Args["install_path"])
}
func TestAndroidAppImport_DefaultDevCert(t *testing.T) {
@ -192,6 +221,12 @@ func TestAndroidAppImport_DefaultDevCert(t *testing.T) {
if expected != signingFlag {
t.Errorf("Incorrect signing flags, expected: %q, got: %q", expected, signingFlag)
}
rule := variant.Rule("genProvenanceMetaData")
android.AssertStringEquals(t, "Invalid input", "prebuilts/apk/app.apk", rule.Inputs[0].String())
android.AssertStringEquals(t, "Invalid output", "out/soong/.intermediates/provenance_metadata/foo/provenance_metadata.textproto", rule.Output.String())
android.AssertStringEquals(t, "Invalid args", "foo", rule.Args["module_name"])
android.AssertStringEquals(t, "Invalid args", "/system/app/foo/foo.apk", rule.Args["install_path"])
}
func TestAndroidAppImport_DpiVariants(t *testing.T) {
@ -214,40 +249,46 @@ func TestAndroidAppImport_DpiVariants(t *testing.T) {
}
`
testCases := []struct {
name string
aaptPreferredConfig *string
aaptPrebuiltDPI []string
expected string
name string
aaptPreferredConfig *string
aaptPrebuiltDPI []string
expected string
expectedProvenanceMetaDataArtifactPath string
}{
{
name: "no preferred",
aaptPreferredConfig: nil,
aaptPrebuiltDPI: []string{},
expected: "verify_uses_libraries/apk/app.apk",
name: "no preferred",
aaptPreferredConfig: nil,
aaptPrebuiltDPI: []string{},
expected: "verify_uses_libraries/apk/app.apk",
expectedProvenanceMetaDataArtifactPath: "prebuilts/apk/app.apk",
},
{
name: "AAPTPreferredConfig matches",
aaptPreferredConfig: proptools.StringPtr("xhdpi"),
aaptPrebuiltDPI: []string{"xxhdpi", "ldpi"},
expected: "verify_uses_libraries/apk/app_xhdpi.apk",
name: "AAPTPreferredConfig matches",
aaptPreferredConfig: proptools.StringPtr("xhdpi"),
aaptPrebuiltDPI: []string{"xxhdpi", "ldpi"},
expected: "verify_uses_libraries/apk/app_xhdpi.apk",
expectedProvenanceMetaDataArtifactPath: "prebuilts/apk/app_xhdpi.apk",
},
{
name: "AAPTPrebuiltDPI matches",
aaptPreferredConfig: proptools.StringPtr("mdpi"),
aaptPrebuiltDPI: []string{"xxhdpi", "xhdpi"},
expected: "verify_uses_libraries/apk/app_xxhdpi.apk",
name: "AAPTPrebuiltDPI matches",
aaptPreferredConfig: proptools.StringPtr("mdpi"),
aaptPrebuiltDPI: []string{"xxhdpi", "xhdpi"},
expected: "verify_uses_libraries/apk/app_xxhdpi.apk",
expectedProvenanceMetaDataArtifactPath: "prebuilts/apk/app_xxhdpi.apk",
},
{
name: "non-first AAPTPrebuiltDPI matches",
aaptPreferredConfig: proptools.StringPtr("mdpi"),
aaptPrebuiltDPI: []string{"ldpi", "xhdpi"},
expected: "verify_uses_libraries/apk/app_xhdpi.apk",
name: "non-first AAPTPrebuiltDPI matches",
aaptPreferredConfig: proptools.StringPtr("mdpi"),
aaptPrebuiltDPI: []string{"ldpi", "xhdpi"},
expected: "verify_uses_libraries/apk/app_xhdpi.apk",
expectedProvenanceMetaDataArtifactPath: "prebuilts/apk/app_xhdpi.apk",
},
{
name: "no matches",
aaptPreferredConfig: proptools.StringPtr("mdpi"),
aaptPrebuiltDPI: []string{"ldpi", "xxxhdpi"},
expected: "verify_uses_libraries/apk/app.apk",
name: "no matches",
aaptPreferredConfig: proptools.StringPtr("mdpi"),
aaptPrebuiltDPI: []string{"ldpi", "xxxhdpi"},
expected: "verify_uses_libraries/apk/app.apk",
expectedProvenanceMetaDataArtifactPath: "prebuilts/apk/app.apk",
},
}
@ -270,6 +311,12 @@ func TestAndroidAppImport_DpiVariants(t *testing.T) {
if strings.HasSuffix(matches[1], test.expected) {
t.Errorf("wrong src apk, expected: %q got: %q", test.expected, matches[1])
}
provenanceMetaDataRule := variant.Rule("genProvenanceMetaData")
android.AssertStringEquals(t, "Invalid input", test.expectedProvenanceMetaDataArtifactPath, provenanceMetaDataRule.Inputs[0].String())
android.AssertStringEquals(t, "Invalid output", "out/soong/.intermediates/provenance_metadata/foo/provenance_metadata.textproto", provenanceMetaDataRule.Output.String())
android.AssertStringEquals(t, "Invalid args", "foo", provenanceMetaDataRule.Args["module_name"])
android.AssertStringEquals(t, "Invalid args", "/system/app/foo/foo.apk", provenanceMetaDataRule.Args["install_path"])
}
}
@ -290,16 +337,25 @@ func TestAndroidAppImport_Filename(t *testing.T) {
`)
testCases := []struct {
name string
expected string
name string
expected string
onDevice string
expectedArtifactPath string
expectedMetaDataPath string
}{
{
name: "foo",
expected: "foo.apk",
name: "foo",
expected: "foo.apk",
onDevice: "/system/app/foo/foo.apk",
expectedArtifactPath: "prebuilts/apk/app.apk",
expectedMetaDataPath: "out/soong/.intermediates/provenance_metadata/foo/provenance_metadata.textproto",
},
{
name: "bar",
expected: "bar_sample.apk",
name: "bar",
expected: "bar_sample.apk",
onDevice: "/system/app/bar/bar_sample.apk",
expectedArtifactPath: "prebuilts/apk/app.apk",
expectedMetaDataPath: "out/soong/.intermediates/provenance_metadata/bar/provenance_metadata.textproto",
},
}
@ -316,15 +372,23 @@ func TestAndroidAppImport_Filename(t *testing.T) {
t.Errorf("Incorrect LOCAL_INSTALLED_MODULE_STEM value '%s', expected '%s'",
actualValues, expectedValues)
}
rule := variant.Rule("genProvenanceMetaData")
android.AssertStringEquals(t, "Invalid input", test.expectedArtifactPath, rule.Inputs[0].String())
android.AssertStringEquals(t, "Invalid output", test.expectedMetaDataPath, rule.Output.String())
android.AssertStringEquals(t, "Invalid args", test.name, rule.Args["module_name"])
android.AssertStringEquals(t, "Invalid args", test.onDevice, rule.Args["install_path"])
}
}
func TestAndroidAppImport_ArchVariants(t *testing.T) {
// The test config's target arch is ARM64.
testCases := []struct {
name string
bp string
expected string
name string
bp string
expected string
artifactPath string
metaDataPath string
installPath string
}{
{
name: "matching arch",
@ -343,7 +407,9 @@ func TestAndroidAppImport_ArchVariants(t *testing.T) {
},
}
`,
expected: "verify_uses_libraries/apk/app_arm64.apk",
expected: "verify_uses_libraries/apk/app_arm64.apk",
artifactPath: "prebuilts/apk/app_arm64.apk",
installPath: "/system/app/foo/foo.apk",
},
{
name: "no matching arch",
@ -362,7 +428,9 @@ func TestAndroidAppImport_ArchVariants(t *testing.T) {
},
}
`,
expected: "verify_uses_libraries/apk/app.apk",
expected: "verify_uses_libraries/apk/app.apk",
artifactPath: "prebuilts/apk/app.apk",
installPath: "/system/app/foo/foo.apk",
},
{
name: "no matching arch without default",
@ -380,7 +448,9 @@ func TestAndroidAppImport_ArchVariants(t *testing.T) {
},
}
`,
expected: "",
expected: "",
artifactPath: "prebuilts/apk/app_arm.apk",
installPath: "/system/app/foo/foo.apk",
},
}
@ -393,6 +463,8 @@ func TestAndroidAppImport_ArchVariants(t *testing.T) {
if variant.Module().Enabled() {
t.Error("module should have been disabled, but wasn't")
}
rule := variant.MaybeRule("genProvenanceMetaData")
android.AssertDeepEquals(t, "Provenance metadata is not empty", android.TestingBuildParams{}, rule)
continue
}
jniRuleCommand := variant.Output("jnis-uncompressed/foo.apk").RuleParams.Command
@ -403,6 +475,11 @@ func TestAndroidAppImport_ArchVariants(t *testing.T) {
if strings.HasSuffix(matches[1], test.expected) {
t.Errorf("wrong src apk, expected: %q got: %q", test.expected, matches[1])
}
rule := variant.Rule("genProvenanceMetaData")
android.AssertStringEquals(t, "Invalid input", test.artifactPath, rule.Inputs[0].String())
android.AssertStringEquals(t, "Invalid output", "out/soong/.intermediates/provenance_metadata/foo/provenance_metadata.textproto", rule.Output.String())
android.AssertStringEquals(t, "Invalid args", "foo", rule.Args["module_name"])
android.AssertStringEquals(t, "Invalid args", test.installPath, rule.Args["install_path"])
}
}

36
provenance/Android.bp Normal file
View file

@ -0,0 +1,36 @@
/*
* Copyright (C) 2022 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 {
default_applicable_licenses: ["Android-Apache-2.0"],
}
bootstrap_go_package {
name: "soong-provenance",
pkgPath: "android/soong/provenance",
srcs: [
"provenance_singleton.go",
],
deps: [
"soong-android",
],
testSrcs: [
"provenance_singleton_test.go",
],
pluginFor: [
"soong_build",
],
}

View file

@ -0,0 +1,34 @@
/*
* Copyright (C) 2022 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 {
default_applicable_licenses: ["Android-Apache-2.0"],
}
python_library_host {
name: "provenance_metadata_proto",
version: {
py3: {
enabled: true,
},
},
srcs: [
"provenance_metadata.proto",
],
proto: {
canonical_path_from_root: false,
},
}

View file

@ -0,0 +1,47 @@
/*
* Copyright (C) 2022 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.
*/
syntax = "proto3";
package provenance_metadata_proto;
option go_package = "android/soong/provenance/provenance_metadata_proto";
// Provenance metadata of artifacts.
message ProvenanceMetadata {
// Name of the module/target that creates the artifact.
// It is either a Soong module name or Bazel target label.
string module_name = 1;
// The path to the prebuilt artifacts, which is relative to the source tree
// directory. For example, prebuilts/runtime/mainline/i18n/apex/com.android.i18n-arm.apex.
string artifact_path = 2;
// The SHA256 hash of the artifact.
string artifact_sha256 = 3;
// The install path of the artifact in filesystem images.
// This is the absolute path of the artifact on the device.
string artifact_install_path = 4;
// Path of the attestation file of a prebuilt artifact, which is relative to
// the source tree directory. This is for prebuilt artifacts which have
// corresponding attestation files checked in the source tree.
string attestation_path = 5;
}
message ProvenanceMetaDataList {
repeated ProvenanceMetadata metadata = 1;
}

View file

@ -0,0 +1,112 @@
/*
* Copyright (C) 2022 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 provenance
import (
"android/soong/android"
"github.com/google/blueprint"
)
var (
pctx = android.NewPackageContext("android/soong/provenance")
rule = pctx.HostBinToolVariable("gen_provenance_metadata", "gen_provenance_metadata")
genProvenanceMetaData = pctx.AndroidStaticRule("genProvenanceMetaData",
blueprint.RuleParams{
Command: `rm -rf "$out" && ` +
`${gen_provenance_metadata} --module_name=${module_name} ` +
`--artifact_path=$in --install_path=${install_path} --metadata_path=$out`,
CommandDeps: []string{"${gen_provenance_metadata}"},
}, "module_name", "install_path")
mergeProvenanceMetaData = pctx.AndroidStaticRule("mergeProvenanceMetaData",
blueprint.RuleParams{
Command: `rm -rf $out $out.temp && ` +
`echo -e "# proto-file: build/soong/provenance/proto/provenance_metadata.proto\n# proto-message: ProvenanceMetaDataList" > $out && ` +
`touch $out.temp && cat $out.temp $in | grep -v "^#.*" >> $out && rm -rf $out.temp`,
})
)
type ProvenanceMetadata interface {
ProvenanceMetaDataFile() android.OutputPath
}
func init() {
RegisterProvenanceSingleton(android.InitRegistrationContext)
}
func RegisterProvenanceSingleton(ctx android.RegistrationContext) {
ctx.RegisterSingletonType("provenance_metadata_singleton", provenanceInfoSingletonFactory)
}
var PrepareForTestWithProvenanceSingleton = android.FixtureRegisterWithContext(RegisterProvenanceSingleton)
func provenanceInfoSingletonFactory() android.Singleton {
return &provenanceInfoSingleton{}
}
type provenanceInfoSingleton struct {
}
func (b *provenanceInfoSingleton) GenerateBuildActions(context android.SingletonContext) {
allMetaDataFiles := make([]android.Path, 0)
context.VisitAllModulesIf(moduleFilter, func(module android.Module) {
if p, ok := module.(ProvenanceMetadata); ok {
allMetaDataFiles = append(allMetaDataFiles, p.ProvenanceMetaDataFile())
}
})
mergedMetaDataFile := android.PathForOutput(context, "provenance_metadata.textproto")
context.Build(pctx, android.BuildParams{
Rule: mergeProvenanceMetaData,
Description: "merge provenance metadata",
Inputs: allMetaDataFiles,
Output: mergedMetaDataFile,
})
context.Build(pctx, android.BuildParams{
Rule: blueprint.Phony,
Description: "phony rule of merge provenance metadata",
Inputs: []android.Path{mergedMetaDataFile},
Output: android.PathForPhony(context, "provenance_metadata"),
})
}
func moduleFilter(module android.Module) bool {
if !module.Enabled() || module.IsSkipInstall() {
return false
}
if p, ok := module.(ProvenanceMetadata); ok {
return p.ProvenanceMetaDataFile().String() != ""
}
return false
}
func GenerateArtifactProvenanceMetaData(ctx android.ModuleContext, artifactPath android.Path, installedFile android.InstallPath) android.OutputPath {
onDevicePathOfInstalledFile := android.InstallPathToOnDevicePath(ctx, installedFile)
artifactMetaDataFile := android.PathForIntermediates(ctx, "provenance_metadata", ctx.ModuleDir(), ctx.ModuleName(), "provenance_metadata.textproto")
ctx.Build(pctx, android.BuildParams{
Rule: genProvenanceMetaData,
Description: "generate artifact provenance metadata",
Inputs: []android.Path{artifactPath},
Output: artifactMetaDataFile,
Args: map[string]string{
"module_name": ctx.ModuleName(),
"install_path": onDevicePathOfInstalledFile,
}})
return artifactMetaDataFile
}

View file

@ -0,0 +1,48 @@
/*
* Copyright (C) 2022 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 provenance
import (
"strings"
"testing"
"android/soong/android"
)
func TestProvenanceSingleton(t *testing.T) {
result := android.GroupFixturePreparers(
PrepareForTestWithProvenanceSingleton,
android.PrepareForTestWithAndroidMk).RunTestWithBp(t, "")
outputs := result.SingletonForTests("provenance_metadata_singleton").AllOutputs()
for _, output := range outputs {
testingBuildParam := result.SingletonForTests("provenance_metadata_singleton").Output(output)
switch {
case strings.Contains(output, "soong/provenance_metadata.textproto"):
android.AssertStringEquals(t, "Invalid build rule", "android/soong/provenance.mergeProvenanceMetaData", testingBuildParam.Rule.String())
android.AssertIntEquals(t, "Invalid input", len(testingBuildParam.Inputs), 0)
android.AssertStringDoesContain(t, "Invalid output path", output, "soong/provenance_metadata.textproto")
android.AssertIntEquals(t, "Invalid args", len(testingBuildParam.Args), 0)
case strings.HasSuffix(output, "provenance_metadata"):
android.AssertStringEquals(t, "Invalid build rule", "<builtin>:phony", testingBuildParam.Rule.String())
android.AssertStringEquals(t, "Invalid input", testingBuildParam.Inputs[0].String(), "out/soong/provenance_metadata.textproto")
android.AssertStringEquals(t, "Invalid output path", output, "provenance_metadata")
android.AssertIntEquals(t, "Invalid args", len(testingBuildParam.Args), 0)
}
}
}

View file

@ -0,0 +1,47 @@
/*
* Copyright (C) 2022 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.
*/
python_binary_host {
name: "gen_provenance_metadata",
srcs: [
"gen_provenance_metadata.py",
],
version: {
py3: {
embedded_launcher: true,
},
},
libs: [
"provenance_metadata_proto",
"libprotobuf-python",
],
}
python_test_host {
name: "gen_provenance_metadata_test",
main: "gen_provenance_metadata_test.py",
srcs: [
"gen_provenance_metadata_test.py",
],
data: [
":gen_provenance_metadata",
],
libs: [
"provenance_metadata_proto",
"libprotobuf-python",
],
test_suites: ["general-tests"],
}

View file

@ -0,0 +1,66 @@
#!/usr/bin/env python3
#
# Copyright (C) 2022 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.
import argparse
import hashlib
import sys
import google.protobuf.text_format as text_format
import provenance_metadata_pb2
def Log(*info):
if args.verbose:
for i in info:
print(i)
def ParseArgs(argv):
parser = argparse.ArgumentParser(description='Create provenance metadata for a prebuilt artifact')
parser.add_argument('-v', '--verbose', action='store_true', help='Print more information in execution')
parser.add_argument('--module_name', help='Module name', required=True)
parser.add_argument('--artifact_path', help='Relative path of the prebuilt artifact in source tree', required=True)
parser.add_argument('--install_path', help='Absolute path of the artifact in the filesystem images', required=True)
parser.add_argument('--metadata_path', help='Path of the provenance metadata file created for the artifact', required=True)
return parser.parse_args(argv)
def main(argv):
global args
args = ParseArgs(argv)
Log("Args:", vars(args))
provenance_metadata = provenance_metadata_pb2.ProvenanceMetadata()
provenance_metadata.module_name = args.module_name
provenance_metadata.artifact_path = args.artifact_path
provenance_metadata.artifact_install_path = args.install_path
Log("Generating SHA256 hash")
h = hashlib.sha256()
with open(args.artifact_path, "rb") as artifact_file:
h.update(artifact_file.read())
provenance_metadata.artifact_sha256 = h.hexdigest()
text_proto = [
"# proto-file: build/soong/provenance/proto/provenance_metadata.proto",
"# proto-message: ProvenanceMetaData",
"",
text_format.MessageToString(provenance_metadata)
]
with open(args.metadata_path, "wt") as metadata_file:
file_content = "\n".join(text_proto)
Log("Writing provenance metadata in textproto:", file_content)
metadata_file.write(file_content)
if __name__ == '__main__':
main(sys.argv[1:])

View file

@ -0,0 +1,125 @@
#!/usr/bin/env python3
#
# Copyright (C) 2022 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.
import hashlib
import logging
import os
import subprocess
import tempfile
import unittest
import google.protobuf.text_format as text_format
import provenance_metadata_pb2
logger = logging.getLogger(__name__)
def run(args, verbose=None, **kwargs):
"""Creates and returns a subprocess.Popen object.
Args:
args: The command represented as a list of strings.
verbose: Whether the commands should be shown. Default to the global
verbosity if unspecified.
kwargs: Any additional args to be passed to subprocess.Popen(), such as env,
stdin, etc. stdout and stderr will default to subprocess.PIPE and
subprocess.STDOUT respectively unless caller specifies any of them.
universal_newlines will default to True, as most of the users in
releasetools expect string output.
Returns:
A subprocess.Popen object.
"""
if 'stdout' not in kwargs and 'stderr' not in kwargs:
kwargs['stdout'] = subprocess.PIPE
kwargs['stderr'] = subprocess.STDOUT
if 'universal_newlines' not in kwargs:
kwargs['universal_newlines'] = True
if verbose:
logger.info(" Running: \"%s\"", " ".join(args))
return subprocess.Popen(args, **kwargs)
def run_and_check_output(args, verbose=None, **kwargs):
"""Runs the given command and returns the output.
Args:
args: The command represented as a list of strings.
verbose: Whether the commands should be shown. Default to the global
verbosity if unspecified.
kwargs: Any additional args to be passed to subprocess.Popen(), such as env,
stdin, etc. stdout and stderr will default to subprocess.PIPE and
subprocess.STDOUT respectively unless caller specifies any of them.
Returns:
The output string.
Raises:
ExternalError: On non-zero exit from the command.
"""
proc = run(args, verbose=verbose, **kwargs)
output, _ = proc.communicate()
if output is None:
output = ""
if verbose:
logger.info("%s", output.rstrip())
if proc.returncode != 0:
raise RuntimeError(
"Failed to run command '{}' (exit code {}):\n{}".format(
args, proc.returncode, output))
return output
def run_host_command(args, verbose=None, **kwargs):
host_build_top = os.environ.get("ANDROID_BUILD_TOP")
if host_build_top:
host_command_dir = os.path.join(host_build_top, "out/host/linux-x86/bin")
args[0] = os.path.join(host_command_dir, args[0])
return run_and_check_output(args, verbose, **kwargs)
def sha256(s):
h = hashlib.sha256()
h.update(bytearray(s, 'utf-8'))
return h.hexdigest()
class ProvenanceMetaDataToolTest(unittest.TestCase):
def test_gen_provenance_metadata(self):
artifact_content = "test artifact"
artifact_file = tempfile.mktemp()
with open(artifact_file,"wt") as f:
f.write(artifact_content)
metadata_file = tempfile.mktemp()
cmd = ["gen_provenance_metadata"]
cmd.extend(["--module_name", "a"])
cmd.extend(["--artifact_path", artifact_file])
cmd.extend(["--install_path", "b"])
cmd.extend(["--metadata_path", metadata_file])
output = run_host_command(cmd)
self.assertEqual(output, "")
with open(metadata_file,"rt") as f:
data = f.read()
provenance_metadata = provenance_metadata_pb2.ProvenanceMetadata()
text_format.Parse(data, provenance_metadata)
self.assertEqual(provenance_metadata.module_name, "a")
self.assertEqual(provenance_metadata.artifact_path, artifact_file)
self.assertEqual(provenance_metadata.artifact_install_path, "b")
self.assertEqual(provenance_metadata.artifact_sha256, sha256(artifact_content))
os.remove(artifact_file)
os.remove(metadata_file)
if __name__ == '__main__':
unittest.main(verbosity=2)