Merge changes Ieb4ffa10,I9458cbc4
* changes:
soong_ui: Do not clean the OutDir path multiple times.
soong_ui: Construct the proper MODULES-IN-<dir name> for m* build commands.
diff --git a/Android.bp b/Android.bp
index 1b68adb..4893de6 100644
--- a/Android.bp
+++ b/Android.bp
@@ -81,6 +81,7 @@
],
testSrcs: [
"android/android_test.go",
+ "android/androidmk_test.go",
"android/arch_test.go",
"android/config_test.go",
"android/expand_test.go",
@@ -291,6 +292,7 @@
"java/testing.go",
],
testSrcs: [
+ "java/androidmk_test.go",
"java/app_test.go",
"java/device_host_converter_test.go",
"java/dexpreopt_test.go",
diff --git a/android/androidmk.go b/android/androidmk.go
index 9bc2692..1f1bd70 100644
--- a/android/androidmk.go
+++ b/android/androidmk.go
@@ -191,15 +191,7 @@
// Make cannot identify LOCAL_MODULE_TARGET_ARCH:= common.
if archStr != "common" {
if amod.Target().NativeBridge {
- // TODO: Unhardcode these rules.
- guestArchStr := archStr
- hostArchStr := ""
- if guestArchStr == "arm" {
- hostArchStr = "x86"
- } else if guestArchStr == "arm64" {
- hostArchStr = "x86_64"
- }
-
+ hostArchStr := amod.Target().NativeBridgeHostArchName
if hostArchStr != "" {
a.SetString("LOCAL_MODULE_TARGET_ARCH", hostArchStr)
}
@@ -391,6 +383,31 @@
return nil
}
+func (data *AndroidMkData) fillInData(config Config, bpPath string, mod blueprint.Module) {
+ // Get the preamble content through AndroidMkEntries logic.
+ entries := AndroidMkEntries{
+ Class: data.Class,
+ SubName: data.SubName,
+ DistFile: data.DistFile,
+ OutputFile: data.OutputFile,
+ Disabled: data.Disabled,
+ Include: data.Include,
+ Required: data.Required,
+ Host_required: data.Host_required,
+ Target_required: data.Target_required,
+ }
+ entries.fillInEntries(config, bpPath, mod)
+
+ // preamble doesn't need the footer content.
+ entries.footer = bytes.Buffer{}
+ entries.write(&data.preamble)
+
+ // copy entries back to data since it is used in Custom
+ data.Required = entries.Required
+ data.Host_required = entries.Host_required
+ data.Target_required = entries.Target_required
+}
+
func translateAndroidModule(ctx SingletonContext, w io.Writer, mod blueprint.Module,
provider AndroidMkDataProvider) error {
@@ -404,22 +421,7 @@
data.Include = "$(BUILD_PREBUILT)"
}
- // Get the preamble content through AndroidMkEntries logic.
- entries := AndroidMkEntries{
- Class: data.Class,
- SubName: data.SubName,
- DistFile: data.DistFile,
- OutputFile: data.OutputFile,
- Disabled: data.Disabled,
- Include: data.Include,
- Required: data.Required,
- Host_required: data.Host_required,
- Target_required: data.Target_required,
- }
- entries.fillInEntries(ctx.Config(), ctx.BlueprintFile(mod), mod)
- // preamble doesn't need the footer content.
- entries.footer = bytes.Buffer{}
- entries.write(&data.preamble)
+ data.fillInData(ctx.Config(), ctx.BlueprintFile(mod), mod)
prefix := ""
if amod.ArchSpecific() {
diff --git a/android/androidmk_test.go b/android/androidmk_test.go
new file mode 100644
index 0000000..0bb455b
--- /dev/null
+++ b/android/androidmk_test.go
@@ -0,0 +1,82 @@
+// Copyright 2019 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.
+
+package android
+
+import (
+ "io"
+ "reflect"
+ "testing"
+)
+
+type customModule struct {
+ ModuleBase
+ data AndroidMkData
+}
+
+func (m *customModule) GenerateAndroidBuildActions(ctx ModuleContext) {
+}
+
+func (m *customModule) AndroidMk() AndroidMkData {
+ return AndroidMkData{
+ Custom: func(w io.Writer, name, prefix, moduleDir string, data AndroidMkData) {
+ m.data = data
+ },
+ }
+}
+
+func customModuleFactory() Module {
+ module := &customModule{}
+ InitAndroidModule(module)
+ return module
+}
+
+func TestAndroidMkSingleton_PassesUpdatedAndroidMkDataToCustomCallback(t *testing.T) {
+ config := TestConfig(buildDir, nil)
+ config.inMake = true // Enable androidmk Singleton
+
+ ctx := NewTestContext()
+ ctx.RegisterSingletonType("androidmk", SingletonFactoryAdaptor(AndroidMkSingleton))
+ ctx.RegisterModuleType("custom", ModuleFactoryAdaptor(customModuleFactory))
+ ctx.Register()
+
+ bp := `
+ custom {
+ name: "foo",
+ required: ["bar"],
+ host_required: ["baz"],
+ target_required: ["qux"],
+ }
+ `
+
+ ctx.MockFileSystem(map[string][]byte{
+ "Android.bp": []byte(bp),
+ })
+
+ _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
+ FailIfErrored(t, errs)
+ _, errs = ctx.PrepareBuildActions(config)
+ FailIfErrored(t, errs)
+
+ m := ctx.ModuleForTests("foo", "").Module().(*customModule)
+
+ assertEqual := func(expected interface{}, actual interface{}) {
+ if !reflect.DeepEqual(expected, actual) {
+ t.Errorf("%q expected, but got %q", expected, actual)
+ }
+ }
+ assertEqual([]string{"bar"}, m.data.Required)
+ assertEqual([]string{"baz"}, m.data.Host_required)
+ assertEqual([]string{"qux"}, m.data.Target_required)
+}
diff --git a/android/arch.go b/android/arch.go
index 46e582c..44c3f48 100644
--- a/android/arch.go
+++ b/android/arch.go
@@ -691,9 +691,11 @@
)
type Target struct {
- Os OsType
- Arch Arch
- NativeBridge NativeBridgeSupport
+ Os OsType
+ Arch Arch
+ NativeBridge NativeBridgeSupport
+ NativeBridgeHostArchName string
+ NativeBridgeRelativePath string
}
func (target Target) String() string {
@@ -1403,7 +1405,8 @@
var targetErr error
addTarget := func(os OsType, archName string, archVariant, cpuVariant *string, abi []string,
- nativeBridgeEnabled NativeBridgeSupport) {
+ nativeBridgeEnabled NativeBridgeSupport, nativeBridgeHostArchName *string,
+ nativeBridgeRelativePath *string) {
if targetErr != nil {
return
}
@@ -1413,12 +1416,21 @@
targetErr = err
return
}
+ nativeBridgeRelativePathStr := String(nativeBridgeRelativePath)
+ nativeBridgeHostArchNameStr := String(nativeBridgeHostArchName)
+
+ // Use guest arch as relative install path by default
+ if nativeBridgeEnabled && nativeBridgeRelativePathStr == "" {
+ nativeBridgeRelativePathStr = arch.ArchType.String()
+ }
targets[os] = append(targets[os],
Target{
- Os: os,
- Arch: arch,
- NativeBridge: nativeBridgeEnabled,
+ Os: os,
+ Arch: arch,
+ NativeBridge: nativeBridgeEnabled,
+ NativeBridgeHostArchName: nativeBridgeHostArchNameStr,
+ NativeBridgeRelativePath: nativeBridgeRelativePathStr,
})
}
@@ -1426,14 +1438,14 @@
return nil, fmt.Errorf("No host primary architecture set")
}
- addTarget(BuildOs, *variables.HostArch, nil, nil, nil, NativeBridgeDisabled)
+ addTarget(BuildOs, *variables.HostArch, nil, nil, nil, NativeBridgeDisabled, nil, nil)
if variables.HostSecondaryArch != nil && *variables.HostSecondaryArch != "" {
- addTarget(BuildOs, *variables.HostSecondaryArch, nil, nil, nil, NativeBridgeDisabled)
+ addTarget(BuildOs, *variables.HostSecondaryArch, nil, nil, nil, NativeBridgeDisabled, nil, nil)
}
if Bool(config.Host_bionic) {
- addTarget(LinuxBionic, "x86_64", nil, nil, nil, NativeBridgeDisabled)
+ addTarget(LinuxBionic, "x86_64", nil, nil, nil, NativeBridgeDisabled, nil, nil)
}
if String(variables.CrossHost) != "" {
@@ -1446,10 +1458,10 @@
return nil, fmt.Errorf("No cross-host primary architecture set")
}
- addTarget(crossHostOs, *variables.CrossHostArch, nil, nil, nil, NativeBridgeDisabled)
+ addTarget(crossHostOs, *variables.CrossHostArch, nil, nil, nil, NativeBridgeDisabled, nil, nil)
if variables.CrossHostSecondaryArch != nil && *variables.CrossHostSecondaryArch != "" {
- addTarget(crossHostOs, *variables.CrossHostSecondaryArch, nil, nil, nil, NativeBridgeDisabled)
+ addTarget(crossHostOs, *variables.CrossHostSecondaryArch, nil, nil, nil, NativeBridgeDisabled, nil, nil)
}
}
@@ -1460,12 +1472,12 @@
}
addTarget(target, *variables.DeviceArch, variables.DeviceArchVariant,
- variables.DeviceCpuVariant, variables.DeviceAbi, NativeBridgeDisabled)
+ variables.DeviceCpuVariant, variables.DeviceAbi, NativeBridgeDisabled, nil, nil)
if variables.DeviceSecondaryArch != nil && *variables.DeviceSecondaryArch != "" {
addTarget(Android, *variables.DeviceSecondaryArch,
variables.DeviceSecondaryArchVariant, variables.DeviceSecondaryCpuVariant,
- variables.DeviceSecondaryAbi, NativeBridgeDisabled)
+ variables.DeviceSecondaryAbi, NativeBridgeDisabled, nil, nil)
deviceArches := targets[Android]
if deviceArches[0].Arch.ArchType.Multilib == deviceArches[1].Arch.ArchType.Multilib {
@@ -1476,7 +1488,8 @@
if variables.NativeBridgeArch != nil && *variables.NativeBridgeArch != "" {
addTarget(Android, *variables.NativeBridgeArch,
variables.NativeBridgeArchVariant, variables.NativeBridgeCpuVariant,
- variables.NativeBridgeAbi, NativeBridgeEnabled)
+ variables.NativeBridgeAbi, NativeBridgeEnabled, variables.DeviceArch,
+ variables.NativeBridgeRelativePath)
}
if variables.DeviceSecondaryArch != nil && *variables.DeviceSecondaryArch != "" &&
@@ -1484,7 +1497,10 @@
addTarget(Android, *variables.NativeBridgeSecondaryArch,
variables.NativeBridgeSecondaryArchVariant,
variables.NativeBridgeSecondaryCpuVariant,
- variables.NativeBridgeSecondaryAbi, NativeBridgeEnabled)
+ variables.NativeBridgeSecondaryAbi,
+ NativeBridgeEnabled,
+ variables.DeviceSecondaryArch,
+ variables.NativeBridgeSecondaryRelativePath)
}
}
diff --git a/android/config.go b/android/config.go
index c0ed50f..8ced93a 100644
--- a/android/config.go
+++ b/android/config.go
@@ -240,10 +240,10 @@
config := testConfig.config
config.Targets[Android] = []Target{
- {Android, Arch{ArchType: X86_64, ArchVariant: "silvermont", Native: true, Abi: []string{"arm64-v8a"}}, NativeBridgeDisabled},
- {Android, Arch{ArchType: X86, ArchVariant: "silvermont", Native: true, Abi: []string{"armeabi-v7a"}}, NativeBridgeDisabled},
- {Android, Arch{ArchType: Arm64, ArchVariant: "armv8-a", Native: true, Abi: []string{"arm64-v8a"}}, NativeBridgeEnabled},
- {Android, Arch{ArchType: Arm, ArchVariant: "armv7-a-neon", Native: true, Abi: []string{"armeabi-v7a"}}, NativeBridgeEnabled},
+ {Android, Arch{ArchType: X86_64, ArchVariant: "silvermont", Native: true, Abi: []string{"arm64-v8a"}}, NativeBridgeDisabled, "", ""},
+ {Android, Arch{ArchType: X86, ArchVariant: "silvermont", Native: true, Abi: []string{"armeabi-v7a"}}, NativeBridgeDisabled, "", ""},
+ {Android, Arch{ArchType: Arm64, ArchVariant: "armv8-a", Native: true, Abi: []string{"arm64-v8a"}}, NativeBridgeEnabled, "x86_64", "arm64"},
+ {Android, Arch{ArchType: Arm, ArchVariant: "armv7-a-neon", Native: true, Abi: []string{"armeabi-v7a"}}, NativeBridgeEnabled, "x86", "arm"},
}
return testConfig
@@ -255,10 +255,10 @@
config.Targets = map[OsType][]Target{
Fuchsia: []Target{
- {Fuchsia, Arch{ArchType: Arm64, ArchVariant: "", Native: true}, NativeBridgeDisabled},
+ {Fuchsia, Arch{ArchType: Arm64, ArchVariant: "", Native: true}, NativeBridgeDisabled, "", ""},
},
BuildOs: []Target{
- {BuildOs, Arch{ArchType: X86_64}, NativeBridgeDisabled},
+ {BuildOs, Arch{ArchType: X86_64}, NativeBridgeDisabled, "", ""},
},
}
@@ -272,12 +272,12 @@
config.Targets = map[OsType][]Target{
Android: []Target{
- {Android, Arch{ArchType: Arm64, ArchVariant: "armv8-a", Native: true, Abi: []string{"arm64-v8a"}}, NativeBridgeDisabled},
- {Android, Arch{ArchType: Arm, ArchVariant: "armv7-a-neon", Native: true, Abi: []string{"armeabi-v7a"}}, NativeBridgeDisabled},
+ {Android, Arch{ArchType: Arm64, ArchVariant: "armv8-a", Native: true, Abi: []string{"arm64-v8a"}}, NativeBridgeDisabled, "", ""},
+ {Android, Arch{ArchType: Arm, ArchVariant: "armv7-a-neon", Native: true, Abi: []string{"armeabi-v7a"}}, NativeBridgeDisabled, "", ""},
},
BuildOs: []Target{
- {BuildOs, Arch{ArchType: X86_64}, NativeBridgeDisabled},
- {BuildOs, Arch{ArchType: X86}, NativeBridgeDisabled},
+ {BuildOs, Arch{ArchType: X86_64}, NativeBridgeDisabled, "", ""},
+ {BuildOs, Arch{ArchType: X86}, NativeBridgeDisabled, "", ""},
},
}
diff --git a/android/expand.go b/android/expand.go
index 527c4ac..67fb4ee 100644
--- a/android/expand.go
+++ b/android/expand.go
@@ -18,12 +18,30 @@
"fmt"
"strings"
"unicode"
+
+ "github.com/google/blueprint/proptools"
)
+// ExpandNinjaEscaped substitutes $() variables in a string
+// $(var) is passed to mapping(var), which should return the expanded value, a bool for whether the result should
+// be left unescaped when using in a ninja value (generally false, true if the expanded value is a ninja variable like
+// '${in}'), and an error.
+// $$ is converted to $, which is escaped back to $$.
+func ExpandNinjaEscaped(s string, mapping func(string) (string, bool, error)) (string, error) {
+ return expand(s, true, mapping)
+}
+
// Expand substitutes $() variables in a string
-// $(var) is passed to Expander(var)
-// $$ is converted to $
+// $(var) is passed to mapping(var), which should return the expanded value and an error.
+// $$ is converted to $.
func Expand(s string, mapping func(string) (string, error)) (string, error) {
+ return expand(s, false, func(s string) (string, bool, error) {
+ s, err := mapping(s)
+ return s, false, err
+ })
+}
+
+func expand(s string, ninjaEscape bool, mapping func(string) (string, bool, error)) (string, error) {
// based on os.Expand
buf := make([]byte, 0, 2*len(s))
i := 0
@@ -33,10 +51,13 @@
return "", fmt.Errorf("expected character after '$'")
}
buf = append(buf, s[i:j]...)
- value, w, err := getMapping(s[j+1:], mapping)
+ value, ninjaVariable, w, err := getMapping(s[j+1:], mapping)
if err != nil {
return "", err
}
+ if !ninjaVariable && ninjaEscape {
+ value = proptools.NinjaEscape(value)
+ }
buf = append(buf, value...)
j += w
i = j + 1
@@ -45,26 +66,26 @@
return string(buf) + s[i:], nil
}
-func getMapping(s string, mapping func(string) (string, error)) (string, int, error) {
+func getMapping(s string, mapping func(string) (string, bool, error)) (string, bool, int, error) {
switch s[0] {
case '(':
// Scan to closing brace
for i := 1; i < len(s); i++ {
if s[i] == ')' {
- ret, err := mapping(strings.TrimSpace(s[1:i]))
- return ret, i + 1, err
+ ret, ninjaVariable, err := mapping(strings.TrimSpace(s[1:i]))
+ return ret, ninjaVariable, i + 1, err
}
}
- return "", len(s), fmt.Errorf("missing )")
+ return "", false, len(s), fmt.Errorf("missing )")
case '$':
- return "$$", 1, nil
+ return "$", false, 1, nil
default:
i := strings.IndexFunc(s, unicode.IsSpace)
if i == 0 {
- return "", 0, fmt.Errorf("unexpected character '%c' after '$'", s[0])
+ return "", false, 0, fmt.Errorf("unexpected character '%c' after '$'", s[0])
} else if i == -1 {
i = len(s)
}
- return "", 0, fmt.Errorf("expected '(' after '$', did you mean $(%s)?", s[:i])
+ return "", false, 0, fmt.Errorf("expected '(' after '$', did you mean $(%s)?", s[:i])
}
}
diff --git a/android/expand_test.go b/android/expand_test.go
index 128de8a..12179ed 100644
--- a/android/expand_test.go
+++ b/android/expand_test.go
@@ -20,88 +20,111 @@
)
var vars = map[string]string{
- "var1": "abc",
- "var2": "",
- "var3": "def",
- "💩": "😃",
+ "var1": "abc",
+ "var2": "",
+ "var3": "def",
+ "💩": "😃",
+ "escape": "${in}",
}
-func expander(s string) (string, error) {
+func expander(s string) (string, bool, error) {
if val, ok := vars[s]; ok {
- return val, nil
+ return val, s == "escape", nil
} else {
- return "", fmt.Errorf("unknown variable %q", s)
+ return "", false, fmt.Errorf("unknown variable %q", s)
}
}
var expandTestCases = []struct {
- in string
- out string
- err bool
+ in string
+ out string
+ out_escaped string
+ err bool
}{
{
- in: "$(var1)",
- out: "abc",
+ in: "$(var1)",
+ out: "abc",
+ out_escaped: "abc",
},
{
- in: "$( var1 )",
- out: "abc",
+ in: "$( var1 )",
+ out: "abc",
+ out_escaped: "abc",
},
{
- in: "def$(var1)",
- out: "defabc",
+ in: "def$(var1)",
+ out: "defabc",
+ out_escaped: "defabc",
},
{
- in: "$(var1)def",
- out: "abcdef",
+ in: "$(var1)def",
+ out: "abcdef",
+ out_escaped: "abcdef",
},
{
- in: "def$(var1)def",
- out: "defabcdef",
+ in: "def$(var1)def",
+ out: "defabcdef",
+ out_escaped: "defabcdef",
},
{
- in: "$(var2)",
- out: "",
+ in: "$(var2)",
+ out: "",
+ out_escaped: "",
},
{
- in: "def$(var2)",
- out: "def",
+ in: "def$(var2)",
+ out: "def",
+ out_escaped: "def",
},
{
- in: "$(var2)def",
- out: "def",
+ in: "$(var2)def",
+ out: "def",
+ out_escaped: "def",
},
{
- in: "def$(var2)def",
- out: "defdef",
+ in: "def$(var2)def",
+ out: "defdef",
+ out_escaped: "defdef",
},
{
- in: "$(var1)$(var3)",
- out: "abcdef",
+ in: "$(var1)$(var3)",
+ out: "abcdef",
+ out_escaped: "abcdef",
},
{
- in: "$(var1)g$(var3)",
- out: "abcgdef",
+ in: "$(var1)g$(var3)",
+ out: "abcgdef",
+ out_escaped: "abcgdef",
},
{
- in: "$$",
- out: "$$",
+ in: "$$",
+ out: "$",
+ out_escaped: "$$",
},
{
- in: "$$(var1)",
- out: "$$(var1)",
+ in: "$$(var1)",
+ out: "$(var1)",
+ out_escaped: "$$(var1)",
},
{
- in: "$$$(var1)",
- out: "$$abc",
+ in: "$$$(var1)",
+ out: "$abc",
+ out_escaped: "$$abc",
},
{
- in: "$(var1)$$",
- out: "abc$$",
+ in: "$(var1)$$",
+ out: "abc$",
+ out_escaped: "abc$$",
},
{
- in: "$(💩)",
- out: "😃",
+ in: "$(💩)",
+ out: "😃",
+ out_escaped: "😃",
+ },
+ {
+ in: "$$a$(escape)$$b",
+ out: "$a${in}$b",
+ out_escaped: "$$a${in}$$b",
},
// Errors
@@ -141,7 +164,10 @@
func TestExpand(t *testing.T) {
for _, test := range expandTestCases {
- got, err := Expand(test.in, expander)
+ got, err := Expand(test.in, func(s string) (string, error) {
+ s, _, err := expander(s)
+ return s, err
+ })
if err != nil && !test.err {
t.Errorf("%q: unexpected error %s", test.in, err.Error())
} else if err == nil && test.err {
@@ -151,3 +177,16 @@
}
}
}
+
+func TestExpandNinjaEscaped(t *testing.T) {
+ for _, test := range expandTestCases {
+ got, err := ExpandNinjaEscaped(test.in, expander)
+ if err != nil && !test.err {
+ t.Errorf("%q: unexpected error %s", test.in, err.Error())
+ } else if err == nil && test.err {
+ t.Errorf("%q: expected error, got %q", test.in, got)
+ } else if !test.err && got != test.out_escaped {
+ t.Errorf("%q: expected %q, got %q", test.in, test.out, got)
+ }
+ }
+}
diff --git a/android/notices.go b/android/notices.go
index 8503593..7b61d65 100644
--- a/android/notices.go
+++ b/android/notices.go
@@ -42,8 +42,8 @@
})
generateNoticeRule = pctx.AndroidStaticRule("generateNoticeRule", blueprint.RuleParams{
- Command: `rm -rf $$(dirname $txtOut) $$(dirname htmlOut) $$(dirname $out) && ` +
- `mkdir -p $$(dirname $txtOut) $$(dirname htmlOut) $$(dirname $out) && ` +
+ Command: `rm -rf $$(dirname $txtOut) $$(dirname $htmlOut) $$(dirname $out) && ` +
+ `mkdir -p $$(dirname $txtOut) $$(dirname $htmlOut) $$(dirname $out) && ` +
`${generate_notice} --text-output $txtOut --html-output $htmlOut -t "$title" -s $inputDir && ` +
`${minigzip} -c $htmlOut > $out`,
CommandDeps: []string{"${generate_notice}", "${minigzip}"},
diff --git a/android/path_properties.go b/android/path_properties.go
index af7af59..6b1cdb3 100644
--- a/android/path_properties.go
+++ b/android/path_properties.go
@@ -35,16 +35,17 @@
props := m.base().generalProperties
+ var pathProperties []string
for _, ps := range props {
- pathProperties := pathPropertiesForPropertyStruct(ctx, ps)
- pathProperties = FirstUniqueStrings(pathProperties)
+ pathProperties = append(pathProperties, pathPropertiesForPropertyStruct(ctx, ps)...)
+ }
- for _, s := range pathProperties {
- if m, t := SrcIsModuleWithTag(s); m != "" {
- ctx.AddDependency(ctx.Module(), sourceOrOutputDepTag(t), m)
- }
+ pathProperties = FirstUniqueStrings(pathProperties)
+
+ for _, s := range pathProperties {
+ if m, t := SrcIsModuleWithTag(s); m != "" {
+ ctx.AddDependency(ctx.Module(), sourceOrOutputDepTag(t), m)
}
-
}
}
diff --git a/android/path_properties_test.go b/android/path_properties_test.go
index e98c136..59bfa6c 100644
--- a/android/path_properties_test.go
+++ b/android/path_properties_test.go
@@ -28,12 +28,17 @@
Qux string
}
+ // A second property struct with a duplicate property name
+ props2 struct {
+ Foo string `android:"path"`
+ }
+
sourceDeps []string
}
func pathDepsMutatorTestModuleFactory() Module {
module := &pathDepsMutatorTestModule{}
- module.AddProperties(&module.props)
+ module.AddProperties(&module.props, &module.props2)
InitAndroidArchModule(module, DeviceSupported, MultilibBoth)
return module
}
@@ -44,6 +49,13 @@
p.sourceDeps = append(p.sourceDeps, ctx.OtherModuleName(dep))
}
})
+
+ if p.props.Foo != "" {
+ // Make sure there is only one dependency on a module listed in a property present in multiple property structs
+ if ctx.GetDirectDepWithTag(SrcIsModule(p.props.Foo), sourceOrOutputDepTag("")) == nil {
+ ctx.ModuleErrorf("GetDirectDepWithTag failed")
+ }
+ }
}
func TestPathDepsMutator(t *testing.T) {
diff --git a/android/prebuilt.go b/android/prebuilt.go
index ad7c4aa..b674153 100644
--- a/android/prebuilt.go
+++ b/android/prebuilt.go
@@ -215,3 +215,7 @@
}
return value.String()
}
+
+func (p *Prebuilt) SourceExists() bool {
+ return p.properties.SourceExists
+}
diff --git a/android/proto.go b/android/proto.go
index 5247c68..c8ade45 100644
--- a/android/proto.go
+++ b/android/proto.go
@@ -135,7 +135,7 @@
}
rule.Command().
- Tool(ctx.Config().HostToolPath(ctx, "aprotoc")).
+ BuiltTool(ctx, "aprotoc").
FlagWithArg(flags.OutTypeFlag+"=", strings.Join(flags.OutParams, ",")+":"+outDir.String()).
FlagWithDepFile("--dependency_out=", depFile).
FlagWithArg("-I ", protoBase).
@@ -145,5 +145,5 @@
ImplicitOutputs(outputs)
rule.Command().
- Tool(ctx.Config().HostToolPath(ctx, "dep_fixer")).Flag(depFile.String())
+ BuiltTool(ctx, "dep_fixer").Flag(depFile.String())
}
diff --git a/android/rule_builder.go b/android/rule_builder.go
index e53eb0d..1238ddc 100644
--- a/android/rule_builder.go
+++ b/android/rule_builder.go
@@ -263,11 +263,36 @@
return toolsList
}
-// Commands returns a slice containing a the built command line for each call to RuleBuilder.Command.
+// RspFileInputs returns the list of paths that were passed to the RuleBuilderCommand.FlagWithRspFileInputList method.
+func (r *RuleBuilder) RspFileInputs() Paths {
+ var rspFileInputs Paths
+ for _, c := range r.commands {
+ if c.rspFileInputs != nil {
+ if rspFileInputs != nil {
+ panic("Multiple commands in a rule may not have rsp file inputs")
+ }
+ rspFileInputs = c.rspFileInputs
+ }
+ }
+
+ return rspFileInputs
+}
+
+// Commands returns a slice containing the built command line for each call to RuleBuilder.Command.
func (r *RuleBuilder) Commands() []string {
var commands []string
for _, c := range r.commands {
- commands = append(commands, c.buf.String())
+ commands = append(commands, c.String())
+ }
+ return commands
+}
+
+// NinjaEscapedCommands returns a slice containin the built command line after ninja escaping for each call to
+// RuleBuilder.Command.
+func (r *RuleBuilder) NinjaEscapedCommands() []string {
+ var commands []string
+ for _, c := range r.commands {
+ commands = append(commands, c.NinjaEscapedString())
}
return commands
}
@@ -284,7 +309,7 @@
func (r *RuleBuilder) depFileMergerCmd(ctx PathContext, depFiles WritablePaths) *RuleBuilderCommand {
return r.Command().
- Tool(ctx.Config().HostToolPath(ctx, "dep_fixer")).
+ BuiltTool(ctx, "dep_fixer").
Inputs(depFiles.Paths())
}
@@ -324,7 +349,7 @@
}
tools := r.Tools()
- commands := r.Commands()
+ commands := r.NinjaEscapedCommands()
outputs := r.Outputs()
if len(commands) == 0 {
@@ -334,7 +359,7 @@
panic("No outputs specified from any Commands")
}
- commandString := strings.Join(proptools.NinjaEscapeList(commands), " && ")
+ commandString := strings.Join(commands, " && ")
if r.sbox {
sboxOutputs := make([]string, len(outputs))
@@ -352,7 +377,7 @@
}
sboxCmd := &RuleBuilderCommand{}
- sboxCmd.Tool(ctx.Config().HostToolPath(ctx, "sbox")).
+ sboxCmd.BuiltTool(ctx, "sbox").
Flag("-c").Text(commandString).
Flag("--sandbox-path").Text(shared.TempDirForOutDir(PathForOutput(ctx).String())).
Flag("--output-root").Text(r.sboxOutDir.String()).
@@ -363,17 +388,27 @@
}
// Ninja doesn't like multiple outputs when depfiles are enabled, move all but the first output to
- // ImplicitOutputs. RuleBuilder never uses "$out", so the distinction between Outputs and ImplicitOutputs
- // doesn't matter.
+ // ImplicitOutputs. RuleBuilder only uses "$out" for the rsp file location, so the distinction between Outputs and
+ // ImplicitOutputs doesn't matter.
output := outputs[0]
implicitOutputs := outputs[1:]
+ var rspFile, rspFileContent string
+ rspFileInputs := r.RspFileInputs()
+ if rspFileInputs != nil {
+ rspFile = "$out.rsp"
+ rspFileContent = "$in"
+ }
+
ctx.Build(pctx, BuildParams{
Rule: ctx.Rule(pctx, name, blueprint.RuleParams{
- Command: commandString,
- CommandDeps: tools.Strings(),
- Restat: r.restat,
+ Command: commandString,
+ CommandDeps: tools.Strings(),
+ Restat: r.restat,
+ Rspfile: rspFile,
+ RspfileContent: rspFileContent,
}),
+ Inputs: rspFileInputs,
Implicits: r.Inputs(),
Output: output,
ImplicitOutputs: implicitOutputs,
@@ -388,11 +423,15 @@
// RuleBuilderCommand, so they can be used chained or unchained. All methods that add text implicitly add a single
// space as a separator from the previous method.
type RuleBuilderCommand struct {
- buf strings.Builder
- inputs Paths
- outputs WritablePaths
- depFiles WritablePaths
- tools Paths
+ buf strings.Builder
+ inputs Paths
+ outputs WritablePaths
+ depFiles WritablePaths
+ tools Paths
+ rspFileInputs Paths
+
+ // spans [start,end) of the command that should not be ninja escaped
+ unescapedSpans [][2]int
sbox bool
sboxOutDir WritablePath
@@ -478,6 +517,24 @@
return c.Text(path.String())
}
+// BuiltTool adds the specified tool path that was built using a host Soong module to the command line. The path will
+// be also added to the dependencies returned by RuleBuilder.Tools.
+//
+// It is equivalent to:
+// cmd.Tool(ctx.Config().HostToolPath(ctx, tool))
+func (c *RuleBuilderCommand) BuiltTool(ctx PathContext, tool string) *RuleBuilderCommand {
+ return c.Tool(ctx.Config().HostToolPath(ctx, tool))
+}
+
+// PrebuiltBuildTool adds the specified tool path from prebuils/build-tools. The path will be also added to the
+// dependencies returned by RuleBuilder.Tools.
+//
+// It is equivalent to:
+// cmd.Tool(ctx.Config().PrebuiltBuildTool(ctx, tool))
+func (c *RuleBuilderCommand) PrebuiltBuildTool(ctx PathContext, tool string) *RuleBuilderCommand {
+ return c.Tool(ctx.Config().PrebuiltBuildTool(ctx, tool))
+}
+
// Input adds the specified input path to the command line. The path will also be added to the dependencies returned by
// RuleBuilder.Inputs.
func (c *RuleBuilderCommand) Input(path Path) *RuleBuilderCommand {
@@ -606,11 +663,56 @@
return c.Text(flag + c.outputStr(path))
}
+// FlagWithRspFileInputList adds the specified flag and path to an rspfile to the command line, with no separator
+// between them. The paths will be written to the rspfile.
+func (c *RuleBuilderCommand) FlagWithRspFileInputList(flag string, paths Paths) *RuleBuilderCommand {
+ if c.rspFileInputs != nil {
+ panic("FlagWithRspFileInputList cannot be called if rsp file inputs have already been provided")
+ }
+
+ // Use an empty slice if paths is nil, the non-nil slice is used as an indicator that the rsp file must be
+ // generated.
+ if paths == nil {
+ paths = Paths{}
+ }
+
+ c.rspFileInputs = paths
+
+ rspFile := "$out.rsp"
+ c.FlagWithArg(flag, rspFile)
+ c.unescapedSpans = append(c.unescapedSpans, [2]int{c.buf.Len() - len(rspFile), c.buf.Len()})
+ return c
+}
+
// String returns the command line.
func (c *RuleBuilderCommand) String() string {
return c.buf.String()
}
+// String returns the command line.
+func (c *RuleBuilderCommand) NinjaEscapedString() string {
+ return ninjaEscapeExceptForSpans(c.String(), c.unescapedSpans)
+}
+
+func ninjaEscapeExceptForSpans(s string, spans [][2]int) string {
+ if len(spans) == 0 {
+ return proptools.NinjaEscape(s)
+ }
+
+ sb := strings.Builder{}
+ sb.Grow(len(s) * 11 / 10)
+
+ i := 0
+ for _, span := range spans {
+ sb.WriteString(proptools.NinjaEscape(s[i:span[0]]))
+ sb.WriteString(s[span[0]:span[1]])
+ i = span[1]
+ }
+ sb.WriteString(proptools.NinjaEscape(s[i:]))
+
+ return sb.String()
+}
+
func ninjaNameEscape(s string) string {
b := []byte(s)
escaped := false
diff --git a/android/rule_builder_test.go b/android/rule_builder_test.go
index cfbc2ab..6eba4f1 100644
--- a/android/rule_builder_test.go
+++ b/android/rule_builder_test.go
@@ -38,6 +38,7 @@
"ls": nil,
"turbine": nil,
"java": nil,
+ "javac": nil,
})
}
@@ -235,6 +236,34 @@
// ls --sort=time,size
}
+func ExampleRuleBuilderCommand_FlagWithRspFileInputList() {
+ ctx := pathContext()
+ fmt.Println(NewRuleBuilder().Command().
+ Tool(PathForSource(ctx, "javac")).
+ FlagWithRspFileInputList("@", PathsForTesting("a.java", "b.java")).
+ NinjaEscapedString())
+ // Output:
+ // javac @$out.rsp
+}
+
+func ExampleRuleBuilderCommand_String() {
+ fmt.Println(NewRuleBuilder().Command().
+ Text("FOO=foo").
+ Text("echo $FOO").
+ String())
+ // Output:
+ // FOO=foo echo $FOO
+}
+
+func ExampleRuleBuilderCommand_NinjaEscapedString() {
+ fmt.Println(NewRuleBuilder().Command().
+ Text("FOO=foo").
+ Text("echo $FOO").
+ NinjaEscapedString())
+ // Output:
+ // FOO=foo echo $$FOO
+}
+
func TestRuleBuilder(t *testing.T) {
fs := map[string][]byte{
"dep_fixer": nil,
@@ -503,3 +532,77 @@
"cp bar "+outFile, outFile, outFile+".d", true, nil)
})
}
+
+func Test_ninjaEscapeExceptForSpans(t *testing.T) {
+ type args struct {
+ s string
+ spans [][2]int
+ }
+ tests := []struct {
+ name string
+ args args
+ want string
+ }{
+ {
+ name: "empty",
+ args: args{
+ s: "",
+ },
+ want: "",
+ },
+ {
+ name: "unescape none",
+ args: args{
+ s: "$abc",
+ },
+ want: "$$abc",
+ },
+ {
+ name: "unescape all",
+ args: args{
+ s: "$abc",
+ spans: [][2]int{{0, 4}},
+ },
+ want: "$abc",
+ },
+ {
+ name: "unescape first",
+ args: args{
+ s: "$abc$",
+ spans: [][2]int{{0, 1}},
+ },
+ want: "$abc$$",
+ },
+ {
+ name: "unescape last",
+ args: args{
+ s: "$abc$",
+ spans: [][2]int{{4, 5}},
+ },
+ want: "$$abc$",
+ },
+ {
+ name: "unescape middle",
+ args: args{
+ s: "$a$b$c$",
+ spans: [][2]int{{2, 5}},
+ },
+ want: "$$a$b$c$$",
+ },
+ {
+ name: "unescape multiple",
+ args: args{
+ s: "$a$b$c$",
+ spans: [][2]int{{2, 3}, {4, 5}},
+ },
+ want: "$$a$b$c$$",
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ if got := ninjaEscapeExceptForSpans(tt.args.s, tt.args.spans); got != tt.want {
+ t.Errorf("ninjaEscapeExceptForSpans() = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
diff --git a/android/testing.go b/android/testing.go
index 44bee4b..12e30ec 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -382,3 +382,14 @@
entries.fillInEntries(config, bpPath, mod)
return entries
}
+
+func AndroidMkDataForTest(t *testing.T, config Config, bpPath string, mod blueprint.Module) AndroidMkData {
+ var p AndroidMkDataProvider
+ var ok bool
+ if p, ok = mod.(AndroidMkDataProvider); !ok {
+ t.Errorf("module does not implmement AndroidMkDataProvider: " + mod.Name())
+ }
+ data := p.AndroidMk()
+ data.fillInData(config, bpPath, mod)
+ return data
+}
diff --git a/android/util.go b/android/util.go
index 3b8bc78..97bec10 100644
--- a/android/util.go
+++ b/android/util.go
@@ -115,6 +115,17 @@
return false
}
+// IndexListPred returns the index of the element which in the given `list` satisfying the predicate, or -1 if there is no such element.
+func IndexListPred(pred func(s string) bool, list []string) int {
+ for i, l := range list {
+ if pred(l) {
+ return i
+ }
+ }
+
+ return -1
+}
+
func FilterList(list []string, filter []string) (remainder []string, filtered []string) {
for _, l := range list {
if InList(l, filter) {
diff --git a/android/variable.go b/android/variable.go
index fcd92d4..47586a7 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -165,15 +165,17 @@
DeviceSecondaryCpuVariant *string `json:",omitempty"`
DeviceSecondaryAbi []string `json:",omitempty"`
- NativeBridgeArch *string `json:",omitempty"`
- NativeBridgeArchVariant *string `json:",omitempty"`
- NativeBridgeCpuVariant *string `json:",omitempty"`
- NativeBridgeAbi []string `json:",omitempty"`
+ NativeBridgeArch *string `json:",omitempty"`
+ NativeBridgeArchVariant *string `json:",omitempty"`
+ NativeBridgeCpuVariant *string `json:",omitempty"`
+ NativeBridgeAbi []string `json:",omitempty"`
+ NativeBridgeRelativePath *string `json:",omitempty"`
- NativeBridgeSecondaryArch *string `json:",omitempty"`
- NativeBridgeSecondaryArchVariant *string `json:",omitempty"`
- NativeBridgeSecondaryCpuVariant *string `json:",omitempty"`
- NativeBridgeSecondaryAbi []string `json:",omitempty"`
+ NativeBridgeSecondaryArch *string `json:",omitempty"`
+ NativeBridgeSecondaryArchVariant *string `json:",omitempty"`
+ NativeBridgeSecondaryCpuVariant *string `json:",omitempty"`
+ NativeBridgeSecondaryAbi []string `json:",omitempty"`
+ NativeBridgeSecondaryRelativePath *string `json:",omitempty"`
HostArch *string `json:",omitempty"`
HostSecondaryArch *string `json:",omitempty"`
diff --git a/apex/apex.go b/apex/apex.go
index f95bd05..9701e2a 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -656,8 +656,10 @@
dirInApex = "lib64"
}
dirInApex = filepath.Join(dirInApex, cc.RelativeInstallPath())
- if cc.Target().NativeBridge == android.NativeBridgeEnabled || !cc.Arch().Native {
+ if !cc.Arch().Native {
dirInApex = filepath.Join(dirInApex, cc.Arch().ArchType.String())
+ } else if cc.Target().NativeBridge == android.NativeBridgeEnabled {
+ dirInApex = filepath.Join(dirInApex, cc.Target().NativeBridgeRelativePath)
}
if handleSpecialLibs {
switch cc.Name() {
@@ -681,8 +683,10 @@
func getCopyManifestForExecutable(cc *cc.Module) (fileToCopy android.Path, dirInApex string) {
dirInApex = filepath.Join("bin", cc.RelativeInstallPath())
- if cc.Target().NativeBridge == android.NativeBridgeEnabled || !cc.Arch().Native {
+ if !cc.Arch().Native {
dirInApex = filepath.Join(dirInApex, cc.Arch().ArchType.String())
+ } else if cc.Target().NativeBridge == android.NativeBridgeEnabled {
+ dirInApex = filepath.Join(dirInApex, cc.Target().NativeBridgeRelativePath)
}
fileToCopy = cc.OutputFile().Path()
return
@@ -1403,7 +1407,8 @@
type PrebuiltProperties struct {
// the path to the prebuilt .apex file to import.
- Source string `blueprint:"mutated"`
+ Source string `blueprint:"mutated"`
+ ForceDisable bool `blueprint:"mutated"`
Src *string
Arch struct {
@@ -1432,6 +1437,23 @@
}
func (p *Prebuilt) DepsMutator(ctx android.BottomUpMutatorContext) {
+ // If the device is configured to use flattened APEX, force disable the prebuilt because
+ // the prebuilt is a non-flattened one.
+ forceDisable := ctx.Config().FlattenApex()
+
+ // Force disable the prebuilts when we are doing unbundled build. We do unbundled build
+ // to build the prebuilts themselves.
+ forceDisable = forceDisable || ctx.Config().UnbundledBuild()
+
+ // b/137216042 don't use prebuilts when address sanitizer is on
+ forceDisable = forceDisable || android.InList("address", ctx.Config().SanitizeDevice()) ||
+ android.InList("hwaddress", ctx.Config().SanitizeDevice())
+
+ if forceDisable && p.prebuilt.SourceExists() {
+ p.properties.ForceDisable = true
+ return
+ }
+
// This is called before prebuilt_select and prebuilt_postdeps mutators
// The mutators requires that src to be set correctly for each arch so that
// arch variants are disabled when src is not provided for the arch.
@@ -1473,6 +1495,10 @@
}
func (p *Prebuilt) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ if p.properties.ForceDisable {
+ return
+ }
+
// TODO(jungjw): Check the key validity.
p.inputApex = p.Prebuilt().SingleSourcePath(ctx)
p.installDir = android.PathForModuleInstall(ctx, "apex")
diff --git a/cc/builder.go b/cc/builder.go
index ee40736..cbe2c88 100644
--- a/cc/builder.go
+++ b/cc/builder.go
@@ -78,7 +78,7 @@
blueprint.RuleParams{
// Without -no-pie, clang 7.0 adds -pie to link Android files,
// but -r and -pie cannot be used together.
- Command: "$ldCmd -nostdlib -no-pie -Wl,-r ${in} -o ${out} ${ldFlags}",
+ Command: "$ldCmd -fuse-ld=lld -nostdlib -no-pie -Wl,-r ${in} -o ${out} ${ldFlags}",
CommandDeps: []string{"$ldCmd"},
},
"ldCmd", "ldFlags")
diff --git a/cc/config/x86_64_device.go b/cc/config/x86_64_device.go
index d9c96df..bcfae5d 100644
--- a/cc/config/x86_64_device.go
+++ b/cc/config/x86_64_device.go
@@ -63,7 +63,7 @@
}
x86_64ArchFeatureCflags = map[string][]string{
- "ssse3": []string{"-DUSE_SSSE3", "-mssse3"},
+ "ssse3": []string{"-mssse3"},
"sse4": []string{"-msse4"},
"sse4_1": []string{"-msse4.1"},
"sse4_2": []string{"-msse4.2"},
diff --git a/cc/config/x86_64_fuchsia_device.go b/cc/config/x86_64_fuchsia_device.go
index 79af00c..0f2013b 100644
--- a/cc/config/x86_64_fuchsia_device.go
+++ b/cc/config/x86_64_fuchsia_device.go
@@ -92,7 +92,7 @@
}
func (t *toolchainFuchsiaX8664) ToolchainClangCflags() string {
- return "-DUSE_SSSE3 -mssse3"
+ return "-mssse3"
}
var toolchainFuchsiaSingleton Toolchain = &toolchainFuchsiaX8664{}
diff --git a/cc/config/x86_device.go b/cc/config/x86_device.go
index 6504758..64392dc 100644
--- a/cc/config/x86_device.go
+++ b/cc/config/x86_device.go
@@ -82,7 +82,7 @@
}
x86ArchFeatureCflags = map[string][]string{
- "ssse3": []string{"-DUSE_SSSE3", "-mssse3"},
+ "ssse3": []string{"-mssse3"},
"sse4": []string{"-msse4"},
"sse4_1": []string{"-msse4.1"},
"sse4_2": []string{"-msse4.2"},
diff --git a/cc/gen.go b/cc/gen.go
index 7516b28..e5c4194 100644
--- a/cc/gen.go
+++ b/cc/gen.go
@@ -99,7 +99,7 @@
cmd.Text("BISON_PKGDATADIR=prebuilts/build-tools/common/bison").
FlagWithInput("M4=", ctx.Config().PrebuiltBuildTool(ctx, "m4")).
- Tool(ctx.Config().PrebuiltBuildTool(ctx, "bison")).
+ PrebuiltBuildTool(ctx, "bison").
Flag("-d").
Flags(flags).
FlagWithOutput("--defines=", headerFile).
@@ -121,7 +121,7 @@
headerBp := outDir.Join(ctx, aidlPackage, "Bp"+shortName+".h")
cmd := rule.Command()
- cmd.Tool(ctx.Config().HostToolPath(ctx, "aidl-cpp")).
+ cmd.BuiltTool(ctx, "aidl-cpp").
FlagWithDepFile("-d", depFile).
Flag("--ninja").
Flag(aidlFlags).
diff --git a/cc/installer.go b/cc/installer.go
index cb261b7..a52ccf1 100644
--- a/cc/installer.go
+++ b/cc/installer.go
@@ -66,9 +66,12 @@
if ctx.toolchain().Is64Bit() && installer.dir64 != "" {
dir = installer.dir64
}
- if (!ctx.Host() && !ctx.Arch().Native) || ctx.Target().NativeBridge == android.NativeBridgeEnabled {
+ if !ctx.Host() && !ctx.Arch().Native {
dir = filepath.Join(dir, ctx.Arch().ArchType.String())
}
+ if ctx.Target().NativeBridge == android.NativeBridgeEnabled {
+ dir = filepath.Join(dir, ctx.Target().NativeBridgeRelativePath)
+ }
if installer.location == InstallInData && ctx.useVndk() {
dir = filepath.Join(dir, "vendor")
}
diff --git a/cc/linker.go b/cc/linker.go
index dda2fcb..fa3b0a6 100644
--- a/cc/linker.go
+++ b/cc/linker.go
@@ -483,7 +483,7 @@
Input: in,
Output: out,
Args: map[string]string{
- "buildNumberFromFile": ctx.Config().BuildNumberFromFile(),
+ "buildNumberFromFile": proptools.NinjaEscape(ctx.Config().BuildNumberFromFile()),
},
})
}
diff --git a/cc/sanitize.go b/cc/sanitize.go
index 0eb9a74..c59f53a 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -445,7 +445,6 @@
// libraries needed with -fsanitize=address. http://b/18650275 (WAI)
flags.LdFlags = append(flags.LdFlags, "-Wl,--no-as-needed")
} else {
- flags.CFlags = append(flags.CFlags, "-mllvm", "-asan-globals=0")
if ctx.bootstrap() {
flags.DynamicLinker = "/system/bin/bootstrap/linker_asan"
} else {
diff --git a/genrule/genrule.go b/genrule/genrule.go
index b0657ff..411da05 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -284,12 +284,12 @@
referencedDepfile := false
- rawCommand, err := android.Expand(task.cmd, func(name string) (string, error) {
+ rawCommand, err := android.ExpandNinjaEscaped(task.cmd, func(name string) (string, bool, error) {
// report the error directly without returning an error to android.Expand to catch multiple errors in a
// single run
- reportError := func(fmt string, args ...interface{}) (string, error) {
+ reportError := func(fmt string, args ...interface{}) (string, bool, error) {
ctx.PropertyErrorf("cmd", fmt, args...)
- return "SOONG_ERROR", nil
+ return "SOONG_ERROR", false, nil
}
switch name {
@@ -304,19 +304,19 @@
return reportError("default label %q has multiple files, use $(locations %s) to reference it",
firstLabel, firstLabel)
}
- return locationLabels[firstLabel][0], nil
+ return locationLabels[firstLabel][0], false, nil
case "in":
- return "${in}", nil
+ return "${in}", true, nil
case "out":
- return "__SBOX_OUT_FILES__", nil
+ return "__SBOX_OUT_FILES__", false, nil
case "depfile":
referencedDepfile = true
if !Bool(g.properties.Depfile) {
return reportError("$(depfile) used without depfile property")
}
- return "__SBOX_DEPFILE__", nil
+ return "__SBOX_DEPFILE__", false, nil
case "genDir":
- return "__SBOX_OUT_DIR__", nil
+ return "__SBOX_OUT_DIR__", false, nil
default:
if strings.HasPrefix(name, "location ") {
label := strings.TrimSpace(strings.TrimPrefix(name, "location "))
@@ -327,7 +327,7 @@
return reportError("label %q has multiple files, use $(locations %s) to reference it",
label, label)
}
- return paths[0], nil
+ return paths[0], false, nil
} else {
return reportError("unknown location label %q", label)
}
@@ -337,7 +337,7 @@
if len(paths) == 0 {
return reportError("label %q has no files", label)
}
- return strings.Join(paths, " "), nil
+ return strings.Join(paths, " "), false, nil
} else {
return reportError("unknown locations label %q", label)
}
diff --git a/java/androidmk_test.go b/java/androidmk_test.go
new file mode 100644
index 0000000..107837d
--- /dev/null
+++ b/java/androidmk_test.go
@@ -0,0 +1,184 @@
+// Copyright 2019 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.
+
+package java
+
+import (
+ "android/soong/android"
+ "bytes"
+ "io"
+ "io/ioutil"
+ "strings"
+ "testing"
+)
+
+type testAndroidMk struct {
+ *testing.T
+ body []byte
+}
+type testAndroidMkModule struct {
+ *testing.T
+ props map[string]string
+}
+
+func newTestAndroidMk(t *testing.T, r io.Reader) *testAndroidMk {
+ t.Helper()
+ buf, err := ioutil.ReadAll(r)
+ if err != nil {
+ t.Fatal("failed to open read Android.mk.", err)
+ }
+ return &testAndroidMk{
+ T: t,
+ body: buf,
+ }
+}
+
+func parseAndroidMkProps(lines []string) map[string]string {
+ props := make(map[string]string)
+ for _, line := range lines {
+ line = strings.TrimLeft(line, " ")
+ if line == "" || strings.HasPrefix(line, "#") {
+ continue
+ }
+ tokens := strings.Split(line, " ")
+ if tokens[1] == "+=" {
+ props[tokens[0]] += " " + strings.Join(tokens[2:], " ")
+ } else {
+ props[tokens[0]] = strings.Join(tokens[2:], " ")
+ }
+ }
+ return props
+}
+
+func (t *testAndroidMk) moduleFor(moduleName string) *testAndroidMkModule {
+ t.Helper()
+ lines := strings.Split(string(t.body), "\n")
+ index := android.IndexList("LOCAL_MODULE := "+moduleName, lines)
+ if index == -1 {
+ t.Fatalf("%q is not found.", moduleName)
+ }
+ lines = lines[index:]
+ includeIndex := android.IndexListPred(func(line string) bool {
+ return strings.HasPrefix(line, "include")
+ }, lines)
+ if includeIndex == -1 {
+ t.Fatalf("%q is not properly defined. (\"include\" not found).", moduleName)
+ }
+ props := parseAndroidMkProps(lines[:includeIndex])
+ return &testAndroidMkModule{
+ T: t.T,
+ props: props,
+ }
+}
+
+func (t *testAndroidMkModule) hasRequired(dep string) {
+ t.Helper()
+ required, ok := t.props["LOCAL_REQUIRED_MODULES"]
+ if !ok {
+ t.Error("LOCAL_REQUIRED_MODULES is not found.")
+ return
+ }
+ if !android.InList(dep, strings.Split(required, " ")) {
+ t.Errorf("%q is expected in LOCAL_REQUIRED_MODULES, but not found in %q.", dep, required)
+ }
+}
+
+func (t *testAndroidMkModule) hasNoRequired(dep string) {
+ t.Helper()
+ required, ok := t.props["LOCAL_REQUIRED_MODULES"]
+ if !ok {
+ return
+ }
+ if android.InList(dep, strings.Split(required, " ")) {
+ t.Errorf("%q is not expected in LOCAL_REQUIRED_MODULES, but found.", dep)
+ }
+}
+
+func getAndroidMk(t *testing.T, ctx *android.TestContext, config android.Config, name string) *testAndroidMk {
+ t.Helper()
+ lib, _ := ctx.ModuleForTests(name, "android_common").Module().(*Library)
+ data := android.AndroidMkDataForTest(t, config, "", lib)
+ w := &bytes.Buffer{}
+ data.Custom(w, name, "", "", data)
+ return newTestAndroidMk(t, w)
+}
+
+func TestRequired(t *testing.T) {
+ config := testConfig(nil)
+ ctx := testContext(config, `
+ java_library {
+ name: "foo",
+ srcs: ["a.java"],
+ required: ["libfoo"],
+ }
+ `, nil)
+ run(t, ctx, config)
+
+ mk := getAndroidMk(t, ctx, config, "foo")
+ mk.moduleFor("foo").hasRequired("libfoo")
+}
+
+func TestHostdex(t *testing.T) {
+ config := testConfig(nil)
+ ctx := testContext(config, `
+ java_library {
+ name: "foo",
+ srcs: ["a.java"],
+ hostdex: true,
+ }
+ `, nil)
+ run(t, ctx, config)
+
+ mk := getAndroidMk(t, ctx, config, "foo")
+ mk.moduleFor("foo")
+ mk.moduleFor("foo-hostdex")
+}
+
+func TestHostdexRequired(t *testing.T) {
+ config := testConfig(nil)
+ ctx := testContext(config, `
+ java_library {
+ name: "foo",
+ srcs: ["a.java"],
+ hostdex: true,
+ required: ["libfoo"],
+ }
+ `, nil)
+ run(t, ctx, config)
+
+ mk := getAndroidMk(t, ctx, config, "foo")
+ mk.moduleFor("foo").hasRequired("libfoo")
+ mk.moduleFor("foo-hostdex").hasRequired("libfoo")
+}
+
+func TestHostdexSpecificRequired(t *testing.T) {
+ config := testConfig(nil)
+ ctx := testContext(config, `
+ java_library {
+ name: "foo",
+ srcs: ["a.java"],
+ hostdex: true,
+ target: {
+ hostdex: {
+ required: ["libfoo"],
+ },
+ },
+ }
+ `, nil)
+ run(t, ctx, config)
+
+ mk := getAndroidMk(t, ctx, config, "foo")
+ mk.moduleFor("foo").hasNoRequired("libfoo")
+ mk.moduleFor("foo-hostdex").hasRequired("libfoo")
+}
diff --git a/java/app.go b/java/app.go
index 3442658..a679e88 100644
--- a/java/app.go
+++ b/java/app.go
@@ -816,7 +816,7 @@
rule := android.NewRuleBuilder()
rule.Command().
Textf(`if (zipinfo %s 'lib/*.so' 2>/dev/null | grep -v ' stor ' >/dev/null) ; then`, inputPath).
- Tool(ctx.Config().HostToolPath(ctx, "zip2zip")).
+ BuiltTool(ctx, "zip2zip").
FlagWithInput("-i ", inputPath).
FlagWithOutput("-o ", outputPath).
FlagWithArg("-0 ", "'lib/**/*.so'").
@@ -843,7 +843,7 @@
rule := android.NewRuleBuilder()
rule.Command().
Textf(`if (zipinfo %s '*.dex' 2>/dev/null | grep -v ' stor ' >/dev/null) ; then`, inputPath).
- Tool(ctx.Config().HostToolPath(ctx, "zip2zip")).
+ BuiltTool(ctx, "zip2zip").
FlagWithInput("-i ", inputPath).
FlagWithOutput("-o ", outputPath).
FlagWithArg("-0 ", "'classes*.dex'").
@@ -1067,7 +1067,7 @@
outputFile := android.PathForModuleOut(ctx, "manifest_check", "AndroidManifest.xml")
rule := android.NewRuleBuilder()
- cmd := rule.Command().Tool(ctx.Config().HostToolPath(ctx, "manifest_check")).
+ cmd := rule.Command().BuiltTool(ctx, "manifest_check").
Flag("--enforce-uses-libraries").
Input(manifest).
FlagWithOutput("-o ", outputFile)
diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go
index fe468a9..4ef5bcf 100644
--- a/java/dexpreopt_bootjars.go
+++ b/java/dexpreopt_bootjars.go
@@ -229,7 +229,7 @@
if image.zip != nil {
rule := android.NewRuleBuilder()
rule.Command().
- Tool(ctx.Config().HostToolPath(ctx, "soong_zip")).
+ BuiltTool(ctx, "soong_zip").
FlagWithOutput("-o ", image.zip).
FlagWithArg("-C ", image.dir.String()).
FlagWithInputList("-f ", allFiles, " -f ")
@@ -438,7 +438,7 @@
rule := android.NewRuleBuilder()
rule.Command().
// TODO: for now, use the debug version for better error reporting
- Tool(ctx.Config().HostToolPath(ctx, "oatdumpd")).
+ BuiltTool(ctx, "oatdumpd").
FlagWithInputList("--runtime-arg -Xbootclasspath:", image.dexPaths.Paths(), ":").
FlagWithList("--runtime-arg -Xbootclasspath-locations:", image.dexLocations, ":").
FlagWithArg("--image=", dexpreopt.PathToLocation(image.images[arch], arch)).Implicit(image.images[arch]).
diff --git a/java/droiddoc.go b/java/droiddoc.go
index 25101de..ae810e1 100644
--- a/java/droiddoc.go
+++ b/java/droiddoc.go
@@ -500,6 +500,7 @@
}
}
+// javadoc converts .java source files to documentation using javadoc.
func JavadocFactory() android.Module {
module := &Javadoc{}
@@ -509,6 +510,7 @@
return module
}
+// javadoc_host converts .java source files to documentation using javadoc.
func JavadocHostFactory() android.Module {
module := &Javadoc{}
@@ -798,7 +800,7 @@
"srcJarDir": android.PathForModuleOut(ctx, "srcjars").String(),
"stubsDir": android.PathForModuleOut(ctx, "stubsDir").String(),
"srcJars": strings.Join(j.srcJars.Strings(), " "),
- "opts": opts,
+ "opts": proptools.NinjaEscape(opts),
"bootclasspathArgs": bootClasspathArgs,
"classpathArgs": classpathArgs,
"sourcepathArgs": sourcepathArgs,
@@ -831,6 +833,7 @@
apiFilePath android.Path
}
+// droiddoc converts .java source files to documentation using doclava or dokka.
func DroiddocFactory() android.Module {
module := &Droiddoc{}
@@ -841,6 +844,7 @@
return module
}
+// droiddoc_host converts .java source files to documentation using doclava or dokka.
func DroiddocHostFactory() android.Module {
module := &Droiddoc{}
@@ -918,7 +922,7 @@
args := " -source 1.8 -J-Xmx1600m -J-XX:-OmitStackTraceInFastThrow -XDignore.symbol.file " +
"-doclet com.google.doclava.Doclava -docletpath " + jsilver.String() + ":" + doclava.String() + " " +
"-hdf page.build " + ctx.Config().BuildId() + "-" + ctx.Config().BuildNumberFromFile() + " " +
- `-hdf page.now "$$(` + date + ` @$$(cat ` + ctx.Config().Getenv("BUILD_DATETIME_FILE") + `) "+%d %b %Y %k:%M")" `
+ `-hdf page.now "$(` + date + ` @$(cat ` + ctx.Config().Getenv("BUILD_DATETIME_FILE") + `) "+%d %b %Y %k:%M")" `
if String(d.properties.Custom_template) == "" {
// TODO: This is almost always droiddoc-templates-sdk
@@ -1095,7 +1099,7 @@
"srcJarDir": android.PathForModuleOut(ctx, "srcjars").String(),
"stubsDir": android.PathForModuleOut(ctx, "stubsDir").String(),
"srcJars": strings.Join(d.Javadoc.srcJars.Strings(), " "),
- "opts": opts,
+ "opts": proptools.NinjaEscape(opts),
"bootclasspathArgs": bootclasspathArgs,
"classpathArgs": classpathArgs,
"sourcepathArgs": sourcepathArgs,
@@ -1117,7 +1121,7 @@
Args: map[string]string{
"msg": msg,
"classpath": checkApiClasspath.FormJavaClassPath(""),
- "opts": opts,
+ "opts": proptools.NinjaEscape(opts),
"apiFile": apiFile.String(),
"apiFileToCheck": d.apiFile.String(),
"removedApiFile": removedApiFile.String(),
@@ -1140,7 +1144,7 @@
"stubsDir": android.PathForModuleOut(ctx, "dokka-stubsDir").String(),
"srcJars": strings.Join(d.Javadoc.srcJars.Strings(), " "),
"classpathArgs": classpathArgs,
- "opts": opts,
+ "opts": proptools.NinjaEscape(opts),
"docZip": d.Javadoc.docZip.String(),
},
})
@@ -1258,6 +1262,9 @@
jdiffStubsSrcJar android.WritablePath
}
+// droidstubs passes sources files through Metalava to generate stub .java files that only contain the API to be
+// documented, filtering out hidden classes and methods. The resulting .java files are intended to be passed to
+// a droiddoc module to generate documentation.
func DroidstubsFactory() android.Module {
module := &Droidstubs{}
@@ -1268,6 +1275,10 @@
return module
}
+// droidstubs_host passes sources files through Metalava to generate stub .java files that only contain the API
+// to be documented, filtering out hidden classes and methods. The resulting .java files are intended to be
+// passed to a droiddoc_host module to generate documentation. Use a droidstubs_host instead of a droidstubs
+// module when symbols needed by the source files are provided by java_library_host modules.
func DroidstubsHostFactory() android.Module {
module := &Droidstubs{}
@@ -1558,7 +1569,7 @@
"bootclasspathArgs": bootclasspathArgs,
"classpathArgs": classpathArgs,
"sourcepathArgs": sourcepathArgs,
- "opts": opts,
+ "opts": proptools.NinjaEscape(opts),
},
})
}
@@ -1581,7 +1592,7 @@
"bootclasspathArgs": bootclasspathArgs,
"classpathArgs": classpathArgs,
"sourcepathArgs": sourcepathArgs,
- "opts": opts,
+ "opts": proptools.NinjaEscape(opts),
"msg": msg,
},
})
@@ -1602,7 +1613,7 @@
"srcJarDir": android.PathForModuleOut(ctx, "jdiff-srcjars").String(),
"stubsDir": android.PathForModuleOut(ctx, "jdiff-stubsDir").String(),
"srcJars": strings.Join(d.Javadoc.srcJars.Strings(), " "),
- "opts": opts,
+ "opts": proptools.NinjaEscape(opts),
"bootclasspathArgs": bootclasspathArgs,
"classpathArgs": classpathArgs,
"sourcepathArgs": sourcepathArgs,
@@ -1781,6 +1792,7 @@
dir android.Path
}
+// droiddoc_exported_dir exports a directory of html templates or nullability annotations for use by doclava.
func ExportedDroiddocDirFactory() android.Module {
module := &ExportedDroiddocDir{}
module.AddProperties(&module.properties)
diff --git a/java/hiddenapi_singleton.go b/java/hiddenapi_singleton.go
index cf9f492..c83dda1 100644
--- a/java/hiddenapi_singleton.go
+++ b/java/hiddenapi_singleton.go
@@ -64,7 +64,7 @@
stubFlagsRule(ctx)
// These rules depend on files located in frameworks/base, skip them if running in a tree that doesn't have them.
- if ctx.Config().FrameworksBaseDirExists(ctx) && !ctx.Config().UnbundledBuild() {
+ if ctx.Config().FrameworksBaseDirExists(ctx) {
h.flags = flagsRule(ctx)
h.metadata = metadataRule(ctx)
} else {
@@ -97,7 +97,7 @@
// Add the android.test.base to the set of stubs only if the android.test.base module is on
// the boot jars list as the runtime will only enforce hiddenapi access against modules on
// that list.
- if inList("android.test.base", ctx.Config().BootJars()) {
+ if inList("android.test.base", ctx.Config().BootJars()) && !ctx.Config().UnbundledBuildUsePrebuiltSdks() {
publicStubModules = append(publicStubModules, "android.test.base.stubs")
}
diff --git a/java/java.go b/java/java.go
index 7c84e76..a49aad7 100644
--- a/java/java.go
+++ b/java/java.go
@@ -1322,11 +1322,9 @@
return
}
- if !ctx.Config().UnbundledBuild() {
- // Hidden API CSV generation and dex encoding
- dexOutputFile = j.hiddenAPI.hiddenAPI(ctx, dexOutputFile, j.implementationJarFile,
- j.deviceProperties.UncompressDex)
- }
+ // Hidden API CSV generation and dex encoding
+ dexOutputFile = j.hiddenAPI.hiddenAPI(ctx, dexOutputFile, j.implementationJarFile,
+ j.deviceProperties.UncompressDex)
// merge dex jar with resources if necessary
if j.resourceJar != nil {
@@ -2042,7 +2040,7 @@
// dex_import module
type DexImportProperties struct {
- Jars []string
+ Jars []string `android:"path"`
}
type DexImport struct {
@@ -2070,10 +2068,6 @@
return j.prebuilt.Name(j.ModuleBase.Name())
}
-func (j *DexImport) DepsMutator(ctx android.BottomUpMutatorContext) {
- android.ExtractSourcesDeps(ctx, j.properties.Jars)
-}
-
func (j *DexImport) GenerateAndroidBuildActions(ctx android.ModuleContext) {
if len(j.properties.Jars) != 1 {
ctx.PropertyErrorf("jars", "exactly one jar must be provided")
@@ -2094,14 +2088,14 @@
// use zip2zip to uncompress classes*.dex files
rule.Command().
- Tool(ctx.Config().HostToolPath(ctx, "zip2zip")).
+ BuiltTool(ctx, "zip2zip").
FlagWithInput("-i ", inputJar).
FlagWithOutput("-o ", temporary).
FlagWithArg("-0 ", "'classes*.dex'")
// use zipalign to align uncompressed classes*.dex files
rule.Command().
- Tool(ctx.Config().HostToolPath(ctx, "zipalign")).
+ BuiltTool(ctx, "zipalign").
Flag("-f").
Text("4").
Input(temporary).
diff --git a/java/proto.go b/java/proto.go
index 37de1d2..0ec6499 100644
--- a/java/proto.go
+++ b/java/proto.go
@@ -34,7 +34,7 @@
// Proto generated java files have an unknown package name in the path, so package the entire output directory
// into a srcjar.
rule.Command().
- Tool(ctx.Config().HostToolPath(ctx, "soong_zip")).
+ BuiltTool(ctx, "soong_zip").
Flag("-jar").
FlagWithOutput("-o ", srcJarFile).
FlagWithArg("-C ", outDir.String()).
diff --git a/java/sdk.go b/java/sdk.go
index 7b79a49..d1e2ae4 100644
--- a/java/sdk.go
+++ b/java/sdk.go
@@ -296,7 +296,7 @@
rule.Command().
Text("rm -f").Output(aidl)
rule.Command().
- Tool(ctx.Config().HostToolPath(ctx, "sdkparcelables")).
+ BuiltTool(ctx, "sdkparcelables").
Input(jar).
Output(aidl)
diff --git a/java/sdk_library.go b/java/sdk_library.go
index b4a3f29..d38088d 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -63,12 +63,6 @@
javaSdkLibrariesLock sync.Mutex
)
-// java_sdk_library is to make a Java library that implements optional platform APIs to apps.
-// It is actually a wrapper of several modules: 1) stubs library that clients are linked against
-// to, 2) droiddoc module that internally generates API stubs source files, 3) the real runtime
-// shared library that implements the APIs, and 4) XML file for adding the runtime lib to the
-// classpath at runtime if requested via <uses-library>.
-//
// TODO: these are big features that are currently missing
// 1) disallowing linking to the runtime shared lib
// 2) HTML generation
@@ -155,16 +149,21 @@
var _ SdkLibraryDependency = (*SdkLibrary)(nil)
func (module *SdkLibrary) DepsMutator(ctx android.BottomUpMutatorContext) {
+ useBuiltStubs := !ctx.Config().UnbundledBuildUsePrebuiltSdks()
// Add dependencies to the stubs library
- ctx.AddVariationDependencies(nil, publicApiStubsTag, module.stubsName(apiScopePublic))
+ if useBuiltStubs {
+ ctx.AddVariationDependencies(nil, publicApiStubsTag, module.stubsName(apiScopePublic))
+ }
ctx.AddVariationDependencies(nil, publicApiFileTag, module.docsName(apiScopePublic))
sdkDep := decodeSdkDep(ctx, sdkContext(&module.Library))
if sdkDep.hasStandardLibs() {
- ctx.AddVariationDependencies(nil, systemApiStubsTag, module.stubsName(apiScopeSystem))
+ if useBuiltStubs {
+ ctx.AddVariationDependencies(nil, systemApiStubsTag, module.stubsName(apiScopeSystem))
+ ctx.AddVariationDependencies(nil, testApiStubsTag, module.stubsName(apiScopeTest))
+ }
ctx.AddVariationDependencies(nil, systemApiFileTag, module.docsName(apiScopeSystem))
ctx.AddVariationDependencies(nil, testApiFileTag, module.docsName(apiScopeTest))
- ctx.AddVariationDependencies(nil, testApiStubsTag, module.stubsName(apiScopeTest))
}
module.Library.deps(ctx)
@@ -746,6 +745,11 @@
module.Library.Module.deviceProperties.IsSDKLibrary = true
}
+// java_sdk_library is a special Java library that provides optional platform APIs to apps.
+// In practice, it can be viewed as a combination of several modules: 1) stubs library that clients
+// are linked against to, 2) droiddoc module that internally generates API stubs source files,
+// 3) the real runtime shared library that implements the APIs, and 4) XML file for adding
+// the runtime lib to the classpath at runtime if requested via <uses-library>.
func SdkLibraryFactory() android.Module {
module := &SdkLibrary{}
module.InitSdkLibraryProperties()
@@ -787,6 +791,7 @@
var _ SdkLibraryDependency = (*sdkLibraryImport)(nil)
+// java_sdk_library_import imports a prebuilt java_sdk_library.
func sdkLibraryImportFactory() android.Module {
module := &sdkLibraryImport{}
diff --git a/python/proto.go b/python/proto.go
index 85ed1a5..b71e047 100644
--- a/python/proto.go
+++ b/python/proto.go
@@ -34,7 +34,7 @@
// Proto generated python files have an unknown package name in the path, so package the entire output directory
// into a srcszip.
zipCmd := rule.Command().
- Tool(ctx.Config().HostToolPath(ctx, "soong_zip")).
+ BuiltTool(ctx, "soong_zip").
FlagWithOutput("-o ", srcsZipFile)
if pkgPath != "" {
zipCmd.FlagWithArg("-P ", pkgPath)
diff --git a/scripts/jar-wrapper.sh b/scripts/jar-wrapper.sh
index 71c1d90..b468041 100644
--- a/scripts/jar-wrapper.sh
+++ b/scripts/jar-wrapper.sh
@@ -48,11 +48,11 @@
exit 1
fi
-javaOpts=""
+declare -a javaOpts=()
while expr "x$1" : 'x-J' >/dev/null; do
- opt=`expr "$1" : '-J\(.*\)'`
- javaOpts="${javaOpts} -${opt}"
+ opt=`expr "$1" : '-J-\{0,1\}\(.*\)'`
+ javaOpts+=("-${opt}")
shift
done
-exec java ${javaOpts} -jar ${jardir}/${jarfile} "$@"
+exec java "${javaOpts[@]}" -jar ${jardir}/${jarfile} "$@"