Merge "Simplify equality expressions when comparing to "true""
diff --git a/android/bazel.go b/android/bazel.go
index 63cc6c2..bf214a5 100644
--- a/android/bazel.go
+++ b/android/bazel.go
@@ -332,12 +332,11 @@
// Per-module denylist to opt modules out of mixed builds. Such modules will
// still be generated via bp2build.
mixedBuildsDisabledList = []string{
- "libbrotli", // http://b/198585397, ld.lld: error: bionic/libc/arch-arm64/generic/bionic/memmove.S:95:(.text+0x10): relocation R_AARCH64_CONDBR19 out of range: -1404176 is not in [-1048576, 1048575]; references __memcpy
- "func_to_syscall_nrs", // http://b/200899432, bazel-built cc_genrule does not work in mixed build when it is a dependency of another soong module.
- "libseccomp_policy_app_zygote_sources", // http://b/200899432, bazel-built cc_genrule does not work in mixed build when it is a dependency of another soong module.
- "libseccomp_policy_app_sources", // http://b/200899432, bazel-built cc_genrule does not work in mixed build when it is a dependency of another soong module.
- "libseccomp_policy_system_sources", // http://b/200899432, bazel-built cc_genrule does not work in mixed build when it is a dependency of another soong module.
- "minijail_constants_json", // http://b/200899432, bazel-built cc_genrule does not work in mixed build when it is a dependency of another soong module.
+ "libbrotli", // http://b/198585397, ld.lld: error: bionic/libc/arch-arm64/generic/bionic/memmove.S:95:(.text+0x10): relocation R_AARCH64_CONDBR19 out of range: -1404176 is not in [-1048576, 1048575]; references __memcpy
+ "minijail_constants_json", // http://b/200899432, bazel-built cc_genrule does not work in mixed build when it is a dependency of another soong module.
+
+ "cap_names.h", // TODO(b/204913827) runfiles need to be handled in mixed builds
+ "libcap", // TODO(b/204913827) runfiles need to be handled in mixed builds
}
// Used for quicker lookups
diff --git a/androidmk/androidmk/android.go b/androidmk/androidmk/android.go
index f3ad152..1045ca6 100644
--- a/androidmk/androidmk/android.go
+++ b/androidmk/androidmk/android.go
@@ -58,6 +58,7 @@
"LOCAL_MODULE_STEM": stem,
"LOCAL_MODULE_HOST_OS": hostOs,
"LOCAL_RESOURCE_DIR": localizePathList("resource_dirs"),
+ "LOCAL_NOTICE_FILE": localizePathList("android_license_files"),
"LOCAL_SANITIZE": sanitize(""),
"LOCAL_SANITIZE_DIAG": sanitize("diag."),
"LOCAL_STRIP_MODULE": strip(),
@@ -111,7 +112,6 @@
"LOCAL_PROTOC_OPTIMIZE_TYPE": "proto.type",
"LOCAL_MODULE_OWNER": "owner",
"LOCAL_RENDERSCRIPT_TARGET_API": "renderscript.target_api",
- "LOCAL_NOTICE_FILE": "notice",
"LOCAL_JAVA_LANGUAGE_VERSION": "java_version",
"LOCAL_INSTRUMENTATION_FOR": "instrumentation_for",
"LOCAL_MANIFEST_FILE": "manifest",
@@ -185,6 +185,12 @@
"LOCAL_JACK_COVERAGE_EXCLUDE_FILTER": "jacoco.exclude_filter",
"LOCAL_FULL_LIBS_MANIFEST_FILES": "additional_manifests",
+
+ // will be rewrite later to "license_kinds:" by byfix
+ "LOCAL_LICENSE_KINDS": "android_license_kinds",
+ // will be removed later by byfix
+ // TODO: does this property matter in the license module?
+ "LOCAL_LICENSE_CONDITIONS": "android_license_conditions",
})
addStandardProperties(bpparser.BoolType,
diff --git a/androidmk/androidmk/androidmk_test.go b/androidmk/androidmk/androidmk_test.go
index 775a9a8..ca40aaa 100644
--- a/androidmk/androidmk/androidmk_test.go
+++ b/androidmk/androidmk/androidmk_test.go
@@ -1516,7 +1516,8 @@
],
}
`,
- }, {
+ },
+ {
desc: "Obsolete LOCAL_MODULE_PATH",
in: `
include $(CLEAR_VARS)
@@ -1532,7 +1533,37 @@
name: "foo",
}
-`},
+`,
+ },
+ {
+ desc: "LOCAL_LICENSE_KINDS, LOCAL_LICENSE_CONDITIONS, LOCAL_NOTICE_FILE",
+ // TODO(b/205615944): When valid "android_license_files" exists, the test requires an Android.mk
+ // file (and an Android.bp file is required as well if the license files locates outside the current
+ // directory). So plan to use a mock file system to mock the Android.mk and Android.bp files.
+ in: `
+include $(CLEAR_VARS)
+LOCAL_MODULE := foo
+LOCAL_LICENSE_KINDS := license_kind
+LOCAL_LICENSE_CONDITIONS := license_condition
+LOCAL_NOTICE_FILE := license_notice
+include $(BUILD_PACKAGE)
+`,
+ expected: `
+package {
+ // See: http://go/android-license-faq
+ default_applicable_licenses: [
+ "Android-Apache-2.0",
+ ],
+}
+
+android_app {
+ name: "foo",
+ // ANDROIDMK TRANSLATION ERROR: Only $(LOCAL_PATH)/.. values are allowed
+ // LOCAL_NOTICE_FILE := license_notice
+
+}
+`,
+ },
}
func TestEndToEnd(t *testing.T) {
diff --git a/apex/apex.go b/apex/apex.go
index 8eb261f..378efe6 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -135,16 +135,6 @@
// Default: true.
Installable *bool
- // Whether this is multi-installed APEX should skip installing symbol files.
- // Multi-installed APEXes share the same apex_name and are installed at the same time.
- // Default is false.
- //
- // Should be set to true for all multi-installed APEXes except the singular
- // default version within the multi-installed group.
- // Only the default version can install symbol files in $(PRODUCT_OUT}/apex,
- // or else conflicting build rules may be created.
- Multi_install_skip_symbol_files *bool
-
// Whether this APEX can be compressed or not. Setting this property to false means this
// APEX will never be compressed. When set to true, APEX will be compressed if other
// conditions, e.g, target device needs to support APEX compression, are also fulfilled.
@@ -155,6 +145,16 @@
// Should be only used in non-system apexes (e.g. vendor: true). Default is false.
Use_vndk_as_stable *bool
+ // Whether this is multi-installed APEX should skip installing symbol files.
+ // Multi-installed APEXes share the same apex_name and are installed at the same time.
+ // Default is false.
+ //
+ // Should be set to true for all multi-installed APEXes except the singular
+ // default version within the multi-installed group.
+ // Only the default version can install symbol files in $(PRODUCT_OUT}/apex,
+ // or else conflicting build rules may be created.
+ Multi_install_skip_symbol_files *bool
+
// List of SDKs that are used to build this APEX. A reference to an SDK should be either
// `name#version` or `name` which is an alias for `name#current`. If left empty,
// `platform#current` is implied. This value affects all modules included in this APEX. In
diff --git a/bazel/aquery.go b/bazel/aquery.go
index 6d96b1c..fd8cf67 100644
--- a/bazel/aquery.go
+++ b/bazel/aquery.go
@@ -18,6 +18,7 @@
"encoding/json"
"fmt"
"path/filepath"
+ "regexp"
"strings"
"github.com/google/blueprint/proptools"
@@ -59,6 +60,8 @@
InputDepSetIds []int
Mnemonic string
OutputIds []int
+ TemplateContent string
+ Substitutions []KeyValuePair
}
// actionGraphContainer contains relevant portions of Bazel's aquery proto, ActionGraphContainer.
@@ -100,6 +103,20 @@
artifactIdToPath map[int]string
}
+// The tokens should be substituted with the value specified here, instead of the
+// one returned in 'substitutions' of TemplateExpand action.
+var TemplateActionOverriddenTokens = map[string]string{
+ // Uses "python3" for %python_binary% instead of the value returned by aquery
+ // which is "py3wrapper.sh". See removePy3wrapperScript.
+ "%python_binary%": "python3",
+}
+
+// This pattern matches the MANIFEST file created for a py_binary target.
+var manifestFilePattern = regexp.MustCompile(".*/.+\\.runfiles/MANIFEST$")
+
+// The file name of py3wrapper.sh, which is used by py_binary targets.
+var py3wrapperFileName = "/py3wrapper.sh"
+
func newAqueryHandler(aqueryResult actionGraphContainer) (*aqueryArtifactHandler, error) {
pathFragments := map[int]pathFragment{}
for _, pathFragment := range aqueryResult.PathFragments {
@@ -163,7 +180,31 @@
}
}
}
- return inputPaths, nil
+
+ // TODO(b/197135294): Clean up this custom runfiles handling logic when
+ // SourceSymlinkManifest and SymlinkTree actions are supported.
+ filteredInputPaths := filterOutPy3wrapperAndManifestFileFromInputPaths(inputPaths)
+
+ return filteredInputPaths, nil
+}
+
+// See go/python-binary-host-mixed-build for more details.
+// 1) For py3wrapper.sh, there is no action for creating py3wrapper.sh in the aquery output of
+// Bazel py_binary targets, so there is no Ninja build statements generated for creating it.
+// 2) For MANIFEST file, SourceSymlinkManifest action is in aquery output of Bazel py_binary targets,
+// but it doesn't contain sufficient information so no Ninja build statements are generated
+// for creating it.
+// So in mixed build mode, when these two are used as input of some Ninja build statement,
+// since there is no build statement to create them, they should be removed from input paths.
+func filterOutPy3wrapperAndManifestFileFromInputPaths(inputPaths []string) []string {
+ filteredInputPaths := []string{}
+ for _, path := range inputPaths {
+ if strings.HasSuffix(path, py3wrapperFileName) || manifestFilePattern.MatchString(path) {
+ continue
+ }
+ filteredInputPaths = append(filteredInputPaths, path)
+ }
+ return filteredInputPaths
}
func (a *aqueryArtifactHandler) artifactIdsFromDepsetId(depsetId int) ([]int, error) {
@@ -230,7 +271,7 @@
}
buildStatement := BuildStatement{
- Command: strings.Join(proptools.ShellEscapeList(actionEntry.Arguments), " "),
+ Command: strings.Join(proptools.ShellEscapeListIncludingSpaces(actionEntry.Arguments), " "),
Depfile: depfile,
OutputPaths: outputPaths,
InputPaths: inputPaths,
@@ -249,6 +290,45 @@
// Use absolute paths, because some soong actions don't play well with relative paths (for example, `cp -d`).
buildStatement.Command = fmt.Sprintf("mkdir -p %[1]s && rm -f %[2]s && ln -sf %[3]s %[2]s", outDir, out, in)
buildStatement.SymlinkPaths = outputPaths[:]
+ } else if isTemplateExpandAction(actionEntry) && len(actionEntry.Arguments) < 1 {
+ if len(outputPaths) != 1 {
+ return nil, fmt.Errorf("Expect 1 output to template expand action, got: output %q", outputPaths)
+ }
+ expandedTemplateContent := expandTemplateContent(actionEntry)
+ // The expandedTemplateContent is escaped for being used in double quotes and shell unescape,
+ // and the new line characters (\n) are also changed to \\n which avoids some Ninja escape on \n, which might
+ // change \n to space and mess up the format of Python programs.
+ // sed is used to convert \\n back to \n before saving to output file.
+ // See go/python-binary-host-mixed-build for more details.
+ command := fmt.Sprintf(`/bin/bash -c 'echo "%[1]s" | sed "s/\\\\n/\\n/g" > %[2]s && chmod a+x %[2]s'`,
+ escapeCommandlineArgument(expandedTemplateContent), outputPaths[0])
+ buildStatement.Command = command
+ } else if isPythonZipperAction(actionEntry) {
+ if len(inputPaths) < 1 || len(outputPaths) != 1 {
+ return nil, fmt.Errorf("Expect 1+ input and 1 output to python zipper action, got: input %q, output %q", inputPaths, outputPaths)
+ }
+ buildStatement.InputPaths, buildStatement.Command = removePy3wrapperScript(buildStatement)
+ buildStatement.Command = addCommandForPyBinaryRunfilesDir(buildStatement, inputPaths[0], outputPaths[0])
+ // Add the python zip file as input of the corresponding python binary stub script in Ninja build statements.
+ // In Ninja build statements, the outputs of dependents of a python binary have python binary stub script as input,
+ // which is not sufficient without the python zip file from which runfiles directory is created for py_binary.
+ //
+ // The following logic relies on that Bazel aquery output returns actions in the order that
+ // PythonZipper is after TemplateAction of creating Python binary stub script. If later Bazel doesn't return actions
+ // in that order, the following logic might not find the build statement generated for Python binary
+ // stub script and the build might fail. So the check of pyBinaryFound is added to help debug in case later Bazel might change aquery output.
+ // See go/python-binary-host-mixed-build for more details.
+ pythonZipFilePath := outputPaths[0]
+ pyBinaryFound := false
+ for i, _ := range buildStatements {
+ if len(buildStatements[i].OutputPaths) == 1 && buildStatements[i].OutputPaths[0]+".zip" == pythonZipFilePath {
+ buildStatements[i].InputPaths = append(buildStatements[i].InputPaths, pythonZipFilePath)
+ pyBinaryFound = true
+ }
+ }
+ if !pyBinaryFound {
+ return nil, fmt.Errorf("Could not find the correspondinging Python binary stub script of PythonZipper: %q", outputPaths)
+ }
} else if len(actionEntry.Arguments) < 1 {
return nil, fmt.Errorf("received action with no command: [%v]", buildStatement)
}
@@ -258,10 +338,85 @@
return buildStatements, nil
}
+// expandTemplateContent substitutes the tokens in a template.
+func expandTemplateContent(actionEntry action) string {
+ replacerString := []string{}
+ for _, pair := range actionEntry.Substitutions {
+ value := pair.Value
+ if val, ok := TemplateActionOverriddenTokens[pair.Key]; ok {
+ value = val
+ }
+ replacerString = append(replacerString, pair.Key, value)
+ }
+ replacer := strings.NewReplacer(replacerString...)
+ return replacer.Replace(actionEntry.TemplateContent)
+}
+
+func escapeCommandlineArgument(str string) string {
+ // \->\\, $->\$, `->\`, "->\", \n->\\n, '->'"'"'
+ replacer := strings.NewReplacer(
+ `\`, `\\`,
+ `$`, `\$`,
+ "`", "\\`",
+ `"`, `\"`,
+ "\n", "\\n",
+ `'`, `'"'"'`,
+ )
+ return replacer.Replace(str)
+}
+
+// removePy3wrapperScript removes py3wrapper.sh from the input paths and command of the action of
+// creating python zip file in mixed build mode. py3wrapper.sh is returned as input by aquery but
+// there is no action returned by aquery for creating it. So in mixed build "python3" is used
+// as the PYTHON_BINARY in python binary stub script, and py3wrapper.sh is not needed and should be
+// removed from input paths and command of creating python zip file.
+// See go/python-binary-host-mixed-build for more details.
+// TODO(b/205879240) remove this after py3wrapper.sh could be created in the mixed build mode.
+func removePy3wrapperScript(bs BuildStatement) (newInputPaths []string, newCommand string) {
+ // Remove from inputs
+ filteredInputPaths := []string{}
+ for _, path := range bs.InputPaths {
+ if !strings.HasSuffix(path, py3wrapperFileName) {
+ filteredInputPaths = append(filteredInputPaths, path)
+ }
+ }
+ newInputPaths = filteredInputPaths
+
+ // Remove from command line
+ var re = regexp.MustCompile(`\S*` + py3wrapperFileName)
+ newCommand = re.ReplaceAllString(bs.Command, "")
+ return
+}
+
+// addCommandForPyBinaryRunfilesDir adds commands creating python binary runfiles directory.
+// runfiles directory is created by using MANIFEST file and MANIFEST file is the output of
+// SourceSymlinkManifest action is in aquery output of Bazel py_binary targets,
+// but since SourceSymlinkManifest doesn't contain sufficient information
+// so MANIFEST file could not be created, which also blocks the creation of runfiles directory.
+// See go/python-binary-host-mixed-build for more details.
+// TODO(b/197135294) create runfiles directory from MANIFEST file once it can be created from SourceSymlinkManifest action.
+func addCommandForPyBinaryRunfilesDir(bs BuildStatement, zipperCommandPath, zipFilePath string) string {
+ // Unzip the zip file, zipFilePath looks like <python_binary>.zip
+ runfilesDirName := zipFilePath[0:len(zipFilePath)-4] + ".runfiles"
+ command := fmt.Sprintf("%s x %s -d %s", zipperCommandPath, zipFilePath, runfilesDirName)
+ // Create a symbolic link in <python_binary>.runfiles/, which is the expected structure
+ // when running the python binary stub script.
+ command += fmt.Sprintf(" && ln -sf runfiles/__main__ %s", runfilesDirName)
+ return bs.Command + " && " + command
+}
+
func isSymlinkAction(a action) bool {
return a.Mnemonic == "Symlink" || a.Mnemonic == "SolibSymlink"
}
+func isTemplateExpandAction(a action) bool {
+ return a.Mnemonic == "TemplateExpand"
+}
+
+func isPythonZipperAction(a action) bool {
+ return a.Mnemonic == "PythonZipper"
+}
+
func shouldSkipAction(a action) bool {
// TODO(b/180945121): Handle complex symlink actions.
if a.Mnemonic == "SymlinkTree" || a.Mnemonic == "SourceSymlinkManifest" {
diff --git a/bazel/aquery_test.go b/bazel/aquery_test.go
index 69f1115..68e50c2 100644
--- a/bazel/aquery_test.go
+++ b/bazel/aquery_test.go
@@ -709,7 +709,7 @@
}
expectedBuildStatements := []BuildStatement{
BuildStatement{
- Command: "/bin/bash -c touch bazel-out/sourceroot/k8-fastbuild/bin/testpkg/test_out",
+ Command: "/bin/bash -c 'touch bazel-out/sourceroot/k8-fastbuild/bin/testpkg/test_out'",
OutputPaths: []string{"bazel-out/sourceroot/k8-fastbuild/bin/testpkg/test_out"},
InputPaths: inputPaths,
Mnemonic: "Action",
@@ -1015,6 +1015,355 @@
assertError(t, err, `Expect 1 input and 1 output to symlink action, got: input ["file"], output ["symlink" "other_symlink"]`)
}
+func TestTemplateExpandActionSubstitutions(t *testing.T) {
+ const inputString = `
+{
+ "artifacts": [{
+ "id": 1,
+ "pathFragmentId": 1
+ }],
+ "actions": [{
+ "targetId": 1,
+ "actionKey": "x",
+ "mnemonic": "TemplateExpand",
+ "configurationId": 1,
+ "outputIds": [1],
+ "primaryOutputId": 1,
+ "executionPlatform": "//build/bazel/platforms:linux_x86_64",
+ "templateContent": "Test template substitutions: %token1%, %python_binary%",
+ "substitutions": [{
+ "key": "%token1%",
+ "value": "abcd"
+ },{
+ "key": "%python_binary%",
+ "value": "python3"
+ }]
+ }],
+ "pathFragments": [{
+ "id": 1,
+ "label": "template_file"
+ }]
+}`
+
+ actual, err := AqueryBuildStatements([]byte(inputString))
+
+ if err != nil {
+ t.Errorf("Unexpected error %q", err)
+ }
+
+ expectedBuildStatements := []BuildStatement{
+ BuildStatement{
+ Command: "/bin/bash -c 'echo \"Test template substitutions: abcd, python3\" | sed \"s/\\\\\\\\n/\\\\n/g\" > template_file && " +
+ "chmod a+x template_file'",
+ OutputPaths: []string{"template_file"},
+ Mnemonic: "TemplateExpand",
+ },
+ }
+ assertBuildStatements(t, expectedBuildStatements, actual)
+}
+
+func TestTemplateExpandActionNoOutput(t *testing.T) {
+ const inputString = `
+{
+ "artifacts": [{
+ "id": 1,
+ "pathFragmentId": 1
+ }],
+ "actions": [{
+ "targetId": 1,
+ "actionKey": "x",
+ "mnemonic": "TemplateExpand",
+ "configurationId": 1,
+ "primaryOutputId": 1,
+ "executionPlatform": "//build/bazel/platforms:linux_x86_64",
+ "templateContent": "Test template substitutions: %token1%, %python_binary%",
+ "substitutions": [{
+ "key": "%token1%",
+ "value": "abcd"
+ },{
+ "key": "%python_binary%",
+ "value": "python3"
+ }]
+ }],
+ "pathFragments": [{
+ "id": 1,
+ "label": "template_file"
+ }]
+}`
+
+ _, err := AqueryBuildStatements([]byte(inputString))
+ assertError(t, err, `Expect 1 output to template expand action, got: output []`)
+}
+
+func TestPythonZipperActionSuccess(t *testing.T) {
+ const inputString = `
+{
+ "artifacts": [{
+ "id": 1,
+ "pathFragmentId": 1
+ },{
+ "id": 2,
+ "pathFragmentId": 2
+ },{
+ "id": 3,
+ "pathFragmentId": 3
+ },{
+ "id": 4,
+ "pathFragmentId": 4
+ },{
+ "id": 5,
+ "pathFragmentId": 10
+ },{
+ "id": 10,
+ "pathFragmentId": 20
+ }],
+ "actions": [{
+ "targetId": 1,
+ "actionKey": "x",
+ "mnemonic": "TemplateExpand",
+ "configurationId": 1,
+ "outputIds": [1],
+ "primaryOutputId": 1,
+ "executionPlatform": "//build/bazel/platforms:linux_x86_64",
+ "templateContent": "Test template substitutions: %token1%, %python_binary%",
+ "substitutions": [{
+ "key": "%token1%",
+ "value": "abcd"
+ },{
+ "key": "%python_binary%",
+ "value": "python3"
+ }]
+ },{
+ "targetId": 1,
+ "actionKey": "x",
+ "mnemonic": "PythonZipper",
+ "configurationId": 1,
+ "arguments": ["../bazel_tools/tools/zip/zipper/zipper", "cC", "python_binary.zip", "__main__.py\u003dbazel-out/k8-fastbuild/bin/python_binary.temp", "__init__.py\u003d", "runfiles/__main__/__init__.py\u003d", "runfiles/__main__/python_binary.py\u003dpython_binary.py", "runfiles/bazel_tools/tools/python/py3wrapper.sh\u003dbazel-out/bazel_tools/k8-fastbuild/bin/tools/python/py3wrapper.sh"],
+ "outputIds": [2],
+ "inputDepSetIds": [1],
+ "primaryOutputId": 2
+ }],
+ "depSetOfFiles": [{
+ "id": 1,
+ "directArtifactIds": [4, 3, 5]
+ }],
+ "pathFragments": [{
+ "id": 1,
+ "label": "python_binary"
+ },{
+ "id": 2,
+ "label": "python_binary.zip"
+ },{
+ "id": 3,
+ "label": "python_binary.py"
+ },{
+ "id": 9,
+ "label": ".."
+ }, {
+ "id": 8,
+ "label": "bazel_tools",
+ "parentId": 9
+ }, {
+ "id": 7,
+ "label": "tools",
+ "parentId": 8
+ }, {
+ "id": 6,
+ "label": "zip",
+ "parentId": 7
+ }, {
+ "id": 5,
+ "label": "zipper",
+ "parentId": 6
+ }, {
+ "id": 4,
+ "label": "zipper",
+ "parentId": 5
+ },{
+ "id": 16,
+ "label": "bazel-out"
+ },{
+ "id": 15,
+ "label": "bazel_tools",
+ "parentId": 16
+ }, {
+ "id": 14,
+ "label": "k8-fastbuild",
+ "parentId": 15
+ }, {
+ "id": 13,
+ "label": "bin",
+ "parentId": 14
+ }, {
+ "id": 12,
+ "label": "tools",
+ "parentId": 13
+ }, {
+ "id": 11,
+ "label": "python",
+ "parentId": 12
+ }, {
+ "id": 10,
+ "label": "py3wrapper.sh",
+ "parentId": 11
+ },{
+ "id": 20,
+ "label": "python_binary"
+ }]
+}`
+ actual, err := AqueryBuildStatements([]byte(inputString))
+
+ if err != nil {
+ t.Errorf("Unexpected error %q", err)
+ }
+
+ expectedBuildStatements := []BuildStatement{
+ BuildStatement{
+ Command: "/bin/bash -c 'echo \"Test template substitutions: abcd, python3\" | sed \"s/\\\\\\\\n/\\\\n/g\" > python_binary && " +
+ "chmod a+x python_binary'",
+ InputPaths: []string{"python_binary.zip"},
+ OutputPaths: []string{"python_binary"},
+ Mnemonic: "TemplateExpand",
+ },
+ BuildStatement{
+ Command: "../bazel_tools/tools/zip/zipper/zipper cC python_binary.zip __main__.py=bazel-out/k8-fastbuild/bin/python_binary.temp " +
+ "__init__.py= runfiles/__main__/__init__.py= runfiles/__main__/python_binary.py=python_binary.py && " +
+ "../bazel_tools/tools/zip/zipper/zipper x python_binary.zip -d python_binary.runfiles && ln -sf runfiles/__main__ python_binary.runfiles",
+ InputPaths: []string{"../bazel_tools/tools/zip/zipper/zipper", "python_binary.py"},
+ OutputPaths: []string{"python_binary.zip"},
+ Mnemonic: "PythonZipper",
+ },
+ }
+ assertBuildStatements(t, expectedBuildStatements, actual)
+}
+
+func TestPythonZipperActionNoInput(t *testing.T) {
+ const inputString = `
+{
+ "artifacts": [{
+ "id": 1,
+ "pathFragmentId": 1
+ },{
+ "id": 2,
+ "pathFragmentId": 2
+ }],
+ "actions": [{
+ "targetId": 1,
+ "actionKey": "x",
+ "mnemonic": "PythonZipper",
+ "configurationId": 1,
+ "arguments": ["../bazel_tools/tools/zip/zipper/zipper", "cC", "python_binary.zip", "__main__.py\u003dbazel-out/k8-fastbuild/bin/python_binary.temp", "__init__.py\u003d", "runfiles/__main__/__init__.py\u003d", "runfiles/__main__/python_binary.py\u003dpython_binary.py", "runfiles/bazel_tools/tools/python/py3wrapper.sh\u003dbazel-out/bazel_tools/k8-fastbuild/bin/tools/python/py3wrapper.sh"],
+ "outputIds": [2],
+ "primaryOutputId": 2
+ }],
+ "pathFragments": [{
+ "id": 1,
+ "label": "python_binary"
+ },{
+ "id": 2,
+ "label": "python_binary.zip"
+ }]
+}`
+ _, err := AqueryBuildStatements([]byte(inputString))
+ assertError(t, err, `Expect 1+ input and 1 output to python zipper action, got: input [], output ["python_binary.zip"]`)
+}
+
+func TestPythonZipperActionNoOutput(t *testing.T) {
+ const inputString = `
+{
+ "artifacts": [{
+ "id": 1,
+ "pathFragmentId": 1
+ },{
+ "id": 2,
+ "pathFragmentId": 2
+ },{
+ "id": 3,
+ "pathFragmentId": 3
+ },{
+ "id": 4,
+ "pathFragmentId": 4
+ },{
+ "id": 5,
+ "pathFragmentId": 10
+ }],
+ "actions": [{
+ "targetId": 1,
+ "actionKey": "x",
+ "mnemonic": "PythonZipper",
+ "configurationId": 1,
+ "arguments": ["../bazel_tools/tools/zip/zipper/zipper", "cC", "python_binary.zip", "__main__.py\u003dbazel-out/k8-fastbuild/bin/python_binary.temp", "__init__.py\u003d", "runfiles/__main__/__init__.py\u003d", "runfiles/__main__/python_binary.py\u003dpython_binary.py", "runfiles/bazel_tools/tools/python/py3wrapper.sh\u003dbazel-out/bazel_tools/k8-fastbuild/bin/tools/python/py3wrapper.sh"],
+ "inputDepSetIds": [1]
+ }],
+ "depSetOfFiles": [{
+ "id": 1,
+ "directArtifactIds": [4, 3, 5]
+ }],
+ "pathFragments": [{
+ "id": 1,
+ "label": "python_binary"
+ },{
+ "id": 2,
+ "label": "python_binary.zip"
+ },{
+ "id": 3,
+ "label": "python_binary.py"
+ },{
+ "id": 9,
+ "label": ".."
+ }, {
+ "id": 8,
+ "label": "bazel_tools",
+ "parentId": 9
+ }, {
+ "id": 7,
+ "label": "tools",
+ "parentId": 8
+ }, {
+ "id": 6,
+ "label": "zip",
+ "parentId": 7
+ }, {
+ "id": 5,
+ "label": "zipper",
+ "parentId": 6
+ }, {
+ "id": 4,
+ "label": "zipper",
+ "parentId": 5
+ },{
+ "id": 16,
+ "label": "bazel-out"
+ },{
+ "id": 15,
+ "label": "bazel_tools",
+ "parentId": 16
+ }, {
+ "id": 14,
+ "label": "k8-fastbuild",
+ "parentId": 15
+ }, {
+ "id": 13,
+ "label": "bin",
+ "parentId": 14
+ }, {
+ "id": 12,
+ "label": "tools",
+ "parentId": 13
+ }, {
+ "id": 11,
+ "label": "python",
+ "parentId": 12
+ }, {
+ "id": 10,
+ "label": "py3wrapper.sh",
+ "parentId": 11
+ }]
+}`
+ _, err := AqueryBuildStatements([]byte(inputString))
+ assertError(t, err, `Expect 1+ input and 1 output to python zipper action, got: input ["../bazel_tools/tools/zip/zipper/zipper" "python_binary.py"], output []`)
+}
+
func assertError(t *testing.T, err error, expected string) {
t.Helper()
if err == nil {
@@ -1029,7 +1378,7 @@
func assertBuildStatements(t *testing.T, expected []BuildStatement, actual []BuildStatement) {
t.Helper()
if len(expected) != len(actual) {
- t.Errorf("expected %d build statements, but got %d,\n expected: %v,\n actual: %v",
+ t.Errorf("expected %d build statements, but got %d,\n expected: %#v,\n actual: %#v",
len(expected), len(actual), expected, actual)
return
}
@@ -1040,7 +1389,7 @@
continue ACTUAL_LOOP
}
}
- t.Errorf("unexpected build statement %v.\n expected: %v",
+ t.Errorf("unexpected build statement %#v.\n expected: %#v",
actualStatement, expected)
return
}
diff --git a/bpfix/bpfix/bpfix.go b/bpfix/bpfix/bpfix.go
index a608630..e1140b8 100644
--- a/bpfix/bpfix/bpfix.go
+++ b/bpfix/bpfix/bpfix.go
@@ -19,9 +19,13 @@
import (
"bytes"
"errors"
+ "flag"
"fmt"
"io"
+ "os"
"path/filepath"
+ "reflect"
+ "sort"
"strings"
"github.com/google/blueprint/parser"
@@ -137,11 +141,35 @@
Fix: runPatchListMod(removeObsoleteProperty("sanitize.scudo")),
},
{
+ Name: "removeAndroidLicenseKinds",
+ Fix: runPatchListMod(removeIncorrectProperties("android_license_kinds")),
+ },
+ {
+ Name: "removeAndroidLicenseConditions",
+ Fix: runPatchListMod(removeIncorrectProperties("android_license_conditions")),
+ },
+ {
+ Name: "removeAndroidLicenseFiles",
+ Fix: runPatchListMod(removeIncorrectProperties("android_license_files")),
+ },
+ {
Name: "formatFlagProperties",
Fix: runPatchListMod(formatFlagProperties),
},
}
+// for fix that only need to run once
+var fixStepsOnce = []FixStep{
+ {
+ Name: "haveSameLicense",
+ Fix: haveSameLicense,
+ },
+ {
+ Name: "rewriteLicenseProperties",
+ Fix: runPatchListMod(rewriteLicenseProperties),
+ },
+}
+
func NewFixRequest() FixRequest {
return FixRequest{}
}
@@ -196,6 +224,16 @@
return nil, err
}
+ // run fix that is expected to run once first
+ configOnce := NewFixRequest()
+ configOnce.steps = append(configOnce.steps, fixStepsOnce...)
+ if len(configOnce.steps) > 0 {
+ err = f.fixTreeOnce(configOnce)
+ if err != nil {
+ return nil, err
+ }
+ }
+
maxNumIterations := 20
i := 0
for {
@@ -1413,3 +1451,304 @@
}
return nil
}
+
+// rewrite the "android_license_kinds" and "android_license_files" properties to a package module
+// (and a license module when needed).
+func rewriteLicenseProperties(mod *parser.Module, buf []byte, patchList *parser.PatchList) error {
+ // if a package module has been added, no more action is needed.
+ for _, patch := range *patchList {
+ if strings.Contains(patch.Replacement, "package {") {
+ return nil
+ }
+ }
+
+ licenseKindsPropertyName := "android_license_kinds"
+ licenseFilesPropertyName := "android_license_files"
+
+ androidBpFileErr := "// Error: No Android.bp file is found at\n" +
+ "// %s\n" +
+ "// Please add one there with the needed license module first.\n"
+ licenseModuleErr := "// Error: Cannot get the name of the license module in the\n" +
+ "// %s file.\n" +
+ "// If no such license module exists, please add one there first.\n"
+
+ defaultApplicableLicense := "Android-Apache-2.0"
+ var licenseModuleName, licensePatch string
+ var hasFileInParentDir bool
+
+ // when LOCAL_NOTICE_FILE is not empty
+ if hasNonEmptyLiteralListProperty(mod, licenseFilesPropertyName) {
+ hasFileInParentDir = hasValueStartWithTwoDotsLiteralList(mod, licenseFilesPropertyName)
+ // if have LOCAL_NOTICE_FILE outside the current directory, need to find and refer to the license
+ // module in the LOCAL_NOTICE_FILE location directly and no new license module needs to be created
+ if hasFileInParentDir {
+ bpPath, ok := getPathFromProperty(mod, licenseFilesPropertyName)
+ if !ok {
+ bpDir, err := getDirFromProperty(mod, licenseFilesPropertyName)
+ if err != nil {
+ return err
+ }
+ licensePatch += fmt.Sprintf(androidBpFileErr, bpDir)
+ } else {
+ licenseModuleName, _ = getModuleName(bpPath, "license")
+ if len(licenseModuleName) == 0 {
+ licensePatch += fmt.Sprintf(licenseModuleErr, bpPath)
+ }
+ defaultApplicableLicense = licenseModuleName
+ }
+ } else {
+ // if have LOCAL_NOTICE_FILE in the current directory, need to create a new license module
+ relativePath := getModuleRelativePath()
+ if len(relativePath) == 0 {
+ return fmt.Errorf("Cannot obtain the relative path of the Android.mk file")
+ }
+ licenseModuleName = strings.Replace(relativePath, "/", "_", -1) + "_license"
+ defaultApplicableLicense = licenseModuleName
+ }
+ }
+
+ //add the package module
+ if hasNonEmptyLiteralListProperty(mod, licenseKindsPropertyName) {
+ licensePatch += "package {\n" +
+ " // See: http://go/android-license-faq\n" +
+ " default_applicable_licenses: [\n" +
+ " \"" + defaultApplicableLicense + "\",\n" +
+ " ],\n" +
+ "}\n" +
+ "\n"
+ }
+
+ // append the license module when necessary
+ // when LOCAL_NOTICE_FILE is not empty and in the current directory, create a new license module
+ // otherwise, use the above default license directly
+ if hasNonEmptyLiteralListProperty(mod, licenseFilesPropertyName) && !hasFileInParentDir {
+ licenseKinds, err := mergeLiteralListPropertyValue(mod, licenseKindsPropertyName)
+ if err != nil {
+ return err
+ }
+ licenseFiles, err := mergeLiteralListPropertyValue(mod, licenseFilesPropertyName)
+ if err != nil {
+ return err
+ }
+ licensePatch += "license {\n" +
+ " name: \"" + licenseModuleName + "\",\n" +
+ " visibility: [\":__subpackages__\"],\n" +
+ " license_kinds: [\n" +
+ licenseKinds +
+ " ],\n" +
+ " license_text: [\n" +
+ licenseFiles +
+ " ],\n" +
+ "}\n" +
+ "\n"
+ }
+
+ // add to the patchList
+ pos := mod.Pos().Offset
+ err := patchList.Add(pos, pos, licensePatch)
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+// merge the string vaules in a list property of a module into one string with expected format
+func mergeLiteralListPropertyValue(mod *parser.Module, property string) (s string, err error) {
+ listValue, ok := getLiteralListPropertyValue(mod, property)
+ if !ok {
+ // if do not find
+ return "", fmt.Errorf("Cannot retrieve the %s.%s field", mod.Type, property)
+ }
+ for i := 0; i < len(listValue); i++ {
+ s += " \"" + listValue[i] + "\",\n"
+ }
+ return s, nil
+}
+
+// check whether a string list property has any value starting with `../`
+func hasValueStartWithTwoDotsLiteralList(mod *parser.Module, property string) bool {
+ listValue, ok := getLiteralListPropertyValue(mod, property)
+ if ok {
+ for i := 0; i < len(listValue); i++ {
+ if strings.HasPrefix(listValue[i], "../") {
+ return true
+ }
+ }
+ }
+ return false
+}
+
+// get the relative path from ANDROID_BUILD_TOP to the Android.mk file to be converted
+func getModuleRelativePath() string {
+ // get the absolute path of the top of the tree
+ rootPath := os.Getenv("ANDROID_BUILD_TOP")
+ // get the absolute path of the `Android.mk` file to be converted
+ absPath := getModuleAbsolutePath()
+ // get the relative path of the `Android.mk` file to top of the tree
+ relModulePath, err := filepath.Rel(rootPath, absPath)
+ if err != nil {
+ return ""
+ }
+ return relModulePath
+}
+
+// get the absolute path of the Android.mk file to be converted
+func getModuleAbsolutePath() string {
+ // get the absolute path at where the `androidmk` commend is executed
+ curAbsPath, err := filepath.Abs(".")
+ if err != nil {
+ return ""
+ }
+ // the argument for the `androidmk` command could be
+ // 1. "./a/b/c/Android.mk"; 2. "a/b/c/Android.mk"; 3. "Android.mk"
+ argPath := flag.Arg(0)
+ if strings.HasPrefix(argPath, "./") {
+ argPath = strings.TrimPrefix(argPath, ".")
+ }
+ argPath = strings.TrimSuffix(argPath, "Android.mk")
+ if strings.HasSuffix(argPath, "/") {
+ argPath = strings.TrimSuffix(argPath, "/")
+ }
+ if len(argPath) > 0 && !strings.HasPrefix(argPath, "/") {
+ argPath = "/" + argPath
+ }
+ // get the absolute path of the `Android.mk` file to be converted
+ absPath := curAbsPath + argPath
+ return absPath
+}
+
+// check whether a file exists in a directory
+func hasFile(dir string, fileName string) error {
+ _, err := os.Stat(dir + fileName)
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+// get the directory where an `Android.bp` file and the property files are expected to locate
+func getDirFromProperty(mod *parser.Module, property string) (string, error) {
+ listValue, ok := getLiteralListPropertyValue(mod, property)
+ if !ok {
+ // if do not find
+ return "", fmt.Errorf("Cannot retrieve the %s.%s property", mod.Type, property)
+ }
+ if len(listValue) == 0 {
+ // if empty
+ return "", fmt.Errorf("Cannot find the value of the %s.%s property", mod.Type, property)
+ }
+ path := getModuleAbsolutePath()
+ for {
+ if !strings.HasPrefix(listValue[0], "../") {
+ break
+ }
+ path = filepath.Dir(path)
+ listValue[0] = strings.TrimPrefix(listValue[0], "../")
+ }
+ return path, nil
+}
+
+// get the path of the `Android.bp` file at the expected location where the property files locate
+func getPathFromProperty(mod *parser.Module, property string) (string, bool) {
+ dir, err := getDirFromProperty(mod, property)
+ if err != nil {
+ return "", false
+ }
+ err = hasFile(dir, "/Android.bp")
+ if err != nil {
+ return "", false
+ }
+ return dir + "/Android.bp", true
+}
+
+// parse an Android.bp file to get the name of the first module with type of moduleType
+func getModuleName(path string, moduleType string) (string, error) {
+ tree, err := parserPath(path)
+ if err != nil {
+ return "", err
+ }
+ for _, def := range tree.Defs {
+ mod, ok := def.(*parser.Module)
+ if !ok || mod.Type != moduleType {
+ continue
+ }
+ prop, ok := mod.GetProperty("name")
+ if !ok {
+ return "", fmt.Errorf("Cannot get the %s."+"name property", mod.Type)
+ }
+ propVal, ok := prop.Value.(*parser.String)
+ if ok {
+ return propVal.Value, nil
+ }
+ }
+ return "", fmt.Errorf("Cannot find the value of the %s."+"name property", moduleType)
+}
+
+// parse an Android.bp file with the specific path
+func parserPath(path string) (tree *parser.File, err error) {
+ fileContent, _ := os.ReadFile(path)
+ tree, err = parse(path, bytes.NewBufferString(string(fileContent)))
+ if err != nil {
+ return tree, err
+ }
+ return tree, nil
+}
+
+// remove the incorrect property that Soong does not support
+func removeIncorrectProperties(propName string) patchListModFunction {
+ return removeObsoleteProperty(propName)
+}
+
+// the modules on the same Android.mk file are expected to have the same license
+func haveSameLicense(f *Fixer) error {
+ androidLicenseProperties := []string{
+ "android_license_kinds",
+ "android_license_conditions",
+ "android_license_files",
+ }
+
+ var prevModuleName string
+ var prevLicenseKindsVals, prevLicenseConditionsVals, prevLicenseFilesVals []string
+ prevLicenseVals := [][]string{
+ prevLicenseKindsVals,
+ prevLicenseConditionsVals,
+ prevLicenseFilesVals,
+ }
+
+ for _, def := range f.tree.Defs {
+ mod, ok := def.(*parser.Module)
+ if !ok {
+ continue
+ }
+ for idx, property := range androidLicenseProperties {
+ curModuleName, ok := getLiteralStringPropertyValue(mod, "name")
+ // some modules in the existing test cases in the androidmk_test.go do not have name property
+ hasNameProperty := hasProperty(mod, "name")
+ if hasNameProperty && (!ok || len(curModuleName) == 0) {
+ return fmt.Errorf("Cannot retrieve the name property of a module of %s type.", mod.Type)
+ }
+ curVals, ok := getLiteralListPropertyValue(mod, property)
+ // some modules in the existing test cases in the androidmk_test.go do not have license-related property
+ hasLicenseProperty := hasProperty(mod, property)
+ if hasLicenseProperty && (!ok || len(curVals) == 0) {
+ // if do not find the property, or no value is found for the property
+ return fmt.Errorf("Cannot retrieve the %s.%s property", mod.Type, property)
+ }
+ if len(prevLicenseVals[idx]) > 0 {
+ if !reflect.DeepEqual(prevLicenseVals[idx], curVals) {
+ return fmt.Errorf("Modules %s and %s are expected to have the same %s property.",
+ prevModuleName, curModuleName, property)
+ }
+ }
+ sort.Strings(curVals)
+ prevLicenseVals[idx] = curVals
+ prevModuleName = curModuleName
+ }
+ }
+ return nil
+}
+
+func hasProperty(mod *parser.Module, propName string) bool {
+ _, ok := mod.GetProperty(propName)
+ return ok
+}
diff --git a/bpfix/bpfix/bpfix_test.go b/bpfix/bpfix/bpfix_test.go
index d8772c1..e6b6af5 100644
--- a/bpfix/bpfix/bpfix_test.go
+++ b/bpfix/bpfix/bpfix_test.go
@@ -125,34 +125,103 @@
implFilterListTest(t, []string{}, []string{}, []string{})
}
-func runPass(t *testing.T, in, out string, innerTest func(*Fixer) error) {
- expected, err := Reformat(out)
+func checkError(t *testing.T, in, expectedErr string, innerTest func(*Fixer) error) {
+ expected := preProcessOutErr(expectedErr)
+ runTestOnce(t, in, expected, innerTest)
+}
+
+func runTestOnce(t *testing.T, in, expected string, innerTest func(*Fixer) error) {
+ fixer, err := preProcessIn(in)
if err != nil {
t.Fatal(err)
}
+ out, err := runFixerOnce(fixer, innerTest)
+ if err != nil {
+ out = err.Error()
+ }
+
+ compareResult := compareOutExpected(in, out, expected)
+ if len(compareResult) > 0 {
+ t.Errorf(compareResult)
+ }
+}
+
+func preProcessOutErr(expectedErr string) string {
+ expected := strings.TrimSpace(expectedErr)
+ return expected
+}
+
+func preProcessOut(out string) (expected string, err error) {
+ expected, err = Reformat(out)
+ if err != nil {
+ return expected, err
+ }
+ return expected, nil
+}
+
+func preProcessIn(in string) (fixer *Fixer, err error) {
in, err = Reformat(in)
if err != nil {
- t.Fatal(err)
+ return fixer, err
}
tree, errs := parser.Parse("<testcase>", bytes.NewBufferString(in), parser.NewScope(nil))
if errs != nil {
- t.Fatal(errs)
+ return fixer, err
}
- fixer := NewFixer(tree)
+ fixer = NewFixer(tree)
+
+ return fixer, nil
+}
+
+func runFixerOnce(fixer *Fixer, innerTest func(*Fixer) error) (string, error) {
+ err := innerTest(fixer)
+ if err != nil {
+ return "", err
+ }
+
+ out, err := parser.Print(fixer.tree)
+ if err != nil {
+ return "", err
+ }
+ return string(out), nil
+}
+
+func compareOutExpected(in, out, expected string) string {
+ if out != expected {
+ return fmt.Sprintf("output didn't match:\ninput:\n%s\n\nexpected:\n%s\ngot:\n%s\n",
+ in, expected, out)
+ }
+ return ""
+}
+
+func runPassOnce(t *testing.T, in, out string, innerTest func(*Fixer) error) {
+ expected, err := preProcessOut(out)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ runTestOnce(t, in, expected, innerTest)
+}
+
+func runPass(t *testing.T, in, out string, innerTest func(*Fixer) error) {
+ expected, err := preProcessOut(out)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ fixer, err := preProcessIn(in)
+ if err != nil {
+ t.Fatal(err)
+ }
got := ""
prev := "foo"
passes := 0
for got != prev && passes < 10 {
- err := innerTest(fixer)
- if err != nil {
- t.Fatal(err)
- }
-
- out, err := parser.Print(fixer.tree)
+ out, err = runFixerOnce(fixer, innerTest)
if err != nil {
t.Fatal(err)
}
@@ -162,9 +231,9 @@
passes++
}
- if got != expected {
- t.Errorf("output didn't match:\ninput:\n%s\n\nexpected:\n%s\ngot:\n%s\n",
- in, expected, got)
+ compareResult := compareOutExpected(in, out, expected)
+ if len(compareResult) > 0 {
+ t.Errorf(compareResult)
}
}
@@ -1608,3 +1677,157 @@
})
}
}
+
+func TestRewriteLicenseProperties(t *testing.T) {
+ tests := []struct {
+ name string
+ in string
+ out string
+ }{
+ {
+ name: "license rewriting with one module",
+ in: `
+ android_test {
+ name: "foo",
+ android_license_kinds: ["license_kind"],
+ android_license_conditions: ["license_notice"],
+ }
+ `,
+ out: `
+ package {
+ // See: http://go/android-license-faq
+ default_applicable_licenses: [
+ "Android-Apache-2.0",
+ ],
+ }
+
+ android_test {
+ name: "foo",
+ android_license_kinds: ["license_kind"],
+ android_license_conditions: ["license_notice"],
+ }
+ `,
+ },
+ {
+ name: "license rewriting with two modules",
+ in: `
+ android_test {
+ name: "foo1",
+ android_license_kinds: ["license_kind1"],
+ android_license_conditions: ["license_notice1"],
+ }
+
+ android_test {
+ name: "foo2",
+ android_license_kinds: ["license_kind2"],
+ android_license_conditions: ["license_notice2"],
+ }
+ `,
+ out: `
+ package {
+ // See: http://go/android-license-faq
+ default_applicable_licenses: [
+ "Android-Apache-2.0",
+ ],
+ }
+
+ android_test {
+ name: "foo1",
+ android_license_kinds: ["license_kind1"],
+ android_license_conditions: ["license_notice1"],
+ }
+
+ android_test {
+ name: "foo2",
+ android_license_kinds: ["license_kind2"],
+ android_license_conditions: ["license_notice2"],
+ }
+ `,
+ },
+ // TODO(b/205615944): When valid "android_license_files" exists, the test requires an Android.mk
+ // file (and an Android.bp file is required as well if the license files locates outside the current
+ // directory). So plan to use a mock file system to mock the Android.mk and Android.bp files.
+ }
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ runPassOnce(t, test.in, test.out, runPatchListMod(rewriteLicenseProperties))
+ })
+ }
+}
+
+func TestHaveSameLicense(t *testing.T) {
+ tests := []struct {
+ name string
+ in string
+ out string
+ }{
+ {
+ name: "two modules with the same license",
+ in: `
+ android_test {
+ name: "foo1",
+ android_license_kinds: ["license_kind"],
+ android_license_conditions: ["license_notice"],
+ }
+
+ android_test {
+ name: "foo2",
+ android_license_kinds: ["license_kind"],
+ android_license_conditions: ["license_notice"],
+ }
+ `,
+ out: `
+ android_test {
+ name: "foo1",
+ android_license_kinds: ["license_kind"],
+ android_license_conditions: ["license_notice"],
+ }
+
+ android_test {
+ name: "foo2",
+ android_license_kinds: ["license_kind"],
+ android_license_conditions: ["license_notice"],
+ }
+ `,
+ },
+ }
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ runPassOnce(t, test.in, test.out, func(fixer *Fixer) error {
+ return haveSameLicense(fixer)
+ })
+ })
+ }
+ testErrs := []struct {
+ name string
+ in string
+ expectedErr string
+ }{
+ {
+ name: "two modules will different licenses",
+ in: `
+ android_test {
+ name: "foo1",
+ android_license_kinds: ["license_kind1"],
+ android_license_conditions: ["license_notice1"],
+ }
+
+ android_test {
+ name: "foo2",
+ android_license_kinds: ["license_kind2"],
+ android_license_conditions: ["license_notice2"],
+ }
+ `,
+ expectedErr: `
+ Modules foo1 and foo2 are expected to have the same android_license_kinds property.
+ `,
+ },
+ }
+ for _, test := range testErrs {
+ t.Run(test.name, func(t *testing.T) {
+ checkError(t, test.in, test.expectedErr, func(fixer *Fixer) error {
+ return haveSameLicense(fixer)
+ })
+ })
+ }
+}
diff --git a/cc/cc_test.go b/cc/cc_test.go
index ad2ccae..9ffe48d 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -730,9 +730,16 @@
gtest: false,
}
+ cc_binary {
+ name: "test_bin",
+ relative_install_path: "foo/bar/baz",
+ compile_multilib: "both",
+ }
+
cc_test {
name: "main_test",
data_libs: ["test_lib"],
+ data_bins: ["test_bin"],
gtest: false,
}
`
@@ -750,10 +757,10 @@
t.Fatalf("Expected cc_test to produce output files, error: %s", err)
}
if len(outputFiles) != 1 {
- t.Errorf("expected exactly one output file. output files: [%s]", outputFiles)
+ t.Fatalf("expected exactly one output file. output files: [%s]", outputFiles)
}
- if len(testBinary.dataPaths()) != 1 {
- t.Errorf("expected exactly one test data file. test data files: [%s]", testBinary.dataPaths())
+ if len(testBinary.dataPaths()) != 2 {
+ t.Fatalf("expected exactly one test data file. test data files: [%s]", testBinary.dataPaths())
}
outputPath := outputFiles[0].String()
@@ -766,6 +773,10 @@
t.Errorf("expected LOCAL_TEST_DATA to end with `:test_lib.so:foo/bar/baz`,"+
" but was '%s'", entries.EntryMap["LOCAL_TEST_DATA"][0])
}
+ if !strings.HasSuffix(entries.EntryMap["LOCAL_TEST_DATA"][1], ":test_bin:foo/bar/baz") {
+ t.Errorf("expected LOCAL_TEST_DATA to end with `:test_bin:foo/bar/baz`,"+
+ " but was '%s'", entries.EntryMap["LOCAL_TEST_DATA"][1])
+ }
}
func TestVndkWhenVndkVersionIsNotSet(t *testing.T) {
diff --git a/cc/test.go b/cc/test.go
index c589165..f37fdae 100644
--- a/cc/test.go
+++ b/cc/test.go
@@ -378,31 +378,26 @@
ctx.VisitDirectDepsWithTag(dataLibDepTag, func(dep android.Module) {
depName := ctx.OtherModuleName(dep)
- ccDep, ok := dep.(LinkableInterface)
-
+ linkableDep, ok := dep.(LinkableInterface)
if !ok {
- ctx.ModuleErrorf("data_lib %q is not a linkable cc module", depName)
+ ctx.ModuleErrorf("data_lib %q is not a LinkableInterface module", depName)
}
- ccModule, ok := dep.(*Module)
- if !ok {
- ctx.ModuleErrorf("data_lib %q is not a cc module", depName)
- }
- if ccDep.OutputFile().Valid() {
+ if linkableDep.OutputFile().Valid() {
test.data = append(test.data,
- android.DataPath{SrcPath: ccDep.OutputFile().Path(),
- RelativeInstallPath: ccModule.installer.relativeInstallPath()})
+ android.DataPath{SrcPath: linkableDep.OutputFile().Path(),
+ RelativeInstallPath: linkableDep.RelativeInstallPath()})
}
})
ctx.VisitDirectDepsWithTag(dataBinDepTag, func(dep android.Module) {
depName := ctx.OtherModuleName(dep)
- ccModule, ok := dep.(*Module)
+ linkableDep, ok := dep.(LinkableInterface)
if !ok {
- ctx.ModuleErrorf("data_bin %q is not a cc module", depName)
+ ctx.ModuleErrorf("data_bin %q is not a LinkableInterface module", depName)
}
- if ccModule.OutputFile().Valid() {
+ if linkableDep.OutputFile().Valid() {
test.data = append(test.data,
- android.DataPath{SrcPath: ccModule.OutputFile().Path(),
- RelativeInstallPath: ccModule.installer.relativeInstallPath()})
+ android.DataPath{SrcPath: linkableDep.OutputFile().Path(),
+ RelativeInstallPath: linkableDep.RelativeInstallPath()})
}
})
diff --git a/cc/testing.go b/cc/testing.go
index b0a220c..3bf936d 100644
--- a/cc/testing.go
+++ b/cc/testing.go
@@ -763,3 +763,11 @@
}
return paths
}
+
+func AssertExcludeFromRecoverySnapshotIs(t *testing.T, ctx *android.TestContext, name string, expected bool, variant string) {
+ t.Helper()
+ m := ctx.ModuleForTests(name, variant).Module().(LinkableInterface)
+ if m.ExcludeFromRecoverySnapshot() != expected {
+ t.Errorf("expected %q ExcludeFromRecoverySnapshot to be %t", m.String(), expected)
+ }
+}
diff --git a/cc/vendor_snapshot_test.go b/cc/vendor_snapshot_test.go
index ca2f569..b5022c8 100644
--- a/cc/vendor_snapshot_test.go
+++ b/cc/vendor_snapshot_test.go
@@ -1020,14 +1020,6 @@
assertString(t, staticCfiModule.outputFile.Path().Base(), "libsnapshot.cfi.a")
}
-func assertExcludeFromRecoverySnapshotIs(t *testing.T, ctx *android.TestContext, name string, expected bool) {
- t.Helper()
- m := ctx.ModuleForTests(name, recoveryVariant).Module().(*Module)
- if m.ExcludeFromRecoverySnapshot() != expected {
- t.Errorf("expected %q ExcludeFromRecoverySnapshot to be %t", m.String(), expected)
- }
-}
-
func TestVendorSnapshotExclude(t *testing.T) {
// This test verifies that the exclude_from_vendor_snapshot property
@@ -1371,13 +1363,13 @@
android.FailIfErrored(t, errs)
// Test an include and exclude framework module.
- assertExcludeFromRecoverySnapshotIs(t, ctx, "libinclude", false)
- assertExcludeFromRecoverySnapshotIs(t, ctx, "libexclude", true)
- assertExcludeFromRecoverySnapshotIs(t, ctx, "libavailable_exclude", true)
+ AssertExcludeFromRecoverySnapshotIs(t, ctx, "libinclude", false, recoveryVariant)
+ AssertExcludeFromRecoverySnapshotIs(t, ctx, "libexclude", true, recoveryVariant)
+ AssertExcludeFromRecoverySnapshotIs(t, ctx, "libavailable_exclude", true, recoveryVariant)
// A recovery module is excluded, but by its path, not the
// exclude_from_recovery_snapshot property.
- assertExcludeFromRecoverySnapshotIs(t, ctx, "librecovery", false)
+ AssertExcludeFromRecoverySnapshotIs(t, ctx, "librecovery", false, recoveryVariant)
// Verify the content of the recovery snapshot.
diff --git a/java/app_test.go b/java/app_test.go
index 07439fc..0aae928 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -144,14 +144,14 @@
}
`)
- testJavaError(t, "platform_apis must be true when sdk_version is empty.", `
+ testJavaError(t, "This module has conflicting settings. sdk_version is empty, which means that this module is build against platform APIs. However platform_apis is not set to true", `
android_app {
name: "bar",
srcs: ["b.java"],
}
`)
- testJavaError(t, "platform_apis must be false when sdk_version is not empty.", `
+ testJavaError(t, "This module has conflicting settings. sdk_version is not empty, which means this module cannot use platform APIs. However platform_apis is set to true.", `
android_app {
name: "bar",
srcs: ["b.java"],
diff --git a/java/base.go b/java/base.go
index c1a5299..71b5ac8 100644
--- a/java/base.go
+++ b/java/base.go
@@ -510,9 +510,9 @@
usePlatformAPI := proptools.Bool(j.deviceProperties.Platform_apis)
sdkVersionSpecified := sc.SdkVersion(ctx).Specified()
if usePlatformAPI && sdkVersionSpecified {
- ctx.PropertyErrorf("platform_apis", "platform_apis must be false when sdk_version is not empty.")
+ ctx.PropertyErrorf("platform_apis", "This module has conflicting settings. sdk_version is not empty, which means this module cannot use platform APIs. However platform_apis is set to true.")
} else if !usePlatformAPI && !sdkVersionSpecified {
- ctx.PropertyErrorf("platform_apis", "platform_apis must be true when sdk_version is empty.")
+ ctx.PropertyErrorf("platform_apis", "This module has conflicting settings. sdk_version is empty, which means that this module is build against platform APIs. However platform_apis is not set to true")
}
}
diff --git a/rust/Android.bp b/rust/Android.bp
index cda2dbc..5e14da8 100644
--- a/rust/Android.bp
+++ b/rust/Android.bp
@@ -38,6 +38,7 @@
"strip.go",
"test.go",
"testing.go",
+ "toolchain_library.go",
],
testSrcs: [
"benchmark_test.go",
diff --git a/rust/rust.go b/rust/rust.go
index 3cc7868..b575c7a 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -393,6 +393,10 @@
WholeStaticLibs []string
HeaderLibs []string
+ // Used for data dependencies adjacent to tests
+ DataLibs []string
+ DataBins []string
+
CrtBegin, CrtEnd string
}
@@ -577,7 +581,7 @@
func (mod *Module) CcLibrary() bool {
if mod.compiler != nil {
- if _, ok := mod.compiler.(*libraryDecorator); ok {
+ if _, ok := mod.compiler.(libraryInterface); ok {
return true
}
}
@@ -982,6 +986,8 @@
procMacroDepTag = dependencyTag{name: "procMacro", procMacro: true}
testPerSrcDepTag = dependencyTag{name: "rust_unit_tests"}
sourceDepTag = dependencyTag{name: "source"}
+ dataLibDepTag = dependencyTag{name: "data lib"}
+ dataBinDepTag = dependencyTag{name: "data bin"}
)
func IsDylibDepTag(depTag blueprint.DependencyTag) bool {
@@ -1428,6 +1434,12 @@
}
}
+ actx.AddVariationDependencies([]blueprint.Variation{
+ {Mutator: "link", Variation: "shared"},
+ }, dataLibDepTag, deps.DataLibs...)
+
+ actx.AddVariationDependencies(nil, dataBinDepTag, deps.DataBins...)
+
// proc_macros are compiler plugins, and so we need the host arch variant as a dependendcy.
actx.AddFarVariationDependencies(ctx.Config().BuildOSTarget.Variations(), procMacroDepTag, deps.ProcMacros...)
}
@@ -1542,7 +1554,7 @@
// Overrides ApexModule.IsInstallabeToApex()
func (mod *Module) IsInstallableToApex() bool {
if mod.compiler != nil {
- if lib, ok := mod.compiler.(*libraryDecorator); ok && (lib.shared() || lib.dylib()) {
+ if lib, ok := mod.compiler.(libraryInterface); ok && (lib.shared() || lib.dylib()) {
return true
}
if _, ok := mod.compiler.(*binaryDecorator); ok {
diff --git a/rust/rust_test.go b/rust/rust_test.go
index b99b1e6..97bd541 100644
--- a/rust/rust_test.go
+++ b/rust/rust_test.go
@@ -79,8 +79,10 @@
}
const (
- sharedVendorVariant = "android_vendor.29_arm64_armv8-a_shared"
- rlibVendorVariant = "android_vendor.29_arm64_armv8-a_rlib_rlib-std"
+ sharedVendorVariant = "android_vendor.29_arm64_armv8-a_shared"
+ rlibVendorVariant = "android_vendor.29_arm64_armv8-a_rlib_rlib-std"
+ sharedRecoveryVariant = "android_recovery_arm64_armv8-a_shared"
+ rlibRecoveryVariant = "android_recovery_arm64_armv8-a_rlib_rlib-std"
)
func testRustVndkFs(t *testing.T, bp string, fs android.MockFS) *android.TestContext {
@@ -101,7 +103,22 @@
),
).RunTestWithBp(t, bp)
return result.TestContext
+}
+func testRustRecoveryFsVersions(t *testing.T, bp string, fs android.MockFS, device_version, vndk_version, recovery_version string) *android.TestContext {
+ skipTestIfOsNotSupported(t)
+ result := android.GroupFixturePreparers(
+ prepareForRustTest,
+ fs.AddToFixture(),
+ android.FixtureModifyProductVariables(
+ func(variables android.FixtureProductVariables) {
+ variables.DeviceVndkVersion = StringPtr(device_version)
+ variables.RecoverySnapshotVersion = StringPtr(recovery_version)
+ variables.Platform_vndk_version = StringPtr(vndk_version)
+ },
+ ),
+ ).RunTestWithBp(t, bp)
+ return result.TestContext
}
// testRustCov returns a TestContext in which a basic environment has been
diff --git a/rust/test.go b/rust/test.go
index bb877a9..3eea0ad 100644
--- a/rust/test.go
+++ b/rust/test.go
@@ -18,6 +18,7 @@
"github.com/google/blueprint/proptools"
"android/soong/android"
+ "android/soong/cc"
"android/soong/tradefed"
)
@@ -49,6 +50,12 @@
// the test
Data []string `android:"path,arch_variant"`
+ // list of shared library modules that should be installed alongside the test
+ Data_libs []string `android:"arch_variant"`
+
+ // list of binary modules that should be installed alongside the test
+ Data_bins []string `android:"arch_variant"`
+
// Flag to indicate whether or not to create test config automatically. If AndroidTest.xml
// doesn't exist next to the Android.bp, this attribute doesn't need to be set to true
// explicitly.
@@ -137,6 +144,32 @@
dataSrcPaths := android.PathsForModuleSrc(ctx, test.Properties.Data)
+ ctx.VisitDirectDepsWithTag(dataLibDepTag, func(dep android.Module) {
+ depName := ctx.OtherModuleName(dep)
+ linkableDep, ok := dep.(cc.LinkableInterface)
+ if !ok {
+ ctx.ModuleErrorf("data_lib %q is not a linkable module", depName)
+ }
+ if linkableDep.OutputFile().Valid() {
+ test.data = append(test.data,
+ android.DataPath{SrcPath: linkableDep.OutputFile().Path(),
+ RelativeInstallPath: linkableDep.RelativeInstallPath()})
+ }
+ })
+
+ ctx.VisitDirectDepsWithTag(dataBinDepTag, func(dep android.Module) {
+ depName := ctx.OtherModuleName(dep)
+ linkableDep, ok := dep.(cc.LinkableInterface)
+ if !ok {
+ ctx.ModuleErrorf("data_bin %q is not a linkable module", depName)
+ }
+ if linkableDep.OutputFile().Valid() {
+ test.data = append(test.data,
+ android.DataPath{SrcPath: linkableDep.OutputFile().Path(),
+ RelativeInstallPath: linkableDep.RelativeInstallPath()})
+ }
+ })
+
for _, dataSrcPath := range dataSrcPaths {
test.data = append(test.data, android.DataPath{SrcPath: dataSrcPath})
}
@@ -194,6 +227,9 @@
deps.Rustlibs = append(deps.Rustlibs, "libtest")
+ deps.DataLibs = append(deps.DataLibs, test.Properties.Data_libs...)
+ deps.DataBins = append(deps.DataBins, test.Properties.Data_bins...)
+
return deps
}
diff --git a/rust/test_test.go b/rust/test_test.go
index 892761a..1124176 100644
--- a/rust/test_test.go
+++ b/rust/test_test.go
@@ -74,3 +74,129 @@
t.Errorf("Device rust_test module 'my_test' does not link libstd as an rlib")
}
}
+
+func TestDataLibs(t *testing.T) {
+ bp := `
+ cc_library {
+ name: "test_lib",
+ srcs: ["test_lib.cpp"],
+ }
+
+ rust_binary {
+ name: "rusty",
+ srcs: ["foo.rs"],
+ compile_multilib: "both",
+ }
+
+ rust_ffi {
+ name: "librust_test_lib",
+ crate_name: "rust_test_lib",
+ srcs: ["test_lib.rs"],
+ relative_install_path: "foo/bar/baz",
+ compile_multilib: "64",
+ }
+
+ rust_test {
+ name: "main_test",
+ srcs: ["foo.rs"],
+ data_libs: ["test_lib"],
+ data_bins: ["rusty"],
+ }
+ `
+
+ ctx := testRust(t, bp)
+
+ module := ctx.ModuleForTests("main_test", "android_arm64_armv8-a").Module()
+ testBinary := module.(*Module).compiler.(*testDecorator)
+ outputFiles, err := module.(android.OutputFileProducer).OutputFiles("")
+ if err != nil {
+ t.Fatalf("Expected rust_test to produce output files, error: %s", err)
+ }
+ if len(outputFiles) != 1 {
+ t.Fatalf("expected exactly one output file. output files: [%s]", outputFiles)
+ }
+ if len(testBinary.dataPaths()) != 2 {
+ t.Fatalf("expected exactly two test data files. test data files: [%s]", testBinary.dataPaths())
+ }
+
+ outputPath := outputFiles[0].String()
+ dataLibraryPath := testBinary.dataPaths()[0].SrcPath.String()
+ dataBinaryPath := testBinary.dataPaths()[1].SrcPath.String()
+
+ if !strings.HasSuffix(outputPath, "/main_test") {
+ t.Errorf("expected test output file to be 'main_test', but was '%s'", outputPath)
+ }
+ if !strings.HasSuffix(dataLibraryPath, "/test_lib.so") {
+ t.Errorf("expected test data file to be 'test_lib.so', but was '%s'", dataLibraryPath)
+ }
+ if !strings.HasSuffix(dataBinaryPath, "/rusty") {
+ t.Errorf("expected test data file to be 'test_lib.so', but was '%s'", dataBinaryPath)
+ }
+}
+
+func TestDataLibsRelativeInstallPath(t *testing.T) {
+ bp := `
+ cc_library {
+ name: "test_lib",
+ srcs: ["test_lib.cpp"],
+ relative_install_path: "foo/bar/baz",
+ compile_multilib: "64",
+ }
+
+ rust_ffi {
+ name: "librust_test_lib",
+ crate_name: "rust_test_lib",
+ srcs: ["test_lib.rs"],
+ relative_install_path: "foo/bar/baz",
+ compile_multilib: "64",
+ }
+
+ rust_binary {
+ name: "rusty",
+ srcs: ["foo.rs"],
+ relative_install_path: "foo/bar/baz",
+ compile_multilib: "64",
+ }
+
+ rust_test {
+ name: "main_test",
+ srcs: ["foo.rs"],
+ data_libs: ["test_lib", "librust_test_lib"],
+ data_bins: ["rusty"],
+ compile_multilib: "64",
+ }
+ `
+
+ ctx := testRust(t, bp)
+ module := ctx.ModuleForTests("main_test", "android_arm64_armv8-a").Module()
+ testBinary := module.(*Module).compiler.(*testDecorator)
+ outputFiles, err := module.(android.OutputFileProducer).OutputFiles("")
+ if err != nil {
+ t.Fatalf("Expected rust_test to produce output files, error: %s", err)
+ }
+ if len(outputFiles) != 1 {
+ t.Fatalf("expected exactly one output file. output files: [%s]", outputFiles)
+ }
+ if len(testBinary.dataPaths()) != 3 {
+ t.Fatalf("expected exactly two test data files. test data files: [%s]", testBinary.dataPaths())
+ }
+
+ outputPath := outputFiles[0].String()
+
+ if !strings.HasSuffix(outputPath, "/main_test") {
+ t.Errorf("expected test output file to be 'main_test', but was '%s'", outputPath)
+ }
+ entries := android.AndroidMkEntriesForTest(t, ctx, module)[0]
+ if !strings.HasSuffix(entries.EntryMap["LOCAL_TEST_DATA"][0], ":test_lib.so:foo/bar/baz") {
+ t.Errorf("expected LOCAL_TEST_DATA to end with `:test_lib.so:foo/bar/baz`,"+
+ " but was '%s'", entries.EntryMap["LOCAL_TEST_DATA"][0])
+ }
+ if !strings.HasSuffix(entries.EntryMap["LOCAL_TEST_DATA"][1], ":librust_test_lib.so:foo/bar/baz") {
+ t.Errorf("expected LOCAL_TEST_DATA to end with `:librust_test_lib.so:foo/bar/baz`,"+
+ " but was '%s'", entries.EntryMap["LOCAL_TEST_DATA"][1])
+ }
+ if !strings.HasSuffix(entries.EntryMap["LOCAL_TEST_DATA"][2], ":rusty:foo/bar/baz") {
+ t.Errorf("expected LOCAL_TEST_DATA to end with `:rusty:foo/bar/baz`,"+
+ " but was '%s'", entries.EntryMap["LOCAL_TEST_DATA"][2])
+ }
+}
diff --git a/rust/testing.go b/rust/testing.go
index 9f8ed54..1b34dfe 100644
--- a/rust/testing.go
+++ b/rust/testing.go
@@ -75,6 +75,7 @@
apex_available: ["//apex_available:platform", "//apex_available:anyapex"],
min_sdk_version: "29",
vendor_available: true,
+ recovery_available: true,
llndk: {
symbol_file: "liblog.map.txt",
},
@@ -106,6 +107,7 @@
host_supported: true,
vendor_available: true,
vendor_ramdisk_available: true,
+ recovery_available: true,
native_coverage: false,
sysroot: true,
apex_available: ["//apex_available:platform", "//apex_available:anyapex"],
@@ -118,6 +120,7 @@
host_supported: true,
vendor_available: true,
vendor_ramdisk_available: true,
+ recovery_available: true,
native_coverage: false,
apex_available: ["//apex_available:platform", "//apex_available:anyapex"],
min_sdk_version: "29",
diff --git a/rust/toolchain_library.go b/rust/toolchain_library.go
new file mode 100644
index 0000000..326d529
--- /dev/null
+++ b/rust/toolchain_library.go
@@ -0,0 +1,103 @@
+//
+// Copyright (C) 2021 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 rust
+
+import (
+ "path"
+
+ "android/soong/android"
+ "android/soong/rust/config"
+)
+
+// This module is used to compile the rust toolchain libraries
+// When RUST_PREBUILTS_VERSION is set, the library will generated
+// from the given Rust version.
+func init() {
+ android.RegisterModuleType("rust_toolchain_library",
+ rustToolchainLibraryFactory)
+ android.RegisterModuleType("rust_toolchain_library_rlib",
+ rustToolchainLibraryRlibFactory)
+ android.RegisterModuleType("rust_toolchain_library_dylib",
+ rustToolchainLibraryDylibFactory)
+}
+
+type toolchainLibraryProperties struct {
+ // path to the toolchain source, relative to the top of the toolchain source
+ Toolchain_src *string `android:"arch_variant"`
+}
+
+type toolchainLibraryDecorator struct {
+ *libraryDecorator
+ Properties toolchainLibraryProperties
+}
+
+// rust_toolchain_library produces all rust variants.
+func rustToolchainLibraryFactory() android.Module {
+ module, library := NewRustLibrary(android.HostAndDeviceSupported)
+ library.BuildOnlyRust()
+
+ return initToolchainLibrary(module, library)
+}
+
+// rust_toolchain_library_dylib produces a dylib.
+func rustToolchainLibraryDylibFactory() android.Module {
+ module, library := NewRustLibrary(android.HostAndDeviceSupported)
+ library.BuildOnlyDylib()
+
+ return initToolchainLibrary(module, library)
+}
+
+// rust_toolchain_library_rlib produces an rlib.
+func rustToolchainLibraryRlibFactory() android.Module {
+ module, library := NewRustLibrary(android.HostAndDeviceSupported)
+ library.BuildOnlyRlib()
+
+ return initToolchainLibrary(module, library)
+}
+
+func initToolchainLibrary(module *Module, library *libraryDecorator) android.Module {
+ toolchainLibrary := &toolchainLibraryDecorator{
+ libraryDecorator: library,
+ }
+ module.compiler = toolchainLibrary
+ module.AddProperties(&toolchainLibrary.Properties)
+ android.AddLoadHook(module, rustSetToolchainSource)
+
+ return module.Init()
+}
+
+func rustSetToolchainSource(ctx android.LoadHookContext) {
+ if toolchainLib, ok := ctx.Module().(*Module).compiler.(*toolchainLibraryDecorator); ok {
+ prefix := "linux-x86/" + GetRustPrebuiltVersion(ctx)
+ newSrcs := []string{path.Join(prefix, android.String(toolchainLib.Properties.Toolchain_src))}
+
+ type props struct {
+ Srcs []string
+ }
+ p := &props{}
+ p.Srcs = newSrcs
+ ctx.AppendProperties(p)
+
+ } else {
+ ctx.ModuleErrorf("Called rustSetToolchainSource on a non-Rust Module.")
+ }
+}
+
+// GetRustPrebuiltVersion returns the RUST_PREBUILTS_VERSION env var, or the default version if it is not defined.
+func GetRustPrebuiltVersion(ctx android.LoadHookContext) string {
+ return ctx.AConfig().GetenvWithDefault("RUST_PREBUILTS_VERSION", config.RustDefaultVersion)
+}
diff --git a/rust/vendor_snapshot_test.go b/rust/vendor_snapshot_test.go
index bfa6f36..03bd867 100644
--- a/rust/vendor_snapshot_test.go
+++ b/rust/vendor_snapshot_test.go
@@ -1028,3 +1028,354 @@
t.Errorf("wanted memtag_binary AndroidMkStaticLibs %q, got %q", w, g)
}
}
+
+func TestRecoverySnapshotCapture(t *testing.T) {
+ bp := `
+ rust_ffi {
+ name: "librecovery",
+ recovery: true,
+ srcs: ["foo.rs"],
+ crate_name: "recovery",
+ }
+
+ rust_ffi {
+ name: "librecovery_available",
+ recovery_available: true,
+ srcs: ["foo.rs"],
+ crate_name: "recovery_available",
+ }
+
+ rust_library_rlib {
+ name: "librecovery_rlib",
+ recovery: true,
+ srcs: ["foo.rs"],
+ crate_name: "recovery_rlib",
+ }
+
+ rust_library_rlib {
+ name: "librecovery_available_rlib",
+ recovery_available: true,
+ srcs: ["foo.rs"],
+ crate_name: "recovery_available_rlib",
+ }
+
+ rust_binary {
+ name: "recovery_bin",
+ recovery: true,
+ srcs: ["foo.rs"],
+ }
+
+ rust_binary {
+ name: "recovery_available_bin",
+ recovery_available: true,
+ srcs: ["foo.rs"],
+ }
+
+`
+ // Check Recovery snapshot output.
+
+ ctx := testRustRecoveryFsVersions(t, bp, rustMockedFiles, "", "29", "current")
+ snapshotDir := "recovery-snapshot"
+ snapshotVariantPath := filepath.Join("out/soong", snapshotDir, "arm64")
+ snapshotSingleton := ctx.SingletonForTests("recovery-snapshot")
+
+ var jsonFiles []string
+
+ for _, arch := range [][]string{
+ []string{"arm64", "armv8-a"},
+ } {
+ archType := arch[0]
+ archVariant := arch[1]
+ archDir := fmt.Sprintf("arch-%s-%s", archType, archVariant)
+
+ // For shared libraries, all recovery:true and recovery_available modules are captured.
+ sharedVariant := fmt.Sprintf("android_recovery_%s_%s_shared", archType, archVariant)
+ sharedDir := filepath.Join(snapshotVariantPath, archDir, "shared")
+ cc.CheckSnapshot(t, ctx, snapshotSingleton, "librecovery", "librecovery.so", sharedDir, sharedVariant)
+ cc.CheckSnapshot(t, ctx, snapshotSingleton, "librecovery_available", "librecovery_available.so", sharedDir, sharedVariant)
+ jsonFiles = append(jsonFiles,
+ filepath.Join(sharedDir, "librecovery.so.json"),
+ filepath.Join(sharedDir, "librecovery_available.so.json"))
+
+ // For static libraries, all recovery:true and recovery_available modules are captured.
+ staticVariant := fmt.Sprintf("android_recovery_%s_%s_static", archType, archVariant)
+ staticDir := filepath.Join(snapshotVariantPath, archDir, "static")
+ cc.CheckSnapshot(t, ctx, snapshotSingleton, "librecovery", "librecovery.a", staticDir, staticVariant)
+ cc.CheckSnapshot(t, ctx, snapshotSingleton, "librecovery_available", "librecovery_available.a", staticDir, staticVariant)
+ jsonFiles = append(jsonFiles,
+ filepath.Join(staticDir, "librecovery.a.json"),
+ filepath.Join(staticDir, "librecovery_available.a.json"))
+
+ // For rlib libraries, all recovery:true and recovery_available modules are captured.
+ rlibVariant := fmt.Sprintf("android_recovery_%s_%s_rlib_rlib-std", archType, archVariant)
+ rlibDir := filepath.Join(snapshotVariantPath, archDir, "rlib")
+ cc.CheckSnapshot(t, ctx, snapshotSingleton, "librecovery_rlib", "librecovery_rlib.rlib", rlibDir, rlibVariant)
+ cc.CheckSnapshot(t, ctx, snapshotSingleton, "librecovery_available_rlib", "librecovery_available_rlib.rlib", rlibDir, rlibVariant)
+ jsonFiles = append(jsonFiles,
+ filepath.Join(rlibDir, "librecovery_rlib.rlib.json"),
+ filepath.Join(rlibDir, "librecovery_available_rlib.rlib.json"))
+
+ // For binary executables, all recovery:true and recovery_available modules are captured.
+ if archType == "arm64" {
+ binaryVariant := fmt.Sprintf("android_recovery_%s_%s", archType, archVariant)
+ binaryDir := filepath.Join(snapshotVariantPath, archDir, "binary")
+ cc.CheckSnapshot(t, ctx, snapshotSingleton, "recovery_bin", "recovery_bin", binaryDir, binaryVariant)
+ cc.CheckSnapshot(t, ctx, snapshotSingleton, "recovery_available_bin", "recovery_available_bin", binaryDir, binaryVariant)
+ jsonFiles = append(jsonFiles,
+ filepath.Join(binaryDir, "recovery_bin.json"),
+ filepath.Join(binaryDir, "recovery_available_bin.json"))
+ }
+ }
+
+ for _, jsonFile := range jsonFiles {
+ // verify all json files exist
+ if snapshotSingleton.MaybeOutput(jsonFile).Rule == nil {
+ t.Errorf("%q expected but not found", jsonFile)
+ }
+ }
+}
+
+func TestRecoverySnapshotExclude(t *testing.T) {
+ // This test verifies that the exclude_from_recovery_snapshot property
+ // makes its way from the Android.bp source file into the module data
+ // structure. It also verifies that modules are correctly included or
+ // excluded in the recovery snapshot based on their path (framework or
+ // vendor) and the exclude_from_recovery_snapshot property.
+
+ frameworkBp := `
+ rust_ffi_shared {
+ name: "libinclude",
+ srcs: ["src/include.rs"],
+ recovery_available: true,
+ crate_name: "include",
+ }
+ rust_ffi_shared {
+ name: "libexclude",
+ srcs: ["src/exclude.rs"],
+ recovery: true,
+ exclude_from_recovery_snapshot: true,
+ crate_name: "exclude",
+ }
+ rust_ffi_shared {
+ name: "libavailable_exclude",
+ srcs: ["src/exclude.rs"],
+ recovery_available: true,
+ exclude_from_recovery_snapshot: true,
+ crate_name: "available_exclude",
+ }
+ rust_library_rlib {
+ name: "libinclude_rlib",
+ srcs: ["src/include.rs"],
+ recovery_available: true,
+ crate_name: "include_rlib",
+ }
+ rust_library_rlib {
+ name: "libexclude_rlib",
+ srcs: ["src/exclude.rs"],
+ recovery: true,
+ exclude_from_recovery_snapshot: true,
+ crate_name: "exclude_rlib",
+ }
+ rust_library_rlib {
+ name: "libavailable_exclude_rlib",
+ srcs: ["src/exclude.rs"],
+ recovery_available: true,
+ exclude_from_recovery_snapshot: true,
+ crate_name: "available_exclude_rlib",
+ }
+ `
+
+ vendorProprietaryBp := `
+ rust_ffi_shared {
+ name: "librecovery",
+ srcs: ["recovery.rs"],
+ recovery: true,
+ crate_name: "recovery",
+ }
+ rust_library_rlib {
+ name: "librecovery_rlib",
+ srcs: ["recovery.rs"],
+ recovery: true,
+ crate_name: "recovery_rlib",
+ }
+ `
+
+ mockFS := map[string][]byte{
+ "framework/Android.bp": []byte(frameworkBp),
+ "framework/include.rs": nil,
+ "framework/exclude.rs": nil,
+ "device/Android.bp": []byte(vendorProprietaryBp),
+ "device/recovery.rs": nil,
+ }
+
+ ctx := testRustRecoveryFsVersions(t, "", mockFS, "", "29", "current")
+
+ // Test an include and exclude framework module.
+ cc.AssertExcludeFromRecoverySnapshotIs(t, ctx, "libinclude", false, sharedRecoveryVariant)
+ cc.AssertExcludeFromRecoverySnapshotIs(t, ctx, "libexclude", true, sharedRecoveryVariant)
+ cc.AssertExcludeFromRecoverySnapshotIs(t, ctx, "libavailable_exclude", true, sharedRecoveryVariant)
+ cc.AssertExcludeFromRecoverySnapshotIs(t, ctx, "libinclude_rlib", false, rlibRecoveryVariant)
+ cc.AssertExcludeFromRecoverySnapshotIs(t, ctx, "libexclude_rlib", true, rlibRecoveryVariant)
+ cc.AssertExcludeFromRecoverySnapshotIs(t, ctx, "libavailable_exclude_rlib", true, rlibRecoveryVariant)
+
+ // A recovery module is excluded, but by its path not the exclude_from_recovery_snapshot property
+ // ('device/' and 'vendor/' are default excluded). See snapshot/recovery_snapshot.go for more detail.
+ cc.AssertExcludeFromRecoverySnapshotIs(t, ctx, "librecovery", false, sharedRecoveryVariant)
+ cc.AssertExcludeFromRecoverySnapshotIs(t, ctx, "librecovery_rlib", false, rlibRecoveryVariant)
+
+ // Verify the content of the recovery snapshot.
+
+ snapshotDir := "recovery-snapshot"
+ snapshotVariantPath := filepath.Join("out/soong", snapshotDir, "arm64")
+ snapshotSingleton := ctx.SingletonForTests("recovery-snapshot")
+
+ var includeJsonFiles []string
+ var excludeJsonFiles []string
+
+ for _, arch := range [][]string{
+ []string{"arm64", "armv8-a"},
+ } {
+ archType := arch[0]
+ archVariant := arch[1]
+ archDir := fmt.Sprintf("arch-%s-%s", archType, archVariant)
+
+ sharedVariant := fmt.Sprintf("android_recovery_%s_%s_shared", archType, archVariant)
+ rlibVariant := fmt.Sprintf("android_recovery_%s_%s_rlib_rlib-std", archType, archVariant)
+ sharedDir := filepath.Join(snapshotVariantPath, archDir, "shared")
+ rlibDir := filepath.Join(snapshotVariantPath, archDir, "rlib")
+
+ // Included modules
+ cc.CheckSnapshot(t, ctx, snapshotSingleton, "libinclude", "libinclude.so", sharedDir, sharedVariant)
+ includeJsonFiles = append(includeJsonFiles, filepath.Join(sharedDir, "libinclude.so.json"))
+ cc.CheckSnapshot(t, ctx, snapshotSingleton, "libinclude_rlib", "libinclude_rlib.rlib", rlibDir, rlibVariant)
+ includeJsonFiles = append(includeJsonFiles, filepath.Join(rlibDir, "libinclude_rlib.rlib.json"))
+
+ // Excluded modules
+ cc.CheckSnapshotExclude(t, ctx, snapshotSingleton, "libexclude", "libexclude.so", sharedDir, sharedVariant)
+ excludeJsonFiles = append(excludeJsonFiles, filepath.Join(sharedDir, "libexclude.so.json"))
+ cc.CheckSnapshotExclude(t, ctx, snapshotSingleton, "librecovery", "librecovery.so", sharedDir, sharedVariant)
+ excludeJsonFiles = append(excludeJsonFiles, filepath.Join(sharedDir, "librecovery.so.json"))
+ cc.CheckSnapshotExclude(t, ctx, snapshotSingleton, "libavailable_exclude", "libavailable_exclude.so", sharedDir, sharedVariant)
+ excludeJsonFiles = append(excludeJsonFiles, filepath.Join(sharedDir, "libavailable_exclude.so.json"))
+ cc.CheckSnapshotExclude(t, ctx, snapshotSingleton, "libexclude_rlib", "libexclude_rlib.rlib", rlibDir, rlibVariant)
+ excludeJsonFiles = append(excludeJsonFiles, filepath.Join(rlibDir, "libexclude_rlib.rlib.json"))
+ cc.CheckSnapshotExclude(t, ctx, snapshotSingleton, "librecovery_rlib", "librecovery_rlib.rlib", rlibDir, rlibVariant)
+ excludeJsonFiles = append(excludeJsonFiles, filepath.Join(rlibDir, "librecovery_rlib.rlib.json"))
+ cc.CheckSnapshotExclude(t, ctx, snapshotSingleton, "libavailable_exclude_rlib", "libavailable_exclude_rlib.rlib", rlibDir, rlibVariant)
+ excludeJsonFiles = append(excludeJsonFiles, filepath.Join(rlibDir, "libavailable_exclude_rlib.rlib.json"))
+ }
+
+ // Verify that each json file for an included module has a rule.
+ for _, jsonFile := range includeJsonFiles {
+ if snapshotSingleton.MaybeOutput(jsonFile).Rule == nil {
+ t.Errorf("include json file %q not found", jsonFile)
+ }
+ }
+
+ // Verify that each json file for an excluded module has no rule.
+ for _, jsonFile := range excludeJsonFiles {
+ if snapshotSingleton.MaybeOutput(jsonFile).Rule != nil {
+ t.Errorf("exclude json file %q found", jsonFile)
+ }
+ }
+}
+
+func TestRecoverySnapshotDirected(t *testing.T) {
+ bp := `
+ rust_ffi_shared {
+ name: "librecovery",
+ recovery: true,
+ crate_name: "recovery",
+ srcs: ["foo.rs"],
+ }
+
+ rust_ffi_shared {
+ name: "librecovery_available",
+ recovery_available: true,
+ crate_name: "recovery_available",
+ srcs: ["foo.rs"],
+ }
+
+ rust_library_rlib {
+ name: "librecovery_rlib",
+ recovery: true,
+ crate_name: "recovery",
+ srcs: ["foo.rs"],
+ }
+
+ rust_library_rlib {
+ name: "librecovery_available_rlib",
+ recovery_available: true,
+ crate_name: "recovery_available",
+ srcs: ["foo.rs"],
+ }
+
+ /* TODO: Uncomment when Rust supports the "prefer" property for prebuilts
+ rust_library_rlib {
+ name: "libfoo_rlib",
+ recovery: true,
+ crate_name: "foo",
+ }
+
+ rust_prebuilt_rlib {
+ name: "libfoo_rlib",
+ recovery: true,
+ prefer: true,
+ srcs: ["libfoo.rlib"],
+ crate_name: "foo",
+ }
+ */
+`
+ ctx := testRustRecoveryFsVersions(t, bp, rustMockedFiles, "current", "29", "current")
+ ctx.Config().TestProductVariables.RecoverySnapshotModules = make(map[string]bool)
+ ctx.Config().TestProductVariables.RecoverySnapshotModules["librecovery"] = true
+ ctx.Config().TestProductVariables.RecoverySnapshotModules["librecovery_rlib"] = true
+ ctx.Config().TestProductVariables.DirectedRecoverySnapshot = true
+
+ // Check recovery snapshot output.
+ snapshotDir := "recovery-snapshot"
+ snapshotVariantPath := filepath.Join("out/soong", snapshotDir, "arm64")
+ snapshotSingleton := ctx.SingletonForTests("recovery-snapshot")
+
+ var includeJsonFiles []string
+
+ for _, arch := range [][]string{
+ []string{"arm64", "armv8-a"},
+ } {
+ archType := arch[0]
+ archVariant := arch[1]
+ archDir := fmt.Sprintf("arch-%s-%s", archType, archVariant)
+
+ sharedVariant := fmt.Sprintf("android_recovery_%s_%s_shared", archType, archVariant)
+ rlibVariant := fmt.Sprintf("android_recovery_%s_%s_rlib_rlib-std", archType, archVariant)
+ sharedDir := filepath.Join(snapshotVariantPath, archDir, "shared")
+ rlibDir := filepath.Join(snapshotVariantPath, archDir, "rlib")
+
+ // Included modules
+ cc.CheckSnapshot(t, ctx, snapshotSingleton, "librecovery", "librecovery.so", sharedDir, sharedVariant)
+ includeJsonFiles = append(includeJsonFiles, filepath.Join(sharedDir, "librecovery.so.json"))
+ cc.CheckSnapshot(t, ctx, snapshotSingleton, "librecovery_rlib", "librecovery_rlib.rlib", rlibDir, rlibVariant)
+ includeJsonFiles = append(includeJsonFiles, filepath.Join(rlibDir, "librecovery_rlib.rlib.json"))
+
+ // TODO: When Rust supports the "prefer" property for prebuilts, perform this check.
+ /*
+ // Check that snapshot captures "prefer: true" prebuilt
+ cc.CheckSnapshot(t, ctx, snapshotSingleton, "prebuilt_libfoo_rlib", "libfoo_rlib.rlib", rlibDir, rlibVariant)
+ includeJsonFiles = append(includeJsonFiles, filepath.Join(sharedDir, "libfoo_rlib.rlib.json"))
+ */
+
+ // Excluded modules. Modules not included in the directed recovery snapshot
+ // are still included as fake modules.
+ cc.CheckSnapshotRule(t, ctx, snapshotSingleton, "librecovery_available", "librecovery_available.so", sharedDir, sharedVariant)
+ includeJsonFiles = append(includeJsonFiles, filepath.Join(sharedDir, "librecovery_available.so.json"))
+ cc.CheckSnapshotRule(t, ctx, snapshotSingleton, "librecovery_available_rlib", "librecovery_available_rlib.rlib", rlibDir, rlibVariant)
+ includeJsonFiles = append(includeJsonFiles, filepath.Join(rlibDir, "librecovery_available_rlib.rlib.json"))
+ }
+
+ // Verify that each json file for an included module has a rule.
+ for _, jsonFile := range includeJsonFiles {
+ if snapshotSingleton.MaybeOutput(jsonFile).Rule == nil {
+ t.Errorf("include json file %q not found, %#v", jsonFile, includeJsonFiles)
+ }
+ }
+}
diff --git a/scripts/build-mainline-modules.sh b/scripts/build-mainline-modules.sh
index f183c05..1e3f6ce 100755
--- a/scripts/build-mainline-modules.sh
+++ b/scripts/build-mainline-modules.sh
@@ -17,7 +17,6 @@
MODULES_SDK_AND_EXPORTS=(
art-module-sdk
art-module-test-exports
- compos-module-sdk
conscrypt-module-host-exports
conscrypt-module-sdk
conscrypt-module-test-exports
diff --git a/ui/metrics/Android.bp b/ui/metrics/Android.bp
index 1590ab0..96f6389 100644
--- a/ui/metrics/Android.bp
+++ b/ui/metrics/Android.bp
@@ -23,6 +23,7 @@
"golang-protobuf-proto",
"soong-ui-metrics_upload_proto",
"soong-ui-metrics_proto",
+ "soong-ui-bp2build_metrics_proto",
"soong-ui-tracer",
],
srcs: [
@@ -57,3 +58,15 @@
"upload_proto/upload.pb.go",
],
}
+
+bootstrap_go_package {
+ name: "soong-ui-bp2build_metrics_proto",
+ pkgPath: "android/soong/ui/metrics/bp2build_metrics_proto",
+ deps: [
+ "golang-protobuf-reflect-protoreflect",
+ "golang-protobuf-runtime-protoimpl",
+ ],
+ srcs: [
+ "bp2build_metrics_proto/bp2build_metrics.pb.go",
+ ],
+}
diff --git a/ui/metrics/bp2build_metrics_proto/bp2build_metrics.pb.go b/ui/metrics/bp2build_metrics_proto/bp2build_metrics.pb.go
new file mode 100644
index 0000000..11177e4
--- /dev/null
+++ b/ui/metrics/bp2build_metrics_proto/bp2build_metrics.pb.go
@@ -0,0 +1,222 @@
+// Copyright 2021 Google Inc. All Rights Reserved.
+//
+// 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.
+
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// protoc-gen-go v1.27.1
+// protoc v3.9.1
+// source: bp2build_metrics.proto
+
+package bp2build_metrics_proto
+
+import (
+ protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+ protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+ reflect "reflect"
+ sync "sync"
+)
+
+const (
+ // Verify that this generated code is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+ // Verify that runtime/protoimpl is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+type Bp2BuildMetrics struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ // Total number of Soong modules converted to generated targets
+ GeneratedModuleCount uint64 `protobuf:"varint,1,opt,name=generatedModuleCount,proto3" json:"generatedModuleCount,omitempty"`
+ // Total number of Soong modules converted to handcrafted targets
+ HandCraftedModuleCount uint64 `protobuf:"varint,2,opt,name=handCraftedModuleCount,proto3" json:"handCraftedModuleCount,omitempty"`
+ // Total number of unconverted Soong modules
+ UnconvertedModuleCount uint64 `protobuf:"varint,3,opt,name=unconvertedModuleCount,proto3" json:"unconvertedModuleCount,omitempty"`
+ // Counts of generated Bazel targets per Bazel rule class
+ RuleClassCount map[string]uint64 `protobuf:"bytes,4,rep,name=ruleClassCount,proto3" json:"ruleClassCount,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"varint,2,opt,name=value,proto3"`
+ // List of converted modules
+ ConvertedModules []string `protobuf:"bytes,5,rep,name=convertedModules,proto3" json:"convertedModules,omitempty"`
+}
+
+func (x *Bp2BuildMetrics) Reset() {
+ *x = Bp2BuildMetrics{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_bp2build_metrics_proto_msgTypes[0]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *Bp2BuildMetrics) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Bp2BuildMetrics) ProtoMessage() {}
+
+func (x *Bp2BuildMetrics) ProtoReflect() protoreflect.Message {
+ mi := &file_bp2build_metrics_proto_msgTypes[0]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use Bp2BuildMetrics.ProtoReflect.Descriptor instead.
+func (*Bp2BuildMetrics) Descriptor() ([]byte, []int) {
+ return file_bp2build_metrics_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *Bp2BuildMetrics) GetGeneratedModuleCount() uint64 {
+ if x != nil {
+ return x.GeneratedModuleCount
+ }
+ return 0
+}
+
+func (x *Bp2BuildMetrics) GetHandCraftedModuleCount() uint64 {
+ if x != nil {
+ return x.HandCraftedModuleCount
+ }
+ return 0
+}
+
+func (x *Bp2BuildMetrics) GetUnconvertedModuleCount() uint64 {
+ if x != nil {
+ return x.UnconvertedModuleCount
+ }
+ return 0
+}
+
+func (x *Bp2BuildMetrics) GetRuleClassCount() map[string]uint64 {
+ if x != nil {
+ return x.RuleClassCount
+ }
+ return nil
+}
+
+func (x *Bp2BuildMetrics) GetConvertedModules() []string {
+ if x != nil {
+ return x.ConvertedModules
+ }
+ return nil
+}
+
+var File_bp2build_metrics_proto protoreflect.FileDescriptor
+
+var file_bp2build_metrics_proto_rawDesc = []byte{
+ 0x0a, 0x16, 0x62, 0x70, 0x32, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69,
+ 0x63, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1c, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f,
+ 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x62, 0x70, 0x32, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d,
+ 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x22, 0x8f, 0x03, 0x0a, 0x0f, 0x42, 0x70, 0x32, 0x42, 0x75,
+ 0x69, 0x6c, 0x64, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x32, 0x0a, 0x14, 0x67, 0x65,
+ 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6f, 0x75,
+ 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x14, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61,
+ 0x74, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x36,
+ 0x0a, 0x16, 0x68, 0x61, 0x6e, 0x64, 0x43, 0x72, 0x61, 0x66, 0x74, 0x65, 0x64, 0x4d, 0x6f, 0x64,
+ 0x75, 0x6c, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x16,
+ 0x68, 0x61, 0x6e, 0x64, 0x43, 0x72, 0x61, 0x66, 0x74, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x75, 0x6c,
+ 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x36, 0x0a, 0x16, 0x75, 0x6e, 0x63, 0x6f, 0x6e, 0x76,
+ 0x65, 0x72, 0x74, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74,
+ 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x16, 0x75, 0x6e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72,
+ 0x74, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x69,
+ 0x0a, 0x0e, 0x72, 0x75, 0x6c, 0x65, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74,
+ 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x41, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62,
+ 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x62, 0x70, 0x32, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65,
+ 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x42, 0x70, 0x32, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x4d, 0x65,
+ 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x52, 0x75, 0x6c, 0x65, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x43,
+ 0x6f, 0x75, 0x6e, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0e, 0x72, 0x75, 0x6c, 0x65, 0x43,
+ 0x6c, 0x61, 0x73, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2a, 0x0a, 0x10, 0x63, 0x6f, 0x6e,
+ 0x76, 0x65, 0x72, 0x74, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x05, 0x20,
+ 0x03, 0x28, 0x09, 0x52, 0x10, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x65, 0x64, 0x4d, 0x6f,
+ 0x64, 0x75, 0x6c, 0x65, 0x73, 0x1a, 0x41, 0x0a, 0x13, 0x52, 0x75, 0x6c, 0x65, 0x43, 0x6c, 0x61,
+ 0x73, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03,
+ 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14,
+ 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x76,
+ 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x31, 0x5a, 0x2f, 0x61, 0x6e, 0x64, 0x72,
+ 0x6f, 0x69, 0x64, 0x2f, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x2f, 0x75, 0x69, 0x2f, 0x6d, 0x65, 0x74,
+ 0x72, 0x69, 0x63, 0x73, 0x2f, 0x62, 0x70, 0x32, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65,
+ 0x74, 0x72, 0x69, 0x63, 0x73, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f,
+ 0x74, 0x6f, 0x33,
+}
+
+var (
+ file_bp2build_metrics_proto_rawDescOnce sync.Once
+ file_bp2build_metrics_proto_rawDescData = file_bp2build_metrics_proto_rawDesc
+)
+
+func file_bp2build_metrics_proto_rawDescGZIP() []byte {
+ file_bp2build_metrics_proto_rawDescOnce.Do(func() {
+ file_bp2build_metrics_proto_rawDescData = protoimpl.X.CompressGZIP(file_bp2build_metrics_proto_rawDescData)
+ })
+ return file_bp2build_metrics_proto_rawDescData
+}
+
+var file_bp2build_metrics_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
+var file_bp2build_metrics_proto_goTypes = []interface{}{
+ (*Bp2BuildMetrics)(nil), // 0: soong_build_bp2build_metrics.Bp2BuildMetrics
+ nil, // 1: soong_build_bp2build_metrics.Bp2BuildMetrics.RuleClassCountEntry
+}
+var file_bp2build_metrics_proto_depIdxs = []int32{
+ 1, // 0: soong_build_bp2build_metrics.Bp2BuildMetrics.ruleClassCount:type_name -> soong_build_bp2build_metrics.Bp2BuildMetrics.RuleClassCountEntry
+ 1, // [1:1] is the sub-list for method output_type
+ 1, // [1:1] is the sub-list for method input_type
+ 1, // [1:1] is the sub-list for extension type_name
+ 1, // [1:1] is the sub-list for extension extendee
+ 0, // [0:1] is the sub-list for field type_name
+}
+
+func init() { file_bp2build_metrics_proto_init() }
+func file_bp2build_metrics_proto_init() {
+ if File_bp2build_metrics_proto != nil {
+ return
+ }
+ if !protoimpl.UnsafeEnabled {
+ file_bp2build_metrics_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*Bp2BuildMetrics); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ }
+ type x struct{}
+ out := protoimpl.TypeBuilder{
+ File: protoimpl.DescBuilder{
+ GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+ RawDescriptor: file_bp2build_metrics_proto_rawDesc,
+ NumEnums: 0,
+ NumMessages: 2,
+ NumExtensions: 0,
+ NumServices: 0,
+ },
+ GoTypes: file_bp2build_metrics_proto_goTypes,
+ DependencyIndexes: file_bp2build_metrics_proto_depIdxs,
+ MessageInfos: file_bp2build_metrics_proto_msgTypes,
+ }.Build()
+ File_bp2build_metrics_proto = out.File
+ file_bp2build_metrics_proto_rawDesc = nil
+ file_bp2build_metrics_proto_goTypes = nil
+ file_bp2build_metrics_proto_depIdxs = nil
+}
diff --git a/ui/metrics/bp2build_metrics_proto/bp2build_metrics.proto b/ui/metrics/bp2build_metrics_proto/bp2build_metrics.proto
new file mode 100644
index 0000000..5e88966
--- /dev/null
+++ b/ui/metrics/bp2build_metrics_proto/bp2build_metrics.proto
@@ -0,0 +1,35 @@
+// Copyright 2021 Google Inc. All Rights Reserved.
+//
+// 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 soong_build_bp2build_metrics;
+option go_package = "android/soong/ui/metrics/bp2build_metrics_proto";
+
+message Bp2BuildMetrics {
+ // Total number of Soong modules converted to generated targets
+ uint64 generatedModuleCount = 1;
+
+ // Total number of Soong modules converted to handcrafted targets
+ uint64 handCraftedModuleCount = 2;
+
+ // Total number of unconverted Soong modules
+ uint64 unconvertedModuleCount = 3;
+
+ // Counts of generated Bazel targets per Bazel rule class
+ map<string, uint64> ruleClassCount = 4;
+
+ // List of converted modules
+ repeated string convertedModules = 5;
+}
diff --git a/ui/metrics/bp2build_metrics_proto/regen.sh b/ui/metrics/bp2build_metrics_proto/regen.sh
new file mode 100755
index 0000000..bfe4294
--- /dev/null
+++ b/ui/metrics/bp2build_metrics_proto/regen.sh
@@ -0,0 +1,29 @@
+#!/bin/bash -e
+
+# Copyright 2021 Google Inc. All Rights Reserved.
+#
+# 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.
+
+# Generates the golang source file of bp2build_metrics.proto protobuf file.
+
+function die() { echo "ERROR: $1" >&2; exit 1; }
+
+readonly error_msg="Maybe you need to run 'lunch aosp_arm-eng && m aprotoc blueprint_tools'?"
+
+if ! hash aprotoc &>/dev/null; then
+ die "could not find aprotoc. ${error_msg}"
+fi
+
+if ! aprotoc --go_out=paths=source_relative:. bp2build_metrics.proto; then
+ die "build failed. ${error_msg}"
+fi