export Java variables to Bazel

Test: build/bazel/bp2build.sh
Change-Id: Ia06f9265c9f96e6add6edbd0cee925fe37b430d3
This commit is contained in:
Sam Delmerico 2022-03-25 16:33:26 +00:00
parent 7f88956c16
commit 932c01cf9e
9 changed files with 262 additions and 38 deletions

View file

@ -101,6 +101,17 @@ func (ev ExportedVariables) ExportSourcePathVariable(name string, value string)
ev.exportedStringVars.set(name, value)
}
// ExportVariableFuncVariable declares a variable whose value is evaluated at
// runtime via a function and exports it to Bazel's toolchain.
func (ev ExportedVariables) ExportVariableFuncVariable(name string, f func() string) {
ev.exportedConfigDependingVars.set(name, func(config Config) string {
return f()
})
ev.pctx.VariableFunc(name, func(PackageVarContext) string {
return f()
})
}
// ExportString only exports a variable to Bazel, but does not declare it in Soong
func (ev ExportedVariables) ExportString(name string, value string) {
ev.exportedStringVars.set(name, value)
@ -403,7 +414,8 @@ func expandVar(config Config, toExpand string, stringScope ExportedStringVariabl
return ret, nil
}
var ret []string
for _, v := range strings.Split(toExpand, " ") {
stringFields := splitStringKeepingQuotedSubstring(toExpand, ' ')
for _, v := range stringFields {
val, err := expandVarInternal(v, map[string]bool{})
if err != nil {
return ret, err
@ -414,6 +426,46 @@ func expandVar(config Config, toExpand string, stringScope ExportedStringVariabl
return ret, nil
}
// splitStringKeepingQuotedSubstring splits a string on a provided separator,
// but it will not split substrings inside unescaped double quotes. If the double
// quotes are escaped, then the returned string will only include the quote, and
// not the escape.
func splitStringKeepingQuotedSubstring(s string, delimiter byte) []string {
var ret []string
quote := byte('"')
var substring []byte
quoted := false
escaped := false
for i := range s {
if !quoted && s[i] == delimiter {
ret = append(ret, string(substring))
substring = []byte{}
continue
}
characterIsEscape := i < len(s)-1 && s[i] == '\\' && s[i+1] == quote
if characterIsEscape {
escaped = true
continue
}
if s[i] == quote {
if !escaped {
quoted = !quoted
}
escaped = false
}
substring = append(substring, s[i])
}
ret = append(ret, string(substring))
return ret
}
func validateVariableMethod(name string, methodValue reflect.Value) {
methodType := methodValue.Type()
if methodType.Kind() != reflect.Func {

View file

@ -321,3 +321,134 @@ constants = struct(
})
}
}
func TestSplitStringKeepingQuotedSubstring(t *testing.T) {
testCases := []struct {
description string
s string
delimiter byte
split []string
}{
{
description: "empty string returns single empty string",
s: "",
delimiter: ' ',
split: []string{
"",
},
},
{
description: "string with single space returns two empty strings",
s: " ",
delimiter: ' ',
split: []string{
"",
"",
},
},
{
description: "string with two spaces returns three empty strings",
s: " ",
delimiter: ' ',
split: []string{
"",
"",
"",
},
},
{
description: "string with four words returns four word string",
s: "hello world with words",
delimiter: ' ',
split: []string{
"hello",
"world",
"with",
"words",
},
},
{
description: "string with words and nested quote returns word strings and quote string",
s: `hello "world with" words`,
delimiter: ' ',
split: []string{
"hello",
`"world with"`,
"words",
},
},
{
description: "string with escaped quote inside real quotes",
s: `hello \"world "with\" words"`,
delimiter: ' ',
split: []string{
"hello",
`"world`,
`"with" words"`,
},
},
{
description: "string with words and escaped quotes returns word strings",
s: `hello \"world with\" words`,
delimiter: ' ',
split: []string{
"hello",
`"world`,
`with"`,
"words",
},
},
{
description: "string which is single quoted substring returns only substring",
s: `"hello world with words"`,
delimiter: ' ',
split: []string{
`"hello world with words"`,
},
},
{
description: "string starting with quote returns quoted string",
s: `"hello world with" words`,
delimiter: ' ',
split: []string{
`"hello world with"`,
"words",
},
},
{
description: "string with starting quote and no ending quote returns quote to end of string",
s: `hello "world with words`,
delimiter: ' ',
split: []string{
"hello",
`"world with words`,
},
},
{
description: "quoted string is treated as a single \"word\" unless separated by delimiter",
s: `hello "world"with words`,
delimiter: ' ',
split: []string{
"hello",
`"world"with`,
"words",
},
},
}
for _, tc := range testCases {
t.Run(tc.description, func(t *testing.T) {
split := splitStringKeepingQuotedSubstring(tc.s, tc.delimiter)
if len(split) != len(tc.split) {
t.Fatalf("number of split string elements (%d) differs from expected (%d): split string (%v), expected (%v)",
len(split), len(tc.split), split, tc.split,
)
}
for i := range split {
if split[i] != tc.split[i] {
t.Errorf("split string element (%d), %v, differs from expected, %v", i, split[i], tc.split[i])
}
}
})
}
}

View file

@ -580,7 +580,9 @@ func prettyPrint(propertyValue reflect.Value, indent int, emitZeroValues bool) (
elements = append(elements, val)
}
}
return starlark_fmt.PrintList(elements, indent, "%s"), nil
return starlark_fmt.PrintList(elements, indent, func(s string) string {
return "%s"
}), nil
case reflect.Struct:
// Special cases where the bp2build sends additional information to the codegenerator

View file

@ -7,7 +7,8 @@ import (
"strings"
"android/soong/android"
"android/soong/cc/config"
cc_config "android/soong/cc/config"
java_config "android/soong/java/config"
"github.com/google/blueprint/proptools"
)
@ -22,7 +23,10 @@ func CreateSoongInjectionFiles(cfg android.Config, metrics CodegenMetrics) []Baz
var files []BazelFile
files = append(files, newFile("cc_toolchain", GeneratedBuildFileName, "")) // Creates a //cc_toolchain package.
files = append(files, newFile("cc_toolchain", "constants.bzl", config.BazelCcToolchainVars(cfg)))
files = append(files, newFile("cc_toolchain", "constants.bzl", cc_config.BazelCcToolchainVars(cfg)))
files = append(files, newFile("java_toolchain", GeneratedBuildFileName, "")) // Creates a //java_toolchain package.
files = append(files, newFile("java_toolchain", "constants.bzl", java_config.BazelJavaToolchainVars(cfg)))
files = append(files, newFile("metrics", "converted_modules.txt", strings.Join(metrics.convertedModules, "\n")))

View file

@ -94,6 +94,14 @@ func TestCreateBazelFiles_Bp2Build_CreatesDefaultFiles(t *testing.T) {
dir: "cc_toolchain",
basename: "constants.bzl",
},
{
dir: "java_toolchain",
basename: GeneratedBuildFileName,
},
{
dir: "java_toolchain",
basename: "constants.bzl",
},
{
dir: "metrics",
basename: "converted_modules.txt",

View file

@ -26,7 +26,8 @@ import (
)
var (
pctx = android.NewPackageContext("android/soong/java/config")
pctx = android.NewPackageContext("android/soong/java/config")
exportedVars = android.NewExportedVariables(pctx)
LegacyCorePlatformBootclasspathLibraries = []string{"legacy.core.platform.api.stubs", "core-lambda-stubs"}
LegacyCorePlatformSystemModules = "legacy-core-platform-api-stubs-system-modules"
@ -53,25 +54,40 @@ var (
}
)
const (
JavaVmFlags = `-XX:OnError="cat hs_err_pid%p.log" -XX:CICompilerCount=6 -XX:+UseDynamicNumberOfGCThreads`
JavacVmFlags = `-J-XX:OnError="cat hs_err_pid%p.log" -J-XX:CICompilerCount=6 -J-XX:+UseDynamicNumberOfGCThreads -J-XX:+TieredCompilation -J-XX:TieredStopAtLevel=1`
var (
JavacVmFlags = strings.Join(javacVmFlagsList, " ")
javaVmFlagsList = []string{
`-XX:OnError="cat hs_err_pid%p.log"`,
"-XX:CICompilerCount=6",
"-XX:+UseDynamicNumberOfGCThreads",
}
javacVmFlagsList = []string{
`-J-XX:OnError="cat hs_err_pid%p.log"`,
"-J-XX:CICompilerCount=6",
"-J-XX:+UseDynamicNumberOfGCThreads",
"-J-XX:+TieredCompilation",
"-J-XX:TieredStopAtLevel=1",
}
)
func init() {
pctx.Import("github.com/google/blueprint/bootstrap")
pctx.StaticVariable("JavacHeapSize", "2048M")
pctx.StaticVariable("JavacHeapFlags", "-J-Xmx${JavacHeapSize}")
exportedVars.ExportStringStaticVariable("JavacHeapSize", "2048M")
exportedVars.ExportStringStaticVariable("JavacHeapFlags", "-J-Xmx${JavacHeapSize}")
// ErrorProne can use significantly more memory than javac alone, give it a higher heap
// size (b/221480398).
pctx.StaticVariable("ErrorProneHeapSize", "4096M")
pctx.StaticVariable("ErrorProneHeapFlags", "-J-Xmx${ErrorProneHeapSize}")
exportedVars.ExportStringStaticVariable("ErrorProneHeapSize", "4096M")
exportedVars.ExportStringStaticVariable("ErrorProneHeapFlags", "-J-Xmx${ErrorProneHeapSize}")
pctx.StaticVariable("DexFlags", "-JXX:OnError='cat hs_err_pid%p.log' -JXX:CICompilerCount=6 -JXX:+UseDynamicNumberOfGCThreads")
exportedVars.ExportStringListStaticVariable("DexFlags", []string{
`-JXX:OnError="cat hs_err_pid%p.log"`,
"-JXX:CICompilerCount=6",
"-JXX:+UseDynamicNumberOfGCThreads",
})
pctx.StaticVariable("CommonJdkFlags", strings.Join([]string{
exportedVars.ExportStringListStaticVariable("CommonJdkFlags", []string{
`-Xmaxerrs 9999999`,
`-encoding UTF-8`,
`-sourcepath ""`,
@ -85,10 +101,10 @@ func init() {
// b/65004097: prevent using java.lang.invoke.StringConcatFactory when using -target 1.9
`-XDstringConcat=inline`,
}, " "))
})
pctx.StaticVariable("JavaVmFlags", JavaVmFlags)
pctx.StaticVariable("JavacVmFlags", JavacVmFlags)
exportedVars.ExportStringListStaticVariable("JavaVmFlags", javaVmFlagsList)
exportedVars.ExportStringListStaticVariable("JavacVmFlags", javacVmFlagsList)
pctx.VariableConfigMethod("hostPrebuiltTag", android.Config.PrebuiltOS)
@ -184,6 +200,10 @@ func init() {
hostJNIToolVariableWithSdkToolsPrebuilt("SignapkJniLibrary", "libconscrypt_openjdk_jni")
}
func BazelJavaToolchainVars(config android.Config) string {
return android.BazelToolchainVars(config, exportedVars)
}
func hostBinToolVariableWithSdkToolsPrebuilt(name, tool string) {
pctx.VariableFunc(name, func(ctx android.PackageVarContext) string {
if ctx.Config().AlwaysUsePrebuiltSdks() {

View file

@ -16,8 +16,6 @@ package config
import (
"strings"
"android/soong/android"
)
var (
@ -31,23 +29,23 @@ var (
)
// Wrapper that grabs value of val late so it can be initialized by a later module's init function
func errorProneVar(name string, val *[]string, sep string) {
pctx.VariableFunc(name, func(android.PackageVarContext) string {
func errorProneVar(val *[]string, sep string) func() string {
return func() string {
return strings.Join(*val, sep)
})
}
}
func init() {
errorProneVar("ErrorProneClasspath", &ErrorProneClasspath, ":")
errorProneVar("ErrorProneChecksError", &ErrorProneChecksError, " ")
errorProneVar("ErrorProneChecksWarning", &ErrorProneChecksWarning, " ")
errorProneVar("ErrorProneChecksDefaultDisabled", &ErrorProneChecksDefaultDisabled, " ")
errorProneVar("ErrorProneChecksOff", &ErrorProneChecksOff, " ")
errorProneVar("ErrorProneFlags", &ErrorProneFlags, " ")
pctx.StaticVariable("ErrorProneChecks", strings.Join([]string{
exportedVars.ExportVariableFuncVariable("ErrorProneClasspath", errorProneVar(&ErrorProneClasspath, ":"))
exportedVars.ExportVariableFuncVariable("ErrorProneChecksError", errorProneVar(&ErrorProneChecksError, " "))
exportedVars.ExportVariableFuncVariable("ErrorProneChecksWarning", errorProneVar(&ErrorProneChecksWarning, " "))
exportedVars.ExportVariableFuncVariable("ErrorProneChecksDefaultDisabled", errorProneVar(&ErrorProneChecksDefaultDisabled, " "))
exportedVars.ExportVariableFuncVariable("ErrorProneChecksOff", errorProneVar(&ErrorProneChecksOff, " "))
exportedVars.ExportVariableFuncVariable("ErrorProneFlags", errorProneVar(&ErrorProneFlags, " "))
exportedVars.ExportStringListStaticVariable("ErrorProneChecks", []string{
"${ErrorProneChecksOff}",
"${ErrorProneChecksError}",
"${ErrorProneChecksWarning}",
"${ErrorProneChecksDefaultDisabled}",
}, " "))
})
}

View file

@ -39,21 +39,26 @@ func PrintBool(item bool) string {
// PrintsStringList returns a Starlark-compatible string of a list of Strings/Labels.
func PrintStringList(items []string, indentLevel int) string {
return PrintList(items, indentLevel, `"%s"`)
return PrintList(items, indentLevel, func(s string) string {
if strings.Contains(s, "\"") {
return `'''%s'''`
}
return `"%s"`
})
}
// PrintList returns a Starlark-compatible string of list formmated as requested.
func PrintList(items []string, indentLevel int, formatString string) string {
func PrintList(items []string, indentLevel int, formatString func(string) string) string {
if len(items) == 0 {
return "[]"
} else if len(items) == 1 {
return fmt.Sprintf("["+formatString+"]", items[0])
return fmt.Sprintf("["+formatString(items[0])+"]", items[0])
}
list := make([]string, 0, len(items)+2)
list = append(list, "[")
innerIndent := Indention(indentLevel + 1)
for _, item := range items {
list = append(list, fmt.Sprintf(`%s`+formatString+`,`, innerIndent, item))
list = append(list, fmt.Sprintf(`%s`+formatString(item)+`,`, innerIndent, item))
}
list = append(list, Indention(indentLevel)+"]")
return strings.Join(list, "\n")

View file

@ -18,6 +18,10 @@ import (
"testing"
)
func simpleFormat(s string) string {
return "%s"
}
func TestPrintEmptyStringList(t *testing.T) {
in := []string{}
indentLevel := 0
@ -54,7 +58,7 @@ func TestPrintMultiElementStringList(t *testing.T) {
func TestPrintEmptyList(t *testing.T) {
in := []string{}
indentLevel := 0
out := PrintList(in, indentLevel, "%s")
out := PrintList(in, indentLevel, simpleFormat)
expectedOut := "[]"
if out != expectedOut {
t.Errorf("Expected %q, got %q", expectedOut, out)
@ -64,7 +68,7 @@ func TestPrintEmptyList(t *testing.T) {
func TestPrintSingleElementList(t *testing.T) {
in := []string{"1"}
indentLevel := 0
out := PrintList(in, indentLevel, "%s")
out := PrintList(in, indentLevel, simpleFormat)
expectedOut := `[1]`
if out != expectedOut {
t.Errorf("Expected %q, got %q", expectedOut, out)
@ -74,7 +78,7 @@ func TestPrintSingleElementList(t *testing.T) {
func TestPrintMultiElementList(t *testing.T) {
in := []string{"1", "2"}
indentLevel := 0
out := PrintList(in, indentLevel, "%s")
out := PrintList(in, indentLevel, simpleFormat)
expectedOut := `[
1,
2,
@ -87,7 +91,7 @@ func TestPrintMultiElementList(t *testing.T) {
func TestListWithNonZeroIndent(t *testing.T) {
in := []string{"1", "2"}
indentLevel := 1
out := PrintList(in, indentLevel, "%s")
out := PrintList(in, indentLevel, simpleFormat)
expectedOut := `[
1,
2,