Merge "Automatically enable abi checker for the stub libs"
diff --git a/Android.bp b/Android.bp
index 5c76f5a..d63921a 100644
--- a/Android.bp
+++ b/Android.bp
@@ -326,6 +326,50 @@
}
bootstrap_go_package {
+ name: "soong-rust-config",
+ pkgPath: "android/soong/rust/config",
+ deps: [
+ "soong-android",
+ "soong-cc-config",
+ ],
+ srcs: [
+ "rust/config/global.go",
+ "rust/config/toolchain.go",
+ "rust/config/x86_linux_host.go",
+ "rust/config/x86_64_device.go",
+ ],
+}
+
+bootstrap_go_package {
+ name: "soong-rust",
+ pkgPath: "android/soong/rust",
+ deps: [
+ "soong",
+ "soong-android",
+ "soong-cc",
+ "soong-rust-config",
+ ],
+ srcs: [
+ "rust/androidmk.go",
+ "rust/compiler.go",
+ "rust/binary.go",
+ "rust/builder.go",
+ "rust/library.go",
+ "rust/prebuilt.go",
+ "rust/proc_macro.go",
+ "rust/rust.go",
+ "rust/testing.go",
+ ],
+ testSrcs: [
+ "rust/binary_test.go",
+ "rust/compiler_test.go",
+ "rust/library_test.go",
+ "rust/rust_test.go",
+ ],
+ pluginFor: ["soong_build"],
+}
+
+bootstrap_go_package {
name: "soong-python",
pkgPath: "android/soong/python",
deps: [
diff --git a/android/androidmk.go b/android/androidmk.go
index 1f1bd70..124523f 100644
--- a/android/androidmk.go
+++ b/android/androidmk.go
@@ -79,12 +79,14 @@
header bytes.Buffer
footer bytes.Buffer
- AddCustomEntries func(name, prefix, moduleDir string, entries *AndroidMkEntries)
+ ExtraEntries []AndroidMkExtraEntriesFunc
EntryMap map[string][]string
entryOrder []string
}
+type AndroidMkExtraEntriesFunc func(entries *AndroidMkEntries)
+
func (a *AndroidMkEntries) SetString(name, value string) {
if _, ok := a.EntryMap[name]; !ok {
a.entryOrder = append(a.entryOrder, name)
@@ -246,9 +248,8 @@
prefix = "2ND_" + prefix
}
}
- blueprintDir := filepath.Dir(bpPath)
- if a.AddCustomEntries != nil {
- a.AddCustomEntries(name, prefix, blueprintDir, a)
+ for _, extra := range a.ExtraEntries {
+ extra(a)
}
// Write to footer.
diff --git a/android/module.go b/android/module.go
index 0ab9be7..dda526f 100644
--- a/android/module.go
+++ b/android/module.go
@@ -1038,6 +1038,13 @@
}
if m.Enabled() {
+ // ensure all direct android.Module deps are enabled
+ ctx.VisitDirectDepsBlueprint(func(bm blueprint.Module) {
+ if _, ok := bm.(Module); ok {
+ ctx.validateAndroidModule(bm, ctx.baseModuleContext.strictVisitDeps)
+ }
+ })
+
notice := proptools.StringDefault(m.commonProperties.Notice, "NOTICE")
if module := SrcIsModule(notice); module != "" {
m.noticeFile = ctx.ExpandOptionalSource(¬ice, "notice")
diff --git a/android/module_test.go b/android/module_test.go
index c790a68..6dca29f 100644
--- a/android/module_test.go
+++ b/android/module_test.go
@@ -14,7 +14,9 @@
package android
-import "testing"
+import (
+ "testing"
+)
func TestSrcIsModule(t *testing.T) {
type args struct {
@@ -139,3 +141,55 @@
})
}
}
+
+type depsModule struct {
+ ModuleBase
+ props struct {
+ Deps []string
+ }
+}
+
+func (m *depsModule) GenerateAndroidBuildActions(ctx ModuleContext) {
+}
+
+func (m *depsModule) DepsMutator(ctx BottomUpMutatorContext) {
+ ctx.AddDependency(ctx.Module(), nil, m.props.Deps...)
+}
+
+func depsModuleFactory() Module {
+ m := &depsModule{}
+ m.AddProperties(&m.props)
+ InitAndroidModule(m)
+ return m
+}
+
+func TestErrorDependsOnDisabledModule(t *testing.T) {
+ ctx := NewTestContext()
+ ctx.RegisterModuleType("deps", ModuleFactoryAdaptor(depsModuleFactory))
+
+ bp := `
+ deps {
+ name: "foo",
+ deps: ["bar"],
+ }
+ deps {
+ name: "bar",
+ enabled: false,
+ }
+ `
+
+ mockFS := map[string][]byte{
+ "Android.bp": []byte(bp),
+ }
+
+ ctx.MockFileSystem(mockFS)
+
+ ctx.Register()
+
+ config := TestConfig(buildDir, nil)
+
+ _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
+ FailIfErrored(t, errs)
+ _, errs = ctx.PrepareBuildActions(config)
+ FailIfNoMatchingErrors(t, `module "foo": depends on disabled module "bar"`, errs)
+}
diff --git a/android/prebuilt_etc.go b/android/prebuilt_etc.go
index 9722a25..d29ed16 100644
--- a/android/prebuilt_etc.go
+++ b/android/prebuilt_etc.go
@@ -155,16 +155,18 @@
Class: "ETC",
SubName: nameSuffix,
OutputFile: OptionalPathForPath(p.outputFilePath),
- AddCustomEntries: func(name, prefix, moduleDir string, entries *AndroidMkEntries) {
- entries.SetString("LOCAL_MODULE_TAGS", "optional")
- entries.SetString("LOCAL_MODULE_PATH", "$(OUT_DIR)/"+p.installDirPath.RelPathString())
- entries.SetString("LOCAL_INSTALLED_MODULE_STEM", p.outputFilePath.Base())
- entries.SetString("LOCAL_UNINSTALLABLE_MODULE", strconv.FormatBool(!p.Installable()))
- if p.additionalDependencies != nil {
- for _, path := range *p.additionalDependencies {
- entries.SetString("LOCAL_ADDITIONAL_DEPENDENCIES", path.String())
+ ExtraEntries: []AndroidMkExtraEntriesFunc{
+ func(entries *AndroidMkEntries) {
+ entries.SetString("LOCAL_MODULE_TAGS", "optional")
+ entries.SetString("LOCAL_MODULE_PATH", "$(OUT_DIR)/"+p.installDirPath.RelPathString())
+ entries.SetString("LOCAL_INSTALLED_MODULE_STEM", p.outputFilePath.Base())
+ entries.SetString("LOCAL_UNINSTALLABLE_MODULE", strconv.FormatBool(!p.Installable()))
+ if p.additionalDependencies != nil {
+ for _, path := range *p.additionalDependencies {
+ entries.SetString("LOCAL_ADDITIONAL_DEPENDENCIES", path.String())
+ }
}
- }
+ },
},
}
}
diff --git a/android/sh_binary.go b/android/sh_binary.go
index 2855aa0..ba0c8be 100644
--- a/android/sh_binary.go
+++ b/android/sh_binary.go
@@ -133,8 +133,10 @@
Class: "EXECUTABLES",
OutputFile: OptionalPathForPath(s.outputFilePath),
Include: "$(BUILD_SYSTEM)/soong_cc_prebuilt.mk",
- AddCustomEntries: func(name, prefix, moduleDir string, entries *AndroidMkEntries) {
- s.customAndroidMkEntries(entries)
+ ExtraEntries: []AndroidMkExtraEntriesFunc{
+ func(entries *AndroidMkEntries) {
+ s.customAndroidMkEntries(entries)
+ },
},
}
}
@@ -156,20 +158,22 @@
Class: "NATIVE_TESTS",
OutputFile: OptionalPathForPath(s.outputFilePath),
Include: "$(BUILD_SYSTEM)/soong_cc_prebuilt.mk",
- AddCustomEntries: func(name, prefix, moduleDir string, entries *AndroidMkEntries) {
- s.customAndroidMkEntries(entries)
+ ExtraEntries: []AndroidMkExtraEntriesFunc{
+ func(entries *AndroidMkEntries) {
+ s.customAndroidMkEntries(entries)
- entries.AddStrings("LOCAL_COMPATIBILITY_SUITE", s.testProperties.Test_suites...)
- entries.SetString("LOCAL_TEST_CONFIG", String(s.testProperties.Test_config))
- for _, d := range s.data {
- rel := d.Rel()
- path := d.String()
- if !strings.HasSuffix(path, rel) {
- panic(fmt.Errorf("path %q does not end with %q", path, rel))
+ entries.AddStrings("LOCAL_COMPATIBILITY_SUITE", s.testProperties.Test_suites...)
+ entries.SetString("LOCAL_TEST_CONFIG", String(s.testProperties.Test_config))
+ for _, d := range s.data {
+ rel := d.Rel()
+ path := d.String()
+ if !strings.HasSuffix(path, rel) {
+ panic(fmt.Errorf("path %q does not end with %q", path, rel))
+ }
+ path = strings.TrimSuffix(path, rel)
+ entries.AddStrings("LOCAL_TEST_DATA", path+":"+rel)
}
- path = strings.TrimSuffix(path, rel)
- entries.AddStrings("LOCAL_TEST_DATA", path+":"+rel)
- }
+ },
},
}
}
diff --git a/android/variable.go b/android/variable.go
index 0931db8..3f2b9e9 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -115,6 +115,10 @@
Static_libs []string
Srcs []string
}
+
+ Flatten_apex struct {
+ Enabled *bool
+ }
} `android:"arch_variant"`
}
diff --git a/apex/apex.go b/apex/apex.go
index 2ce0d01..70e685a 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -435,6 +435,7 @@
bundleModuleFile android.WritablePath
outputFiles map[apexPackaging]android.WritablePath
+ flattenedOutput android.OutputPath
installDir android.OutputPath
prebuiltFileToDelete string
@@ -639,6 +640,13 @@
} else {
return nil, nil
}
+ case ".flattened":
+ if a.flattened {
+ flattenedApexPath := a.flattenedOutput
+ return android.Paths{flattenedApexPath}, nil
+ } else {
+ return nil, nil
+ }
default:
return nil, fmt.Errorf("unsupported module reference tag %q", tag)
}
@@ -784,6 +792,15 @@
return
}
+// Context "decorator", overriding the InstallBypassMake method to always reply `true`.
+type flattenedApexContext struct {
+ android.ModuleContext
+}
+
+func (c *flattenedApexContext) InstallBypassMake() bool {
+ return true
+}
+
func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) {
filesInfo := []apexFile{}
@@ -1051,6 +1068,14 @@
},
})
+ // Temporarily wrap the original `ctx` into a `flattenedApexContext` to have it
+ // reply true to `InstallBypassMake()` (thus making the call
+ // `android.PathForModuleInstall` below use `android.pathForInstallInMakeDir`
+ // instead of `android.PathForOutput`) to return the correct path to the flattened
+ // APEX (as its contents is installed by Make, not Soong).
+ factx := flattenedApexContext{ctx}
+ a.flattenedOutput = android.PathForModuleInstall(&factx, "apex", factx.ModuleName())
+
if a.apexTypes.zip() {
a.buildUnflattenedApex(ctx, zipApex)
}
@@ -1213,6 +1238,13 @@
optFlags = append(optFlags, "--assets_dir "+filepath.Dir(noticeFile.String()))
}
+ if !ctx.Config().UnbundledBuild() && a.installable() {
+ // Apexes which are supposed to be installed in builtin dirs(/system, etc)
+ // don't need hashtree for activation. Therefore, by removing hashtree from
+ // apex bundle (filesystem image in it, to be specific), we can save storage.
+ optFlags = append(optFlags, "--no_hashtree")
+ }
+
ctx.Build(pctx, android.BuildParams{
Rule: apexRule,
Implicits: implicitInputs,
@@ -1444,6 +1476,8 @@
fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES :=", strings.Join(moduleNames, " "))
}
fmt.Fprintln(w, "include $(BUILD_PHONY_PACKAGE)")
+ fmt.Fprintln(w, "$(LOCAL_INSTALLED_MODULE): .KATI_IMPLICIT_OUTPUTS :=", a.flattenedOutput.String())
+
} else {
// zip-apex is the less common type so have the name refer to the image-apex
// only and use {name}.zip if you want the zip-apex
@@ -1680,11 +1714,13 @@
Class: "ETC",
OutputFile: android.OptionalPathForPath(p.inputApex),
Include: "$(BUILD_PREBUILT)",
- AddCustomEntries: func(name, prefix, moduleDir string, entries *android.AndroidMkEntries) {
- entries.SetString("LOCAL_MODULE_PATH", filepath.Join("$(OUT_DIR)", p.installDir.RelPathString()))
- entries.SetString("LOCAL_MODULE_STEM", p.installFilename)
- entries.SetBoolIfTrue("LOCAL_UNINSTALLABLE_MODULE", !p.installable())
- entries.AddStrings("LOCAL_OVERRIDES_PACKAGES", p.properties.Overrides...)
+ ExtraEntries: []android.AndroidMkExtraEntriesFunc{
+ func(entries *android.AndroidMkEntries) {
+ entries.SetString("LOCAL_MODULE_PATH", filepath.Join("$(OUT_DIR)", p.installDir.RelPathString()))
+ entries.SetString("LOCAL_MODULE_STEM", p.installFilename)
+ entries.SetBoolIfTrue("LOCAL_UNINSTALLABLE_MODULE", !p.installable())
+ entries.AddStrings("LOCAL_OVERRIDES_PACKAGES", p.properties.Overrides...)
+ },
},
}
}
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 0845b20..8de4cef 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -1932,6 +1932,51 @@
}
+func TestErrorsIfDepsAreNotEnabled(t *testing.T) {
+ testApexError(t, `module "myapex" .* depends on disabled module "libfoo"`, `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ native_shared_libs: ["libfoo"],
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+
+ cc_library {
+ name: "libfoo",
+ stl: "none",
+ system_shared_libs: [],
+ enabled: false,
+ }
+ `)
+ testApexError(t, `module "myapex" .* depends on disabled module "myjar"`, `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ java_libs: ["myjar"],
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+
+ java_library {
+ name: "myjar",
+ srcs: ["foo/bar/MyClass.java"],
+ sdk_version: "none",
+ system_modules: "none",
+ compile_dex: true,
+ enabled: false,
+ }
+ `)
+}
+
func TestMain(m *testing.M) {
run := func() int {
setUp()
diff --git a/cc/builder.go b/cc/builder.go
index 00dc742..554706c 100644
--- a/cc/builder.go
+++ b/cc/builder.go
@@ -271,6 +271,8 @@
sAbiDump bool
emitXrefs bool
+ assemblerWithCpp bool
+
systemIncludeFlags string
groupStaticLibs bool
@@ -428,7 +430,9 @@
switch srcFile.Ext() {
case ".s":
- rule = ccNoDeps
+ if !flags.assemblerWithCpp {
+ rule = ccNoDeps
+ }
fallthrough
case ".S":
ccCmd = "clang"
diff --git a/cc/cc.go b/cc/cc.go
index 0245c6a..2ff3434 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -174,7 +174,8 @@
CFlagsDeps android.Paths // Files depended on by compiler flags
LdFlagsDeps android.Paths // Files depended on by linker flags
- GroupStaticLibs bool
+ AssemblerWithCpp bool
+ GroupStaticLibs bool
proto android.ProtoFlags
protoC bool // Whether to use C instead of C++
@@ -391,6 +392,14 @@
return ok && ccDepTag == testPerSrcDepTag
}
+func SharedDepTag() dependencyTag {
+ return sharedDepTag
+}
+
+func StaticDepTag() dependencyTag {
+ return staticDepTag
+}
+
// Module contains the properties and members used by all C/C++ module types, and implements
// the blueprint.Module interface. It delegates to compiler, linker, and installer interfaces
// to construct the output file. Behavior can be customized with a Customizer interface
@@ -1053,6 +1062,9 @@
if c.sabi != nil {
flags = c.sabi.flags(ctx, flags)
}
+
+ flags.AssemblerWithCpp = inList("-xassembler-with-cpp", flags.AsFlags)
+
// Optimization to reduce size of build.ninja
// Replace the long list of flags for each file with a module-local variable
ctx.Variable(pctx, "cflags", strings.Join(flags.CFlags, " "))
diff --git a/cc/cc_test.go b/cc/cc_test.go
index 52234a8..c9eb421 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -2264,6 +2264,24 @@
}
}
+func TestErrorsIfAModuleDependsOnDisabled(t *testing.T) {
+ testCcError(t, `module "libA" .* depends on disabled module "libB"`, `
+ cc_library {
+ name: "libA",
+ srcs: ["foo.c"],
+ shared_libs: ["libB"],
+ stl: "none",
+ }
+
+ cc_library {
+ name: "libB",
+ srcs: ["foo.c"],
+ enabled: false,
+ stl: "none",
+ }
+ `)
+}
+
// Simple smoke test for the cc_fuzz target that ensures the rule compiles
// correctly.
func TestFuzzTarget(t *testing.T) {
diff --git a/cc/config/clang.go b/cc/config/clang.go
index 47b60e7..0647584 100644
--- a/cc/config/clang.go
+++ b/cc/config/clang.go
@@ -169,7 +169,6 @@
"-Wno-tautological-constant-compare",
"-Wno-tautological-type-limit-compare",
"-Wno-tautological-unsigned-enum-zero-compare",
- "-Wno-tautological-unsigned-zero-compare",
// http://b/72330874 Disable -Wenum-compare until the instances detected by this new
// warning are fixed.
diff --git a/cc/config/global.go b/cc/config/global.go
index 2150abf..d873494 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -46,6 +46,8 @@
"-g",
"-fno-strict-aliasing",
+
+ "-Werror=date-time",
}
commonGlobalConlyflags = []string{}
@@ -67,7 +69,6 @@
"-Werror=non-virtual-dtor",
"-Werror=address",
"-Werror=sequence-point",
- "-Werror=date-time",
"-Werror=format-security",
}
diff --git a/cc/sanitize.go b/cc/sanitize.go
index 192b8d9..72c0ecb 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -475,6 +475,12 @@
// TODO(b/133876586): Experimental PM breaks sanitizer coverage.
_, flags.CFlags = removeFromList("-fexperimental-new-pass-manager", flags.CFlags)
flags.CFlags = append(flags.CFlags, "-fno-experimental-new-pass-manager")
+
+ // Disable fortify for fuzzing builds. Generally, we'll be building with
+ // UBSan or ASan here and the fortify checks pollute the stack traces.
+ _, flags.CFlags = removeFromList("-D_FORTIFY_SOURCE=1", flags.CFlags)
+ _, flags.CFlags = removeFromList("-D_FORTIFY_SOURCE=2", flags.CFlags)
+ flags.CFlags = append(flags.CFlags, "-U_FORTIFY_SOURCE")
}
if Bool(sanitize.Properties.Sanitize.Cfi) {
diff --git a/cc/test.go b/cc/test.go
index aff8ba5..1a0d44f 100644
--- a/cc/test.go
+++ b/cc/test.go
@@ -68,6 +68,9 @@
// Add RootTargetPreparer to auto generated test config. This guarantees the test to run
// with root permission.
Require_root *bool
+
+ // Add RunCommandTargetPreparer to stop framework before the test and start it after the test.
+ Disable_framework *bool
}
func init() {
@@ -313,7 +316,13 @@
test.data = android.PathsForModuleSrc(ctx, test.Properties.Data)
var configs []tradefed.Config
if Bool(test.Properties.Require_root) {
- configs = append(configs, tradefed.Preparer{"com.android.tradefed.targetprep.RootTargetPreparer"})
+ configs = append(configs, tradefed.Preparer{"com.android.tradefed.targetprep.RootTargetPreparer", nil})
+ }
+ if Bool(test.Properties.Disable_framework) {
+ var options []tradefed.Option
+ options = append(options, tradefed.Option{"run-command", "stop"})
+ options = append(options, tradefed.Option{"teardown-command", "start"})
+ configs = append(configs, tradefed.Preparer{"com.android.tradefed.targetprep.RunCommandTargetPreparer", options})
}
if Bool(test.testDecorator.Properties.Isolated) {
configs = append(configs, tradefed.Option{"not-shardable", "true"})
@@ -448,7 +457,7 @@
benchmark.data = android.PathsForModuleSrc(ctx, benchmark.Properties.Data)
var configs []tradefed.Config
if Bool(benchmark.Properties.Require_root) {
- configs = append(configs, tradefed.Preparer{"com.android.tradefed.targetprep.RootTargetPreparer"})
+ configs = append(configs, tradefed.Preparer{"com.android.tradefed.targetprep.RootTargetPreparer", nil})
}
benchmark.testConfig = tradefed.AutoGenNativeBenchmarkTestConfig(ctx, benchmark.Properties.Test_config,
benchmark.Properties.Test_config_template, benchmark.Properties.Test_suites, configs)
diff --git a/cc/util.go b/cc/util.go
index fb6338a..7b8ad18 100644
--- a/cc/util.go
+++ b/cc/util.go
@@ -79,7 +79,8 @@
systemIncludeFlags: strings.Join(in.SystemIncludeFlags, " "),
- groupStaticLibs: in.GroupStaticLibs,
+ assemblerWithCpp: in.AssemblerWithCpp,
+ groupStaticLibs: in.GroupStaticLibs,
proto: in.proto,
protoC: in.protoC,
diff --git a/java/androidmk.go b/java/androidmk.go
index c00e070..0e8e422 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -122,6 +122,15 @@
}
}
+func testSuiteComponentEntries(entries *android.AndroidMkEntries, test_suites []string) {
+ entries.SetString("LOCAL_MODULE_TAGS", "tests")
+ if len(test_suites) > 0 {
+ entries.AddStrings("LOCAL_COMPATIBILITY_SUITE", test_suites...)
+ } else {
+ entries.SetString("LOCAL_COMPATIBILITY_SUITE", "null-suite")
+ }
+}
+
func (j *Test) AndroidMk() android.AndroidMkData {
data := j.Library.AndroidMk()
data.Extra = append(data.Extra, func(w io.Writer, outputFile android.Path) {
@@ -614,22 +623,33 @@
Class: "APPS",
OutputFile: android.OptionalPathForPath(a.outputFile),
Include: "$(BUILD_SYSTEM)/soong_app_prebuilt.mk",
- AddCustomEntries: func(name, prefix, moduleDir string, entries *android.AndroidMkEntries) {
- entries.SetBoolIfTrue("LOCAL_PRIVILEGED_MODULE", Bool(a.properties.Privileged))
- if a.certificate != nil {
- entries.SetString("LOCAL_CERTIFICATE", a.certificate.Pem.String())
- } else {
- entries.SetString("LOCAL_CERTIFICATE", "PRESIGNED")
- }
- entries.AddStrings("LOCAL_OVERRIDES_PACKAGES", a.properties.Overrides...)
- if len(a.dexpreopter.builtInstalled) > 0 {
- entries.SetString("LOCAL_SOONG_BUILT_INSTALLED", a.dexpreopter.builtInstalled)
- }
- entries.AddStrings("LOCAL_INSTALLED_MODULE_STEM", a.installPath.Rel())
+ ExtraEntries: []android.AndroidMkExtraEntriesFunc{
+ func(entries *android.AndroidMkEntries) {
+ entries.SetBoolIfTrue("LOCAL_PRIVILEGED_MODULE", Bool(a.properties.Privileged))
+ if a.certificate != nil {
+ entries.SetString("LOCAL_CERTIFICATE", a.certificate.Pem.String())
+ } else {
+ entries.SetString("LOCAL_CERTIFICATE", "PRESIGNED")
+ }
+ entries.AddStrings("LOCAL_OVERRIDES_PACKAGES", a.properties.Overrides...)
+ if len(a.dexpreopter.builtInstalled) > 0 {
+ entries.SetString("LOCAL_SOONG_BUILT_INSTALLED", a.dexpreopter.builtInstalled)
+ }
+ entries.AddStrings("LOCAL_INSTALLED_MODULE_STEM", a.installPath.Rel())
+ },
},
}
}
+func (a *AndroidTestImport) AndroidMkEntries() android.AndroidMkEntries {
+ entries := a.AndroidAppImport.AndroidMkEntries()
+ entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
+ testSuiteComponentEntries(entries, a.testProperties.Test_suites)
+ androidMkEntriesWriteTestData(a.data, entries)
+ })
+ return entries
+}
+
func androidMkWriteTestData(data android.Paths, ret *android.AndroidMkData) {
var testFiles []string
for _, d := range data {
@@ -641,3 +661,11 @@
})
}
}
+
+func androidMkEntriesWriteTestData(data android.Paths, entries *android.AndroidMkEntries) {
+ var testFiles []string
+ for _, d := range data {
+ testFiles = append(testFiles, d.String()+":"+d.Rel())
+ }
+ entries.AddStrings("LOCAL_COMPATIBILITY_SUPPORT_FILES", testFiles...)
+}
diff --git a/java/app.go b/java/app.go
index f5a5da0..7df4358 100644
--- a/java/app.go
+++ b/java/app.go
@@ -39,6 +39,7 @@
android.RegisterModuleType("android_app_certificate", AndroidAppCertificateFactory)
android.RegisterModuleType("override_android_app", OverrideAndroidAppModuleFactory)
android.RegisterModuleType("android_app_import", AndroidAppImportFactory)
+ android.RegisterModuleType("android_test_import", AndroidTestImportFactory)
initAndroidAppImportVariantGroupTypes()
}
@@ -866,6 +867,10 @@
}
func (a *AndroidAppImport) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ a.generateAndroidBuildActions(ctx)
+}
+
+func (a *AndroidAppImport) generateAndroidBuildActions(ctx android.ModuleContext) {
numCertPropsSet := 0
if String(a.properties.Certificate) != "" {
numCertPropsSet++
@@ -1024,6 +1029,39 @@
return module
}
+type AndroidTestImport struct {
+ AndroidAppImport
+
+ testProperties testProperties
+
+ data android.Paths
+}
+
+func (a *AndroidTestImport) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ a.generateAndroidBuildActions(ctx)
+
+ a.data = android.PathsForModuleSrc(ctx, a.testProperties.Data)
+}
+
+// android_test_import imports a prebuilt test apk with additional processing specified in the
+// module. DPI or arch variant configurations can be made as with android_app_import.
+func AndroidTestImportFactory() android.Module {
+ module := &AndroidTestImport{}
+ module.AddProperties(&module.properties)
+ module.AddProperties(&module.dexpreoptProperties)
+ module.AddProperties(&module.usesLibrary.usesLibraryProperties)
+ module.AddProperties(&module.testProperties)
+ module.populateAllVariantStructs()
+ android.AddLoadHook(module, func(ctx android.LoadHookContext) {
+ module.processVariants(ctx)
+ })
+
+ InitJavaModule(module, android.DeviceSupported)
+ android.InitSingleSourcePrebuiltModule(module, &module.properties, "Apk")
+
+ return module
+}
+
type UsesLibraryProperties struct {
// A list of shared library modules that will be listed in uses-library tags in the AndroidManifest.xml file.
Uses_libs []string
diff --git a/java/app_test.go b/java/app_test.go
index be1ff29..f2aaec3 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -1381,6 +1381,34 @@
}
}
+func TestAndroidTestImport(t *testing.T) {
+ ctx, config := testJava(t, `
+ android_test_import {
+ name: "foo",
+ apk: "prebuilts/apk/app.apk",
+ presigned: true,
+ data: [
+ "testdata/data",
+ ],
+ }
+ `)
+
+ test := ctx.ModuleForTests("foo", "android_common").Module().(*AndroidTestImport)
+
+ // Check android mks.
+ entries := android.AndroidMkEntriesForTest(t, config, "", test)
+ expected := []string{"tests"}
+ actual := entries.EntryMap["LOCAL_MODULE_TAGS"]
+ if !reflect.DeepEqual(expected, actual) {
+ t.Errorf("Unexpected module tags - expected: %q, actual: %q", expected, actual)
+ }
+ expected = []string{"testdata/data:testdata/data"}
+ actual = entries.EntryMap["LOCAL_COMPATIBILITY_SUPPORT_FILES"]
+ if !reflect.DeepEqual(expected, actual) {
+ t.Errorf("Unexpected test data - expected: %q, actual: %q", expected, actual)
+ }
+}
+
func TestStl(t *testing.T) {
ctx, _ := testJava(t, cc.GatherRequiredDepsForTest(android.Android)+`
cc_library {
diff --git a/java/java.go b/java/java.go
index 2193a2b..f278d5e 100644
--- a/java/java.go
+++ b/java/java.go
@@ -907,7 +907,7 @@
ctx.PropertyErrorf("sdk_version", "%s", err)
}
if javaVersion != "" {
- ret = javaVersion
+ ret = normalizeJavaVersion(ctx, javaVersion)
} else if ctx.Device() && sdk <= 23 {
ret = "1.7"
} else if ctx.Device() && sdk <= 29 || !ctx.Config().TargetOpenJDK9() {
@@ -926,6 +926,25 @@
return ret
}
+func normalizeJavaVersion(ctx android.ModuleContext, javaVersion string) string {
+ switch javaVersion {
+ case "1.6", "6":
+ return "1.6"
+ case "1.7", "7":
+ return "1.7"
+ case "1.8", "8":
+ return "1.8"
+ case "1.9", "9":
+ return "1.9"
+ case "10", "11":
+ ctx.PropertyErrorf("java_version", "Java language levels above 9 are not supported")
+ return "unsupported"
+ default:
+ ctx.PropertyErrorf("java_version", "Unrecognized Java language level")
+ return "unrecognized"
+ }
+}
+
func (j *Module) collectBuilderFlags(ctx android.ModuleContext, deps deps) javaBuilderFlags {
var flags javaBuilderFlags
diff --git a/java/java_test.go b/java/java_test.go
index 5fcdf96..c55e325 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -66,6 +66,7 @@
ctx.RegisterModuleType("android_library", android.ModuleFactoryAdaptor(AndroidLibraryFactory))
ctx.RegisterModuleType("android_test", android.ModuleFactoryAdaptor(AndroidTestFactory))
ctx.RegisterModuleType("android_test_helper_app", android.ModuleFactoryAdaptor(AndroidTestHelperAppFactory))
+ ctx.RegisterModuleType("android_test_import", android.ModuleFactoryAdaptor(AndroidTestImportFactory))
ctx.RegisterModuleType("java_binary", android.ModuleFactoryAdaptor(BinaryFactory))
ctx.RegisterModuleType("java_binary_host", android.ModuleFactoryAdaptor(BinaryHostFactory))
ctx.RegisterModuleType("java_device_for_host", android.ModuleFactoryAdaptor(DeviceForHostFactory))
@@ -200,6 +201,8 @@
"cert/new_cert.x509.pem": nil,
"cert/new_cert.pk8": nil,
+
+ "testdata/data": nil,
}
for k, v := range fs {
diff --git a/java/platform_compat_config.go b/java/platform_compat_config.go
index 792edf3..3d46077 100644
--- a/java/platform_compat_config.go
+++ b/java/platform_compat_config.go
@@ -23,8 +23,7 @@
}
type platformCompatConfigProperties struct {
- Src *string `android:"path"`
- Prefix *string
+ Src *string `android:"path"`
}
type platformCompatConfig struct {
@@ -38,13 +37,13 @@
func (p *platformCompatConfig) GenerateAndroidBuildActions(ctx android.ModuleContext) {
rule := android.NewRuleBuilder()
- configFileName := String(p.properties.Prefix) + "_platform_compat_config.xml"
+ configFileName := p.Name() + ".xml"
p.configFile = android.PathForModuleOut(ctx, configFileName).OutputPath
path := android.PathForModuleSrc(ctx, String(p.properties.Src))
// Use the empty config if the compat config file idoesn't exist (can happen if @ChangeId
// annotation is not used).
- emptyConfig := `<?xml version="1.0" encoding="UTF-8" standalone="no"?><config/>`
+ emptyConfig := `'<?xml version="1.0" encoding="UTF-8" standalone="no"?><config/>'`
configPath := `compat/compat_config.xml`
rule.Command().
@@ -61,13 +60,13 @@
Text(configPath).
Text(`>`).
Output(p.configFile).
- Text(`; else echo '`).
+ Text(`; else echo `).
Text(emptyConfig).
- Text(`' >`).
+ Text(`>`).
Output(p.configFile).
Text(`; fi`)
- p.installDirPath = android.PathForModuleInstall(ctx, "etc", "sysconfig")
+ p.installDirPath = android.PathForModuleInstall(ctx, "etc", "compatconfig")
rule.Build(pctx, ctx, configFileName, "Extract compat/compat_config.xml and install it")
}
@@ -77,9 +76,11 @@
Class: "ETC",
OutputFile: android.OptionalPathForPath(p.configFile),
Include: "$(BUILD_PREBUILT)",
- AddCustomEntries: func(name, prefix, moduleDir string, entries *android.AndroidMkEntries) {
- entries.SetString("LOCAL_MODULE_PATH", "$(OUT_DIR)/"+p.installDirPath.RelPathString())
- entries.SetString("LOCAL_INSTALLED_MODULE_STEM", p.configFile.Base())
+ ExtraEntries: []android.AndroidMkExtraEntriesFunc{
+ func(entries *android.AndroidMkEntries) {
+ entries.SetString("LOCAL_MODULE_PATH", "$(OUT_DIR)/"+p.installDirPath.RelPathString())
+ entries.SetString("LOCAL_INSTALLED_MODULE_STEM", p.configFile.Base())
+ },
},
}
}
diff --git a/rust/androidmk.go b/rust/androidmk.go
new file mode 100644
index 0000000..c9056e1
--- /dev/null
+++ b/rust/androidmk.go
@@ -0,0 +1,155 @@
+// Copyright 2019 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 (
+ "fmt"
+ "io"
+ "path/filepath"
+ "regexp"
+ "strings"
+
+ "android/soong/android"
+)
+
+type AndroidMkContext interface {
+ Name() string
+ Target() android.Target
+ subAndroidMk(*android.AndroidMkData, interface{})
+}
+
+type subAndroidMkProvider interface {
+ AndroidMk(AndroidMkContext, *android.AndroidMkData)
+}
+
+func (mod *Module) subAndroidMk(data *android.AndroidMkData, obj interface{}) {
+ if mod.subAndroidMkOnce == nil {
+ mod.subAndroidMkOnce = make(map[subAndroidMkProvider]bool)
+ }
+ if androidmk, ok := obj.(subAndroidMkProvider); ok {
+ if !mod.subAndroidMkOnce[androidmk] {
+ mod.subAndroidMkOnce[androidmk] = true
+ androidmk.AndroidMk(mod, data)
+ }
+ }
+}
+
+func (mod *Module) AndroidMk() android.AndroidMkData {
+ ret := android.AndroidMkData{
+ OutputFile: mod.outputFile,
+ Include: "$(BUILD_SYSTEM)/soong_rust_prebuilt.mk",
+ Extra: []android.AndroidMkExtraFunc{
+ func(w io.Writer, outputFile android.Path) {
+ if len(mod.Properties.AndroidMkRlibs) > 0 {
+ fmt.Fprintln(w, "LOCAL_RLIB_LIBRARIES := "+strings.Join(mod.Properties.AndroidMkRlibs, " "))
+ }
+ if len(mod.Properties.AndroidMkDylibs) > 0 {
+ fmt.Fprintln(w, "LOCAL_DYLIB_LIBRARIES := "+strings.Join(mod.Properties.AndroidMkDylibs, " "))
+ }
+ if len(mod.Properties.AndroidMkProcMacroLibs) > 0 {
+ fmt.Fprintln(w, "LOCAL_PROC_MACRO_LIBRARIES := "+strings.Join(mod.Properties.AndroidMkProcMacroLibs, " "))
+ }
+ if len(mod.Properties.AndroidMkSharedLibs) > 0 {
+ fmt.Fprintln(w, "LOCAL_SHARED_LIBRARIES := "+strings.Join(mod.Properties.AndroidMkSharedLibs, " "))
+ }
+ if len(mod.Properties.AndroidMkStaticLibs) > 0 {
+ fmt.Fprintln(w, "LOCAL_STATIC_LIBRARIES := "+strings.Join(mod.Properties.AndroidMkStaticLibs, " "))
+ }
+ },
+ },
+ }
+
+ mod.subAndroidMk(&ret, mod.compiler)
+
+ return ret
+}
+
+func (binary *binaryDecorator) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkData) {
+ ctx.subAndroidMk(ret, binary.baseCompiler)
+
+ ret.Class = "EXECUTABLES"
+ ret.DistFile = binary.distFile
+ ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) {
+ fmt.Fprintln(w, "LOCAL_SOONG_UNSTRIPPED_BINARY :=", binary.unstrippedOutputFile.String())
+ })
+}
+
+func (library *libraryDecorator) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkData) {
+ ctx.subAndroidMk(ret, library.baseCompiler)
+
+ if library.rlib() {
+ ret.Class = "RLIB_LIBRARIES"
+ } else if library.dylib() {
+ ret.Class = "DYLIB_LIBRARIES"
+ }
+ ret.DistFile = library.distFile
+ ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) {
+ if !library.rlib() {
+ fmt.Fprintln(w, "LOCAL_SOONG_UNSTRIPPED_BINARY :=", library.unstrippedOutputFile.String())
+ }
+ })
+}
+
+func (procMacro *procMacroDecorator) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkData) {
+ ctx.subAndroidMk(ret, procMacro.baseCompiler)
+
+ ret.Class = "PROC_MACRO_LIBRARIES"
+ ret.DistFile = procMacro.distFile
+
+}
+
+func (compiler *baseCompiler) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkData) {
+ // Soong installation is only supported for host modules. Have Make
+ // installation trigger Soong installation.
+ if ctx.Target().Os.Class == android.Host {
+ ret.OutputFile = android.OptionalPathForPath(compiler.path)
+ }
+ ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) {
+ path := compiler.path.RelPathString()
+ dir, file := filepath.Split(path)
+ stem, suffix, _ := splitFileExt(file)
+ fmt.Fprintln(w, "LOCAL_MODULE_SUFFIX := "+suffix)
+ fmt.Fprintln(w, "LOCAL_MODULE_PATH := $(OUT_DIR)/"+filepath.Clean(dir))
+ fmt.Fprintln(w, "LOCAL_MODULE_STEM := "+stem)
+ })
+}
+
+//TODO: splitFileExt copied from cc/util.go; move this to android/util.go and refactor usages.
+
+// splitFileExt splits a file name into root, suffix and ext. root stands for the file name without
+// the file extension and the version number (e.g. "libexample"). suffix stands for the
+// concatenation of the file extension and the version number (e.g. ".so.1.0"). ext stands for the
+// file extension after the version numbers are trimmed (e.g. ".so").
+var shlibVersionPattern = regexp.MustCompile("(?:\\.\\d+(?:svn)?)+")
+
+func splitFileExt(name string) (string, string, string) {
+ // Extract and trim the shared lib version number if the file name ends with dot digits.
+ suffix := ""
+ matches := shlibVersionPattern.FindAllStringIndex(name, -1)
+ if len(matches) > 0 {
+ lastMatch := matches[len(matches)-1]
+ if lastMatch[1] == len(name) {
+ suffix = name[lastMatch[0]:lastMatch[1]]
+ name = name[0:lastMatch[0]]
+ }
+ }
+
+ // Extract the file name root and the file extension.
+ ext := filepath.Ext(name)
+ root := strings.TrimSuffix(name, ext)
+ suffix = ext + suffix
+
+ return root, suffix, ext
+}
diff --git a/rust/binary.go b/rust/binary.go
new file mode 100644
index 0000000..279c6f5
--- /dev/null
+++ b/rust/binary.go
@@ -0,0 +1,110 @@
+// Copyright 2019 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 (
+ "android/soong/android"
+ "android/soong/rust/config"
+)
+
+func init() {
+ android.RegisterModuleType("rust_binary", RustBinaryFactory)
+ android.RegisterModuleType("rust_binary_host", RustBinaryHostFactory)
+}
+
+type BinaryCompilerProperties struct {
+ // path to the main source file that contains the program entry point (e.g. src/main.rs)
+ Srcs []string `android:"path,arch_variant"`
+
+ // passes -C prefer-dynamic to rustc, which tells it to dynamically link the stdlib (assuming it has no dylib dependencies already)
+ Prefer_dynamic *bool
+}
+
+type binaryDecorator struct {
+ *baseCompiler
+
+ Properties BinaryCompilerProperties
+ distFile android.OptionalPath
+ unstrippedOutputFile android.Path
+}
+
+var _ compiler = (*binaryDecorator)(nil)
+
+// rust_binary produces a binary that is runnable on a device.
+func RustBinaryFactory() android.Module {
+ module, _ := NewRustBinary(android.HostAndDeviceSupported)
+ return module.Init()
+}
+
+func RustBinaryHostFactory() android.Module {
+ module, _ := NewRustBinary(android.HostSupported)
+ return module.Init()
+}
+
+func NewRustBinary(hod android.HostOrDeviceSupported) (*Module, *binaryDecorator) {
+ module := newModule(hod, android.MultilibFirst)
+
+ binary := &binaryDecorator{
+ baseCompiler: NewBaseCompiler("bin", ""),
+ }
+
+ module.compiler = binary
+
+ return module, binary
+}
+
+func (binary *binaryDecorator) preferDynamic() bool {
+ return Bool(binary.Properties.Prefer_dynamic)
+}
+
+func (binary *binaryDecorator) compilerFlags(ctx ModuleContext, flags Flags) Flags {
+ flags = binary.baseCompiler.compilerFlags(ctx, flags)
+ if binary.preferDynamic() {
+ flags.RustFlags = append(flags.RustFlags, "-C prefer-dynamic")
+ }
+ return flags
+}
+
+func (binary *binaryDecorator) compilerDeps(ctx DepsContext, deps Deps) Deps {
+ deps = binary.baseCompiler.compilerDeps(ctx, deps)
+
+ if binary.preferDynamic() || len(deps.Dylibs) > 0 {
+ for _, stdlib := range config.Stdlibs {
+ deps.Dylibs = append(deps.Dylibs, stdlib+"_"+ctx.toolchain().RustTriple())
+ }
+ }
+
+ return deps
+}
+
+func (binary *binaryDecorator) compilerProps() []interface{} {
+ return append(binary.baseCompiler.compilerProps(),
+ &binary.Properties)
+}
+
+func (binary *binaryDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) android.Path {
+ fileName := binary.getStem(ctx) + ctx.toolchain().ExecutableSuffix()
+
+ srcPath := srcPathFromModuleSrcs(ctx, binary.Properties.Srcs)
+
+ outputFile := android.PathForModuleOut(ctx, fileName)
+ binary.unstrippedOutputFile = outputFile
+
+ flags.RustFlags = append(flags.RustFlags, deps.depFlags...)
+
+ TransformSrcToBinary(ctx, srcPath, deps, flags, outputFile, deps.linkDirs)
+
+ return outputFile
+}
diff --git a/rust/binary_test.go b/rust/binary_test.go
new file mode 100644
index 0000000..cd41fcf
--- /dev/null
+++ b/rust/binary_test.go
@@ -0,0 +1,46 @@
+// Copyright 2019 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 (
+ "strings"
+ "testing"
+)
+
+// Test that the prefer_dynamic property is handled correctly.
+func TestPreferDynamicBinary(t *testing.T) {
+ ctx := testRust(t, `
+ rust_binary_host {
+ name: "fizz-buzz-dynamic",
+ srcs: ["foo.rs"],
+ prefer_dynamic: true,
+ }
+
+ rust_binary_host {
+ name: "fizz-buzz",
+ srcs: ["foo.rs"],
+ }`)
+
+ fizzBuzz := ctx.ModuleForTests("fizz-buzz", "linux_glibc_x86_64").Output("fizz-buzz")
+ fizzBuzzDynamic := ctx.ModuleForTests("fizz-buzz-dynamic", "linux_glibc_x86_64").Output("fizz-buzz-dynamic")
+
+ if !strings.Contains(fizzBuzzDynamic.Args["rustcFlags"], "prefer-dynamic") {
+ t.Errorf("missing prefer-dynamic flag, rustcFlags: %#v", fizzBuzzDynamic.Args["rustcFlags"])
+ }
+
+ if strings.Contains(fizzBuzz.Args["rustcFlags"], "prefer-dynamic") {
+ t.Errorf("unexpected prefer-dynamic flag, rustcFlags: %#v", fizzBuzz.Args["rustcFlags"])
+ }
+}
diff --git a/rust/builder.go b/rust/builder.go
new file mode 100644
index 0000000..64e387b
--- /dev/null
+++ b/rust/builder.go
@@ -0,0 +1,132 @@
+// Copyright 2019 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 (
+ "strings"
+
+ "github.com/google/blueprint"
+
+ "android/soong/android"
+)
+
+var (
+ _ = pctx.SourcePathVariable("rustcCmd", "${config.RustBin}/rustc")
+ rustc = pctx.AndroidStaticRule("rustc",
+ blueprint.RuleParams{
+ Command: "$rustcCmd " +
+ "-C linker=${config.RustLinker} " +
+ "-C link-args=\"${config.RustLinkerArgs} ${linkFlags}\" " +
+ "-o $out $in ${libFlags} $rustcFlags " +
+ "&& $rustcCmd --emit=dep-info -o $out.d $in ${libFlags} $rustcFlags",
+ CommandDeps: []string{"$rustcCmd"},
+ Depfile: "$out.d",
+ Deps: blueprint.DepsGCC, // Rustc deps-info writes out make compatible dep files: https://github.com/rust-lang/rust/issues/7633
+ },
+ "rustcFlags", "linkFlags", "libFlags")
+)
+
+func init() {
+
+}
+
+func TransformSrcToBinary(ctx android.ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags, outputFile android.WritablePath, includeDirs []string) {
+ targetTriple := ctx.(ModuleContext).toolchain().RustTriple()
+
+ transformSrctoCrate(ctx, mainSrc, deps.RLibs, deps.DyLibs, deps.ProcMacros, deps.StaticLibs, deps.SharedLibs, flags, outputFile, "bin", includeDirs, targetTriple)
+}
+
+func TransformSrctoRlib(ctx android.ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags, outputFile android.WritablePath, includeDirs []string) {
+ targetTriple := ctx.(ModuleContext).toolchain().RustTriple()
+
+ transformSrctoCrate(ctx, mainSrc, deps.RLibs, deps.DyLibs, deps.ProcMacros, deps.StaticLibs, deps.SharedLibs, flags, outputFile, "rlib", includeDirs, targetTriple)
+}
+
+func TransformSrctoDylib(ctx android.ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags, outputFile android.WritablePath, includeDirs []string) {
+ targetTriple := ctx.(ModuleContext).toolchain().RustTriple()
+
+ transformSrctoCrate(ctx, mainSrc, deps.RLibs, deps.DyLibs, deps.ProcMacros, deps.StaticLibs, deps.SharedLibs, flags, outputFile, "dylib", includeDirs, targetTriple)
+}
+
+func TransformSrctoProcMacro(ctx android.ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags, outputFile android.WritablePath, includeDirs []string) {
+ // Proc macros are compiler plugins, and thus should target the host compiler
+ targetTriple := ""
+
+ transformSrctoCrate(ctx, mainSrc, deps.RLibs, deps.DyLibs, deps.ProcMacros, deps.StaticLibs, deps.SharedLibs, flags, outputFile, "proc-macro", includeDirs, targetTriple)
+}
+
+func rustLibsToPaths(libs RustLibraries) android.Paths {
+ var paths android.Paths
+ for _, lib := range libs {
+ paths = append(paths, lib.Path)
+ }
+ return paths
+}
+
+func transformSrctoCrate(ctx android.ModuleContext, main android.Path,
+ rlibs, dylibs, proc_macros RustLibraries, static_libs, shared_libs android.Paths, flags Flags, outputFile android.WritablePath, crate_type string, includeDirs []string, targetTriple string) {
+
+ var inputs android.Paths
+ var deps android.Paths
+ var libFlags, rustcFlags []string
+ crate_name := ctx.(ModuleContext).CrateName()
+
+ inputs = append(inputs, main)
+
+ // Collect rustc flags
+ rustcFlags = append(rustcFlags, flags.GlobalFlags...)
+ rustcFlags = append(rustcFlags, flags.RustFlags...)
+ rustcFlags = append(rustcFlags, "--crate-type="+crate_type)
+ rustcFlags = append(rustcFlags, "--crate-name="+crate_name)
+ if targetTriple != "" {
+ rustcFlags = append(rustcFlags, "--target="+targetTriple)
+ }
+
+ // Collect library/crate flags
+ for _, lib := range rlibs {
+ libFlags = append(libFlags, "--extern "+lib.CrateName+"="+lib.Path.String())
+ }
+ for _, lib := range dylibs {
+ libFlags = append(libFlags, "--extern "+lib.CrateName+"="+lib.Path.String())
+ }
+ for _, proc_macro := range proc_macros {
+ libFlags = append(libFlags, "--extern "+proc_macro.CrateName+"="+proc_macro.Path.String())
+ }
+
+ for _, path := range includeDirs {
+ libFlags = append(libFlags, "-L "+path)
+ }
+
+ // Collect dependencies
+ deps = append(deps, rustLibsToPaths(rlibs)...)
+ deps = append(deps, rustLibsToPaths(dylibs)...)
+ deps = append(deps, rustLibsToPaths(proc_macros)...)
+ deps = append(deps, static_libs...)
+ deps = append(deps, shared_libs...)
+
+ ctx.Build(pctx, android.BuildParams{
+ Rule: rustc,
+ Description: "rustc " + main.Rel(),
+ Output: outputFile,
+ Inputs: inputs,
+ Implicits: deps,
+ Args: map[string]string{
+ "rustcFlags": strings.Join(rustcFlags, " "),
+ "linkFlags": strings.Join(flags.LinkFlags, " "),
+ "libFlags": strings.Join(libFlags, " "),
+ },
+ })
+
+}
diff --git a/rust/compiler.go b/rust/compiler.go
new file mode 100644
index 0000000..87cf08b
--- /dev/null
+++ b/rust/compiler.go
@@ -0,0 +1,193 @@
+// Copyright 2019 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 (
+ "fmt"
+ "path/filepath"
+
+ "android/soong/android"
+ "android/soong/rust/config"
+)
+
+func NewBaseCompiler(dir, dir64 string) *baseCompiler {
+ return &baseCompiler{
+ Properties: BaseCompilerProperties{
+ Edition: &config.DefaultEdition,
+ },
+ dir: dir,
+ dir64: dir64,
+ }
+}
+
+type BaseCompilerProperties struct {
+ // flags to pass to rustc
+ Flags []string `android:"path,arch_variant"`
+
+ // flags to pass to the linker
+ Ld_flags []string `android:"path,arch_variant"`
+
+ // list of rust rlib crate dependencies
+ Rlibs []string `android:"arch_variant"`
+
+ // list of rust dylib crate dependencies
+ Dylibs []string `android:"arch_variant"`
+
+ // list of rust proc_macro crate dependencies
+ Proc_macros []string `android:"arch_variant"`
+
+ // list of C shared library dependencies
+ Shared_libs []string `android:"arch_variant"`
+
+ // list of C static library dependencies
+ Static_libs []string `android:"arch_variant"`
+
+ // crate name (defaults to module name); if library, this must be the expected extern crate name
+ Crate_name string `android:"arch_variant"`
+
+ // list of features to enable for this crate
+ Features []string `android:"arch_variant"`
+
+ // specific rust edition that should be used if the default version is not desired
+ Edition *string `android:"arch_variant"`
+
+ // sets name of the output
+ Stem *string `android:"arch_variant"`
+
+ // append to name of output
+ Suffix *string `android:"arch_variant"`
+
+ // install to a subdirectory of the default install path for the module
+ Relative_install_path *string `android:"arch_variant"`
+}
+
+type baseCompiler struct {
+ Properties BaseCompilerProperties
+ pathDeps android.Paths
+ rustFlagsDeps android.Paths
+ linkFlagsDeps android.Paths
+ flags string
+ linkFlags string
+ depFlags []string
+ linkDirs []string
+ edition string
+ src android.Path //rustc takes a single src file
+
+ // Install related
+ dir string
+ dir64 string
+ subDir string
+ relative string
+ path android.OutputPath
+}
+
+var _ compiler = (*baseCompiler)(nil)
+
+func (compiler *baseCompiler) compilerProps() []interface{} {
+ return []interface{}{&compiler.Properties}
+}
+
+func (compiler *baseCompiler) featuresToFlags(features []string) []string {
+ flags := []string{}
+ for _, feature := range features {
+ flags = append(flags, "--cfg 'feature=\""+feature+"\"'")
+ }
+ return flags
+}
+
+func (compiler *baseCompiler) compilerFlags(ctx ModuleContext, flags Flags) Flags {
+
+ flags.RustFlags = append(flags.RustFlags, compiler.Properties.Flags...)
+ flags.RustFlags = append(flags.RustFlags, compiler.featuresToFlags(compiler.Properties.Features)...)
+ flags.RustFlags = append(flags.RustFlags, "--edition="+*compiler.Properties.Edition)
+ flags.LinkFlags = append(flags.LinkFlags, compiler.Properties.Ld_flags...)
+ flags.GlobalFlags = append(flags.GlobalFlags, ctx.toolchain().ToolchainRustFlags())
+
+ if ctx.Host() && !ctx.Windows() {
+ rpath_prefix := `\$$ORIGIN/`
+ if ctx.Darwin() {
+ rpath_prefix = "@loader_path/"
+ }
+
+ var rpath string
+ if ctx.toolchain().Is64Bit() {
+ rpath = "lib64"
+ } else {
+ rpath = "lib"
+ }
+ flags.LinkFlags = append(flags.LinkFlags, "-Wl,-rpath,"+rpath_prefix+rpath)
+ flags.LinkFlags = append(flags.LinkFlags, "-Wl,-rpath,"+rpath_prefix+"../"+rpath)
+ }
+
+ return flags
+}
+
+func (compiler *baseCompiler) compile(ctx ModuleContext, flags Flags, deps PathDeps) android.Path {
+ panic(fmt.Errorf("baseCrater doesn't know how to crate things!"))
+}
+
+func (compiler *baseCompiler) compilerDeps(ctx DepsContext, deps Deps) Deps {
+ deps.Rlibs = append(deps.Rlibs, compiler.Properties.Rlibs...)
+ deps.Dylibs = append(deps.Dylibs, compiler.Properties.Dylibs...)
+ deps.ProcMacros = append(deps.ProcMacros, compiler.Properties.Proc_macros...)
+ deps.StaticLibs = append(deps.StaticLibs, compiler.Properties.Static_libs...)
+ deps.SharedLibs = append(deps.SharedLibs, compiler.Properties.Shared_libs...)
+
+ return deps
+}
+
+func (compiler *baseCompiler) crateName() string {
+ return compiler.Properties.Crate_name
+}
+
+func (compiler *baseCompiler) installDir(ctx ModuleContext) android.OutputPath {
+ dir := compiler.dir
+ if ctx.toolchain().Is64Bit() && compiler.dir64 != "" {
+ dir = compiler.dir64
+ }
+ if (!ctx.Host() && !ctx.Arch().Native) || ctx.Target().NativeBridge == android.NativeBridgeEnabled {
+ dir = filepath.Join(dir, ctx.Arch().ArchType.String())
+ }
+ return android.PathForModuleInstall(ctx, dir, compiler.subDir,
+ compiler.relativeInstallPath(), compiler.relative)
+}
+
+func (compiler *baseCompiler) install(ctx ModuleContext, file android.Path) {
+ compiler.path = ctx.InstallFile(compiler.installDir(ctx), file.Base(), file)
+}
+
+func (compiler *baseCompiler) getStem(ctx ModuleContext) string {
+ return compiler.getStemWithoutSuffix(ctx) + String(compiler.Properties.Suffix)
+}
+
+func (compiler *baseCompiler) getStemWithoutSuffix(ctx BaseModuleContext) string {
+ stem := ctx.baseModuleName()
+ if String(compiler.Properties.Stem) != "" {
+ stem = String(compiler.Properties.Stem)
+ }
+
+ return stem
+}
+func (compiler *baseCompiler) relativeInstallPath() string {
+ return String(compiler.Properties.Relative_install_path)
+}
+
+func srcPathFromModuleSrcs(ctx ModuleContext, srcs []string) android.Path {
+ srcPaths := android.PathsForModuleSrc(ctx, srcs)
+ if len(srcPaths) != 1 {
+ ctx.PropertyErrorf("srcs", "srcs can only contain one path for rust modules")
+ }
+ return srcPaths[0]
+}
diff --git a/rust/compiler_test.go b/rust/compiler_test.go
new file mode 100644
index 0000000..5369096
--- /dev/null
+++ b/rust/compiler_test.go
@@ -0,0 +1,77 @@
+// Copyright 2019 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 (
+ "strings"
+ "testing"
+)
+
+// Test that feature flags are being correctly generated.
+func TestFeaturesToFlags(t *testing.T) {
+ ctx := testRust(t, `
+ rust_library_host_dylib {
+ name: "libfoo",
+ srcs: ["foo.rs"],
+ crate_name: "foo",
+ features: [
+ "fizz",
+ "buzz"
+ ],
+ }`)
+
+ libfooDylib := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_dylib").Rule("rustc")
+
+ if !strings.Contains(libfooDylib.Args["rustcFlags"], "cfg 'feature=\"fizz\"'") ||
+ !strings.Contains(libfooDylib.Args["rustcFlags"], "cfg 'feature=\"buzz\"'") {
+ t.Fatalf("missing fizz and buzz feature flags for libfoo dylib, rustcFlags: %#v", libfooDylib.Args["rustcFlags"])
+ }
+}
+
+// Test that we reject multiple source files.
+func TestEnforceSingleSourceFile(t *testing.T) {
+
+ singleSrcError := "srcs can only contain one path for rust modules"
+
+ // Test libraries
+ testRustError(t, singleSrcError, `
+ rust_library_host {
+ name: "foo-bar-library",
+ srcs: ["foo.rs", "src/bar.rs"],
+ }`)
+
+ // Test binaries
+ testRustError(t, singleSrcError, `
+ rust_binary_host {
+ name: "foo-bar-binary",
+ srcs: ["foo.rs", "src/bar.rs"],
+ }`)
+
+ // Test proc_macros
+ testRustError(t, singleSrcError, `
+ rust_proc_macro {
+ name: "foo-bar-proc-macro",
+ srcs: ["foo.rs", "src/bar.rs"],
+ host_supported: true,
+ }`)
+
+ // Test prebuilts
+ testRustError(t, singleSrcError, `
+ rust_prebuilt_dylib {
+ name: "foo-bar-prebuilt",
+ srcs: ["liby.so", "libz.so"],
+ host_supported: true,
+ }`)
+}
diff --git a/rust/config/global.go b/rust/config/global.go
new file mode 100644
index 0000000..2e08a8c
--- /dev/null
+++ b/rust/config/global.go
@@ -0,0 +1,65 @@
+// Copyright 2019 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 config
+
+import (
+ "android/soong/android"
+ _ "android/soong/cc/config"
+)
+
+var pctx = android.NewPackageContext("android/soong/rust/config")
+
+var (
+ RustDefaultVersion = "1.35.0"
+ RustDefaultBase = "prebuilts/rust/"
+ DefaultEdition = "2018"
+ Stdlibs = []string{
+ "libarena",
+ "libfmt_macros",
+ "libgraphviz",
+ "libserialize",
+ "libstd",
+ "libsyntax",
+ "libsyntax_ext",
+ "libsyntax_pos",
+ "libterm",
+ }
+)
+
+func init() {
+ pctx.SourcePathVariable("RustDefaultBase", RustDefaultBase)
+ pctx.VariableConfigMethod("HostPrebuiltTag", android.Config.PrebuiltOS)
+
+ pctx.VariableFunc("RustBase", func(ctx android.PackageVarContext) string {
+ if override := ctx.Config().Getenv("RUST_PREBUILTS_BASE"); override != "" {
+ return override
+ }
+ return "${RustDefaultBase}"
+ })
+
+ pctx.VariableFunc("RustVersion", func(ctx android.PackageVarContext) string {
+ if override := ctx.Config().Getenv("RUST_PREBUILTS_VERSION"); override != "" {
+ return override
+ }
+ return RustDefaultVersion
+ })
+
+ pctx.StaticVariable("RustPath", "${RustBase}/${HostPrebuiltTag}/${RustVersion}")
+ pctx.StaticVariable("RustBin", "${RustPath}/bin")
+
+ pctx.ImportAs("ccConfig", "android/soong/cc/config")
+ pctx.StaticVariable("RustLinker", "${ccConfig.ClangBin}/clang++")
+ pctx.StaticVariable("RustLinkerArgs", "-B ${ccConfig.ClangBin} -fuse-ld=lld")
+}
diff --git a/rust/config/toolchain.go b/rust/config/toolchain.go
new file mode 100644
index 0000000..a36d61b
--- /dev/null
+++ b/rust/config/toolchain.go
@@ -0,0 +1,124 @@
+// Copyright 2019 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 config
+
+import (
+ "android/soong/android"
+)
+
+type Toolchain interface {
+ RustTriple() string
+ ToolchainRustFlags() string
+ ToolchainLinkFlags() string
+
+ SharedLibSuffix() string
+ StaticLibSuffix() string
+ RlibSuffix() string
+ DylibSuffix() string
+ ProcMacroSuffix() string
+ ExecutableSuffix() string
+
+ Is64Bit() bool
+ Supported() bool
+}
+
+type toolchainBase struct {
+}
+
+func (toolchainBase) RustTriple() string {
+ panic("toolchainBase does not define a triple.")
+}
+
+func (toolchainBase) ToolchainRustFlags() string {
+ panic("toolchainBase does not provide rust flags.")
+}
+
+func (toolchainBase) ToolchainLinkFlags() string {
+ panic("toolchainBase does not provide link flags.")
+}
+
+func (toolchainBase) Is64Bit() bool {
+ panic("toolchainBase cannot determine datapath width.")
+}
+
+type toolchain64Bit struct {
+ toolchainBase
+}
+
+func (toolchain64Bit) Is64Bit() bool {
+ return true
+}
+
+type toolchain32Bit struct {
+ toolchainBase
+}
+
+func (toolchain32Bit) Is64Bit() bool {
+ return false
+}
+
+func (toolchain32Bit) Bionic() bool {
+ return true
+}
+
+func (toolchainBase) ExecutableSuffix() string {
+ return ""
+}
+
+func (toolchainBase) SharedLibSuffix() string {
+ return ".so"
+}
+
+func (toolchainBase) StaticLibSuffix() string {
+ return ".a"
+}
+
+func (toolchainBase) RlibSuffix() string {
+ return ".rlib"
+}
+func (toolchainBase) DylibSuffix() string {
+ return ".so"
+}
+
+func (toolchainBase) ProcMacroSuffix() string {
+ return ".so"
+}
+
+func (toolchainBase) Supported() bool {
+ return false
+}
+
+func toolchainBaseFactory() Toolchain {
+ return &toolchainBase{}
+}
+
+type toolchainFactory func(arch android.Arch) Toolchain
+
+var toolchainFactories = make(map[android.OsType]map[android.ArchType]toolchainFactory)
+
+func registerToolchainFactory(os android.OsType, arch android.ArchType, factory toolchainFactory) {
+ if toolchainFactories[os] == nil {
+ toolchainFactories[os] = make(map[android.ArchType]toolchainFactory)
+ }
+ toolchainFactories[os][arch] = factory
+}
+
+func FindToolchain(os android.OsType, arch android.Arch) Toolchain {
+ factory := toolchainFactories[os][arch.ArchType]
+ if factory == nil {
+ return toolchainBaseFactory()
+ }
+ return factory(arch)
+}
diff --git a/rust/config/x86_64_device.go b/rust/config/x86_64_device.go
new file mode 100644
index 0000000..2aca56a
--- /dev/null
+++ b/rust/config/x86_64_device.go
@@ -0,0 +1,88 @@
+// Copyright 2019 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 config
+
+import (
+ "strings"
+
+ "android/soong/android"
+)
+
+var (
+ x86_64RustFlags = []string{}
+ x86_64ArchFeatureRustFlags = map[string][]string{}
+ x86_64LinkFlags = []string{}
+
+ x86_64ArchVariantRustFlags = map[string][]string{
+ "": []string{},
+ "broadwell": []string{"-C target-cpu=broadwell"},
+ "haswell": []string{"-C target-cpu=haswell"},
+ "ivybridge": []string{"-C target-cpu=ivybridge"},
+ "sandybridge": []string{"-C target-cpu=sandybridge"},
+ "silvermont": []string{"-C target-cpu=silvermont"},
+ "skylake": []string{"-C target-cpu=skylake"},
+ //TODO: Add target-cpu=stoneyridge when rustc supports it.
+ "stoneyridge": []string{""},
+ }
+)
+
+func init() {
+ registerToolchainFactory(android.Android, android.X86_64, x86_64ToolchainFactory)
+
+ pctx.StaticVariable("x86_64ToolchainRustFlags", strings.Join(x86_64RustFlags, " "))
+ pctx.StaticVariable("x86_64ToolchainLinkFlags", strings.Join(x86_64LinkFlags, " "))
+
+ for variant, rustFlags := range x86_64ArchVariantRustFlags {
+ pctx.StaticVariable("X86_64"+variant+"VariantRustFlags",
+ strings.Join(rustFlags, " "))
+ }
+
+}
+
+type toolchainX86_64 struct {
+ toolchain64Bit
+ toolchainRustFlags string
+}
+
+func (t *toolchainX86_64) RustTriple() string {
+ return "x86_64-unknown-linux-gnu"
+}
+
+func (t *toolchainX86_64) ToolchainLinkFlags() string {
+ return "${config.x86_64ToolchainLinkFlags}"
+}
+
+func (t *toolchainX86_64) ToolchainRustFlags() string {
+ return t.toolchainRustFlags
+}
+
+func (t *toolchainX86_64) RustFlags() string {
+ return "${config.x86_64ToolchainRustFlags}"
+}
+
+func x86_64ToolchainFactory(arch android.Arch) Toolchain {
+ toolchainRustFlags := []string{
+ "${config.x86_64ToolchainRustFlags}",
+ "${config.X86_64" + arch.ArchVariant + "VariantRustFlags}",
+ }
+
+ for _, feature := range arch.ArchFeatures {
+ toolchainRustFlags = append(toolchainRustFlags, x86_64ArchFeatureRustFlags[feature]...)
+ }
+
+ return &toolchainX86_64{
+ toolchainRustFlags: strings.Join(toolchainRustFlags, " "),
+ }
+}
diff --git a/rust/config/x86_linux_host.go b/rust/config/x86_linux_host.go
new file mode 100644
index 0000000..cb6bf1a
--- /dev/null
+++ b/rust/config/x86_linux_host.go
@@ -0,0 +1,109 @@
+// Copyright 2019 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 config
+
+import (
+ "strings"
+
+ "android/soong/android"
+)
+
+var (
+ LinuxRustFlags = []string{}
+ LinuxRustLinkFlags = []string{}
+ linuxX86Rustflags = []string{}
+ linuxX86Linkflags = []string{}
+ linuxX8664Rustflags = []string{}
+ linuxX8664Linkflags = []string{}
+)
+
+func init() {
+ registerToolchainFactory(android.Linux, android.X86_64, linuxX8664ToolchainFactory)
+ registerToolchainFactory(android.Linux, android.X86, linuxX86ToolchainFactory)
+
+ pctx.StaticVariable("LinuxToolchainRustFlags", strings.Join(LinuxRustFlags, " "))
+ pctx.StaticVariable("LinuxToolchainLinkFlags", strings.Join(LinuxRustLinkFlags, " "))
+ pctx.StaticVariable("LinuxToolchainX86RustFlags", strings.Join(linuxX86Rustflags, " "))
+ pctx.StaticVariable("LinuxToolchainX86LinkFlags", strings.Join(linuxX86Linkflags, " "))
+ pctx.StaticVariable("LinuxToolchainX8664RustFlags", strings.Join(linuxX8664Rustflags, " "))
+ pctx.StaticVariable("LinuxToolchainX8664LinkFlags", strings.Join(linuxX8664Linkflags, " "))
+
+}
+
+type toolchainLinux struct {
+ toolchainRustFlags string
+ toolchainLinkFlags string
+}
+
+type toolchainLinuxX86 struct {
+ toolchain32Bit
+ toolchainLinux
+}
+
+type toolchainLinuxX8664 struct {
+ toolchain64Bit
+ toolchainLinux
+}
+
+func (toolchainLinuxX8664) Supported() bool {
+ return true
+}
+
+func (t *toolchainLinuxX8664) Name() string {
+ return "x86_64"
+}
+
+func (t *toolchainLinuxX8664) RustTriple() string {
+ return "x86_64-unknown-linux-gnu"
+}
+
+func (t *toolchainLinuxX8664) ToolchainLinkFlags() string {
+ return "${config.LinuxToolchainLinkFlags} ${config.LinuxToolchainX8664LinkFlags}"
+}
+
+func (t *toolchainLinuxX8664) ToolchainRustFlags() string {
+ return "${config.LinuxToolchainRustFlags} ${config.LinuxToolchainX8664RustFlags}"
+}
+
+func linuxX8664ToolchainFactory(arch android.Arch) Toolchain {
+ return toolchainLinuxX8664Singleton
+}
+
+func (toolchainLinuxX86) Supported() bool {
+ return true
+}
+
+func (t *toolchainLinuxX86) Name() string {
+ return "x86"
+}
+
+func (t *toolchainLinuxX86) RustTriple() string {
+ return "i686-unknown-linux-gnu"
+}
+
+func (t *toolchainLinuxX86) ToolchainLinkFlags() string {
+ return "${config.LinuxToolchainLinkFlags} ${config.LinuxToolchainX86LinkFlags}"
+}
+
+func (t *toolchainLinuxX86) ToolchainRustFlags() string {
+ return "${config.LinuxToolchainRustFlags} ${config.LinuxToolchainX86RustFlags}"
+}
+
+func linuxX86ToolchainFactory(arch android.Arch) Toolchain {
+ return toolchainLinuxX86Singleton
+}
+
+var toolchainLinuxX8664Singleton Toolchain = &toolchainLinuxX8664{}
+var toolchainLinuxX86Singleton Toolchain = &toolchainLinuxX86{}
diff --git a/rust/library.go b/rust/library.go
new file mode 100644
index 0000000..5cf8ac7
--- /dev/null
+++ b/rust/library.go
@@ -0,0 +1,245 @@
+// Copyright 2019 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 (
+ "android/soong/android"
+)
+
+func init() {
+ android.RegisterModuleType("rust_library", RustLibraryFactory)
+ android.RegisterModuleType("rust_library_dylib", RustLibraryDylibFactory)
+ android.RegisterModuleType("rust_library_rlib", RustLibraryRlibFactory)
+ android.RegisterModuleType("rust_library_host", RustLibraryHostFactory)
+ android.RegisterModuleType("rust_library_host_dylib", RustLibraryDylibHostFactory)
+ android.RegisterModuleType("rust_library_host_rlib", RustLibraryRlibHostFactory)
+
+ //TODO: Add support for generating standard shared/static libraries.
+}
+
+type VariantLibraryProperties struct {
+ Enabled *bool `android:"arch_variant"`
+}
+
+type LibraryCompilerProperties struct {
+ Rlib VariantLibraryProperties `android:"arch_variant"`
+ Dylib VariantLibraryProperties `android:"arch_variant"`
+
+ // path to the source file that is the main entry point of the program (e.g. src/lib.rs)
+ Srcs []string `android:"path,arch_variant"`
+}
+
+type LibraryMutatedProperties struct {
+ VariantName string `blueprint:"mutated"`
+
+ // Build a dylib variant
+ BuildDylib bool `blueprint:"mutated"`
+ // Build an rlib variant
+ BuildRlib bool `blueprint:"mutated"`
+
+ // This variant is a dylib
+ VariantIsDylib bool `blueprint:"mutated"`
+ // This variant is an rlib
+ VariantIsRlib bool `blueprint:"mutated"`
+}
+
+type libraryDecorator struct {
+ *baseCompiler
+
+ Properties LibraryCompilerProperties
+ MutatedProperties LibraryMutatedProperties
+ distFile android.OptionalPath
+ unstrippedOutputFile android.Path
+}
+
+type libraryInterface interface {
+ rlib() bool
+ dylib() bool
+
+ // Returns true if the build options for the module have selected a particular build type
+ buildRlib() bool
+ buildDylib() bool
+
+ // Sets a particular variant type
+ setRlib()
+ setDylib()
+}
+
+func (library *libraryDecorator) exportedDirs() []string {
+ return library.linkDirs
+}
+
+func (library *libraryDecorator) exportedDepFlags() []string {
+ return library.depFlags
+}
+
+func (library *libraryDecorator) reexportDirs(dirs ...string) {
+ library.linkDirs = android.FirstUniqueStrings(append(library.linkDirs, dirs...))
+}
+
+func (library *libraryDecorator) reexportDepFlags(flags ...string) {
+ library.depFlags = android.FirstUniqueStrings(append(library.depFlags, flags...))
+}
+
+func (library *libraryDecorator) rlib() bool {
+ return library.MutatedProperties.VariantIsRlib
+}
+
+func (library *libraryDecorator) dylib() bool {
+ return library.MutatedProperties.VariantIsDylib
+}
+
+func (library *libraryDecorator) buildRlib() bool {
+ return library.MutatedProperties.BuildRlib && BoolDefault(library.Properties.Rlib.Enabled, true)
+}
+
+func (library *libraryDecorator) buildDylib() bool {
+ return library.MutatedProperties.BuildDylib && BoolDefault(library.Properties.Dylib.Enabled, true)
+}
+
+func (library *libraryDecorator) setRlib() {
+ library.MutatedProperties.VariantIsRlib = true
+ library.MutatedProperties.VariantIsDylib = false
+}
+
+func (library *libraryDecorator) setDylib() {
+ library.MutatedProperties.VariantIsRlib = false
+ library.MutatedProperties.VariantIsDylib = true
+}
+
+var _ compiler = (*libraryDecorator)(nil)
+
+// rust_library produces all variants.
+func RustLibraryFactory() android.Module {
+ module, _ := NewRustLibrary(android.HostAndDeviceSupported)
+ return module.Init()
+}
+
+// rust_library_dylib produces a dylib.
+func RustLibraryDylibFactory() android.Module {
+ module, library := NewRustLibrary(android.HostAndDeviceSupported)
+ library.BuildOnlyDylib()
+ return module.Init()
+}
+
+// rust_library_rlib produces an rlib.
+func RustLibraryRlibFactory() android.Module {
+ module, library := NewRustLibrary(android.HostAndDeviceSupported)
+ library.BuildOnlyRlib()
+ return module.Init()
+}
+
+// rust_library_host produces all variants.
+func RustLibraryHostFactory() android.Module {
+ module, _ := NewRustLibrary(android.HostSupported)
+ return module.Init()
+}
+
+// rust_library_dylib_host produces a dylib.
+func RustLibraryDylibHostFactory() android.Module {
+ module, library := NewRustLibrary(android.HostSupported)
+ library.BuildOnlyDylib()
+ return module.Init()
+}
+
+// rust_library_rlib_host produces an rlib.
+func RustLibraryRlibHostFactory() android.Module {
+ module, library := NewRustLibrary(android.HostSupported)
+ library.BuildOnlyRlib()
+ return module.Init()
+}
+
+func (library *libraryDecorator) BuildOnlyDylib() {
+ library.MutatedProperties.BuildRlib = false
+}
+
+func (library *libraryDecorator) BuildOnlyRlib() {
+ library.MutatedProperties.BuildDylib = false
+}
+
+func NewRustLibrary(hod android.HostOrDeviceSupported) (*Module, *libraryDecorator) {
+ module := newModule(hod, android.MultilibFirst)
+
+ library := &libraryDecorator{
+ MutatedProperties: LibraryMutatedProperties{
+ BuildDylib: true,
+ BuildRlib: true,
+ },
+ baseCompiler: NewBaseCompiler("lib", "lib64"),
+ }
+
+ module.compiler = library
+
+ return module, library
+}
+
+func (library *libraryDecorator) compilerProps() []interface{} {
+ return append(library.baseCompiler.compilerProps(),
+ &library.Properties,
+ &library.MutatedProperties)
+}
+
+func (library *libraryDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) android.Path {
+ var outputFile android.WritablePath
+
+ srcPath := srcPathFromModuleSrcs(ctx, library.Properties.Srcs)
+
+ flags.RustFlags = append(flags.RustFlags, deps.depFlags...)
+
+ if library.rlib() {
+ fileName := library.getStem(ctx) + ctx.toolchain().RlibSuffix()
+ outputFile = android.PathForModuleOut(ctx, fileName)
+
+ TransformSrctoRlib(ctx, srcPath, deps, flags, outputFile, deps.linkDirs)
+ } else if library.dylib() {
+ fileName := library.getStem(ctx) + ctx.toolchain().DylibSuffix()
+ outputFile = android.PathForModuleOut(ctx, fileName)
+
+ // We need prefer-dynamic for now to avoid linking in the static stdlib. See:
+ // https://github.com/rust-lang/rust/issues/19680
+ // https://github.com/rust-lang/rust/issues/34909
+ flags.RustFlags = append(flags.RustFlags, "-C prefer-dynamic")
+
+ TransformSrctoDylib(ctx, srcPath, deps, flags, outputFile, deps.linkDirs)
+ }
+
+ library.reexportDirs(deps.linkDirs...)
+ library.reexportDepFlags(deps.depFlags...)
+ library.unstrippedOutputFile = outputFile
+
+ return outputFile
+}
+
+func LibraryMutator(mctx android.BottomUpMutatorContext) {
+ if m, ok := mctx.Module().(*Module); ok && m.compiler != nil {
+ switch library := m.compiler.(type) {
+ case libraryInterface:
+ if library.buildRlib() && library.buildDylib() {
+ modules := mctx.CreateLocalVariations("rlib", "dylib")
+ rlib := modules[0].(*Module)
+ dylib := modules[1].(*Module)
+
+ rlib.compiler.(libraryInterface).setRlib()
+ dylib.compiler.(libraryInterface).setDylib()
+ } else if library.buildRlib() {
+ modules := mctx.CreateLocalVariations("rlib")
+ modules[0].(*Module).compiler.(libraryInterface).setRlib()
+ } else if library.buildDylib() {
+ modules := mctx.CreateLocalVariations("dylib")
+ modules[0].(*Module).compiler.(libraryInterface).setDylib()
+ }
+ }
+ }
+}
diff --git a/rust/library_test.go b/rust/library_test.go
new file mode 100644
index 0000000..bf8643e
--- /dev/null
+++ b/rust/library_test.go
@@ -0,0 +1,61 @@
+// Copyright 2019 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 (
+ "strings"
+ "testing"
+)
+
+// Test that variants are being generated correctly, and that crate-types are correct.
+func TestLibraryVariants(t *testing.T) {
+
+ ctx := testRust(t, `
+ rust_library_host {
+ name: "libfoo",
+ srcs: ["foo.rs"],
+ crate_name: "foo",
+ }`)
+
+ // Test both variants are being built.
+ libfooRlib := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_rlib").Output("libfoo.rlib")
+ libfooDylib := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_dylib").Output("libfoo.so")
+
+ // Test crate type for rlib is correct.
+ if !strings.Contains(libfooRlib.Args["rustcFlags"], "crate-type=rlib") {
+ t.Errorf("missing crate-type for libfoo rlib, rustcFlags: %#v", libfooRlib.Args["rustcFlags"])
+ }
+
+ // Test crate type for dylib is correct.
+ if !strings.Contains(libfooDylib.Args["rustcFlags"], "crate-type=dylib") {
+ t.Errorf("missing crate-type for libfoo dylib, rustcFlags: %#v", libfooDylib.Args["rustcFlags"])
+ }
+}
+
+// Test that dylibs are not statically linking the standard library.
+func TestDylibPreferDynamic(t *testing.T) {
+ ctx := testRust(t, `
+ rust_library_host_dylib {
+ name: "libfoo",
+ srcs: ["foo.rs"],
+ crate_name: "foo",
+ }`)
+
+ libfooDylib := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_dylib").Output("libfoo.so")
+
+ if !strings.Contains(libfooDylib.Args["rustcFlags"], "prefer-dynamic") {
+ t.Errorf("missing prefer-dynamic flag for libfoo dylib, rustcFlags: %#v", libfooDylib.Args["rustcFlags"])
+ }
+}
diff --git a/rust/prebuilt.go b/rust/prebuilt.go
new file mode 100644
index 0000000..d4e631b
--- /dev/null
+++ b/rust/prebuilt.go
@@ -0,0 +1,65 @@
+// Copyright 2019 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 (
+ "android/soong/android"
+)
+
+func init() {
+ android.RegisterModuleType("rust_prebuilt_dylib", PrebuiltDylibFactory)
+}
+
+type PrebuiltProperties struct {
+ // path to the prebuilt file
+ Srcs []string `android:"path,arch_variant"`
+}
+
+type prebuiltLibraryDecorator struct {
+ *libraryDecorator
+ Properties PrebuiltProperties
+}
+
+var _ compiler = (*prebuiltLibraryDecorator)(nil)
+
+func PrebuiltDylibFactory() android.Module {
+ module, _ := NewPrebuiltDylib(android.HostAndDeviceSupported)
+ return module.Init()
+}
+
+func NewPrebuiltDylib(hod android.HostOrDeviceSupported) (*Module, *prebuiltLibraryDecorator) {
+ module, library := NewRustLibrary(hod)
+ library.BuildOnlyDylib()
+ library.setDylib()
+ prebuilt := &prebuiltLibraryDecorator{
+ libraryDecorator: library,
+ }
+ module.compiler = prebuilt
+ module.AddProperties(&library.Properties)
+ return module, prebuilt
+}
+
+func (prebuilt *prebuiltLibraryDecorator) compilerProps() []interface{} {
+ return append(prebuilt.baseCompiler.compilerProps(),
+ &prebuilt.Properties)
+}
+
+func (prebuilt *prebuiltLibraryDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) android.Path {
+ srcPath := srcPathFromModuleSrcs(ctx, prebuilt.Properties.Srcs)
+
+ prebuilt.unstrippedOutputFile = srcPath
+
+ return srcPath
+}
diff --git a/rust/proc_macro.go b/rust/proc_macro.go
new file mode 100644
index 0000000..4acb06f
--- /dev/null
+++ b/rust/proc_macro.go
@@ -0,0 +1,79 @@
+// Copyright 2019 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 (
+ "android/soong/android"
+)
+
+func init() {
+ android.RegisterModuleType("rust_proc_macro", ProcMacroFactory)
+}
+
+type ProcMacroCompilerProperties struct {
+ // path to the source file that is the main entry point of the program (e.g. src/lib.rs)
+ Srcs []string `android:"path,arch_variant"`
+
+ // set name of the procMacro
+ Stem *string `android:"arch_variant"`
+ Suffix *string `android:"arch_variant"`
+}
+
+type procMacroDecorator struct {
+ *baseCompiler
+
+ Properties ProcMacroCompilerProperties
+ distFile android.OptionalPath
+ unstrippedOutputFile android.Path
+}
+
+type procMacroInterface interface {
+}
+
+var _ compiler = (*procMacroDecorator)(nil)
+
+func ProcMacroFactory() android.Module {
+ module, _ := NewProcMacro(android.HostAndDeviceSupported)
+ return module.Init()
+}
+
+func NewProcMacro(hod android.HostOrDeviceSupported) (*Module, *procMacroDecorator) {
+ module := newModule(hod, android.MultilibFirst)
+
+ procMacro := &procMacroDecorator{
+ baseCompiler: NewBaseCompiler("lib", "lib64"),
+ }
+
+ module.compiler = procMacro
+
+ return module, procMacro
+}
+
+func (procMacro *procMacroDecorator) compilerProps() []interface{} {
+ return append(procMacro.baseCompiler.compilerProps(),
+ &procMacro.Properties)
+}
+
+func (procMacro *procMacroDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) android.Path {
+ fileName := procMacro.getStem(ctx) + ctx.toolchain().ProcMacroSuffix()
+ outputFile := android.PathForModuleOut(ctx, fileName)
+
+ srcPath := srcPathFromModuleSrcs(ctx, procMacro.Properties.Srcs)
+
+ procMacro.unstrippedOutputFile = outputFile
+
+ TransformSrctoProcMacro(ctx, srcPath, deps, flags, outputFile, deps.linkDirs)
+ return outputFile
+}
diff --git a/rust/rust.go b/rust/rust.go
new file mode 100644
index 0000000..5a2514e
--- /dev/null
+++ b/rust/rust.go
@@ -0,0 +1,498 @@
+// Copyright 2019 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 (
+ "strings"
+
+ "github.com/google/blueprint"
+ "github.com/google/blueprint/proptools"
+
+ "android/soong/android"
+ "android/soong/cc"
+ "android/soong/rust/config"
+)
+
+var pctx = android.NewPackageContext("android/soong/rust")
+
+func init() {
+ // Only allow rust modules to be defined for certain projects
+ rustModuleTypes := []string{
+ "rust_binary",
+ "rust_binary_host",
+ "rust_library",
+ "rust_library_dylib",
+ "rust_library_rlib",
+ "rust_library_host",
+ "rust_library_host_dylib",
+ "rust_library_host_rlib",
+ "rust_proc_macro",
+ }
+
+ rustAllowedPaths := []string{
+ "external/rust/crates",
+ "external/crosvm",
+ "external/adhd",
+ }
+
+ android.AddNeverAllowRules(
+ android.NeverAllow().
+ NotIn(rustAllowedPaths...).
+ ModuleType(rustModuleTypes...))
+
+ android.RegisterModuleType("rust_defaults", defaultsFactory)
+ android.PreDepsMutators(func(ctx android.RegisterMutatorsContext) {
+ ctx.BottomUp("rust_libraries", LibraryMutator).Parallel()
+ })
+ pctx.Import("android/soong/rust/config")
+}
+
+type Flags struct {
+ GlobalFlags []string // Flags that apply globally
+ RustFlags []string // Flags that apply to rust
+ LinkFlags []string // Flags that apply to linker
+ RustFlagsDeps android.Paths // Files depended on by compiler flags
+ Toolchain config.Toolchain
+}
+
+type BaseProperties struct {
+ AndroidMkRlibs []string
+ AndroidMkDylibs []string
+ AndroidMkProcMacroLibs []string
+ AndroidMkSharedLibs []string
+ AndroidMkStaticLibs []string
+}
+
+type Module struct {
+ android.ModuleBase
+ android.DefaultableModuleBase
+
+ Properties BaseProperties
+
+ hod android.HostOrDeviceSupported
+ multilib android.Multilib
+
+ compiler compiler
+ cachedToolchain config.Toolchain
+ subAndroidMkOnce map[subAndroidMkProvider]bool
+ outputFile android.OptionalPath
+}
+
+type Deps struct {
+ Dylibs []string
+ Rlibs []string
+ ProcMacros []string
+ SharedLibs []string
+ StaticLibs []string
+
+ CrtBegin, CrtEnd string
+}
+
+type PathDeps struct {
+ DyLibs RustLibraries
+ RLibs RustLibraries
+ SharedLibs android.Paths
+ StaticLibs android.Paths
+ ProcMacros RustLibraries
+ linkDirs []string
+ depFlags []string
+ //ReexportedDeps android.Paths
+}
+
+type RustLibraries []RustLibrary
+
+type RustLibrary struct {
+ Path android.Path
+ CrateName string
+}
+
+type compiler interface {
+ compilerFlags(ctx ModuleContext, flags Flags) Flags
+ compilerProps() []interface{}
+ compile(ctx ModuleContext, flags Flags, deps PathDeps) android.Path
+ compilerDeps(ctx DepsContext, deps Deps) Deps
+ crateName() string
+
+ install(ctx ModuleContext, path android.Path)
+ relativeInstallPath() string
+}
+
+func defaultsFactory() android.Module {
+ return DefaultsFactory()
+}
+
+type Defaults struct {
+ android.ModuleBase
+ android.DefaultsModuleBase
+}
+
+func DefaultsFactory(props ...interface{}) android.Module {
+ module := &Defaults{}
+
+ module.AddProperties(props...)
+ module.AddProperties(
+ &BaseProperties{},
+ &BaseCompilerProperties{},
+ &BinaryCompilerProperties{},
+ &LibraryCompilerProperties{},
+ &ProcMacroCompilerProperties{},
+ &PrebuiltProperties{},
+ )
+
+ android.InitDefaultsModule(module)
+ return module
+}
+
+func (mod *Module) CrateName() string {
+ if mod.compiler != nil && mod.compiler.crateName() != "" {
+ return mod.compiler.crateName()
+ }
+ // Default crate names replace '-' in the name to '_'
+ return strings.Replace(mod.BaseModuleName(), "-", "_", -1)
+}
+
+func (mod *Module) Init() android.Module {
+ mod.AddProperties(&mod.Properties)
+
+ if mod.compiler != nil {
+ mod.AddProperties(mod.compiler.compilerProps()...)
+ }
+ android.InitAndroidArchModule(mod, mod.hod, mod.multilib)
+
+ android.InitDefaultableModule(mod)
+
+ return mod
+}
+
+func newBaseModule(hod android.HostOrDeviceSupported, multilib android.Multilib) *Module {
+ return &Module{
+ hod: hod,
+ multilib: multilib,
+ }
+}
+func newModule(hod android.HostOrDeviceSupported, multilib android.Multilib) *Module {
+ module := newBaseModule(hod, multilib)
+ return module
+}
+
+type ModuleContext interface {
+ android.ModuleContext
+ ModuleContextIntf
+}
+
+type BaseModuleContext interface {
+ android.BaseModuleContext
+ ModuleContextIntf
+}
+
+type DepsContext interface {
+ android.BottomUpMutatorContext
+ ModuleContextIntf
+}
+
+type ModuleContextIntf interface {
+ toolchain() config.Toolchain
+ baseModuleName() string
+ CrateName() string
+}
+
+type depsContext struct {
+ android.BottomUpMutatorContext
+ moduleContextImpl
+}
+
+type moduleContext struct {
+ android.ModuleContext
+ moduleContextImpl
+}
+
+type moduleContextImpl struct {
+ mod *Module
+ ctx BaseModuleContext
+}
+
+func (ctx *moduleContextImpl) toolchain() config.Toolchain {
+ return ctx.mod.toolchain(ctx.ctx)
+}
+
+func (mod *Module) toolchain(ctx android.BaseModuleContext) config.Toolchain {
+ if mod.cachedToolchain == nil {
+ mod.cachedToolchain = config.FindToolchain(ctx.Os(), ctx.Arch())
+ }
+ return mod.cachedToolchain
+}
+
+func (d *Defaults) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+}
+
+func (mod *Module) GenerateAndroidBuildActions(actx android.ModuleContext) {
+ ctx := &moduleContext{
+ ModuleContext: actx,
+ moduleContextImpl: moduleContextImpl{
+ mod: mod,
+ },
+ }
+ ctx.ctx = ctx
+
+ toolchain := mod.toolchain(ctx)
+
+ if !toolchain.Supported() {
+ // This toolchain's unsupported, there's nothing to do for this mod.
+ return
+ }
+
+ deps := mod.depsToPaths(ctx)
+ flags := Flags{
+ Toolchain: toolchain,
+ }
+
+ if mod.compiler != nil {
+ flags = mod.compiler.compilerFlags(ctx, flags)
+ outputFile := mod.compiler.compile(ctx, flags, deps)
+ mod.outputFile = android.OptionalPathForPath(outputFile)
+ mod.compiler.install(ctx, mod.outputFile.Path())
+ }
+}
+
+func (mod *Module) deps(ctx DepsContext) Deps {
+ deps := Deps{}
+
+ if mod.compiler != nil {
+ deps = mod.compiler.compilerDeps(ctx, deps)
+ }
+
+ deps.Rlibs = android.LastUniqueStrings(deps.Rlibs)
+ deps.Dylibs = android.LastUniqueStrings(deps.Dylibs)
+ deps.ProcMacros = android.LastUniqueStrings(deps.ProcMacros)
+ deps.SharedLibs = android.LastUniqueStrings(deps.SharedLibs)
+ deps.StaticLibs = android.LastUniqueStrings(deps.StaticLibs)
+
+ return deps
+
+}
+
+func (ctx *moduleContextImpl) baseModuleName() string {
+ return ctx.mod.ModuleBase.BaseModuleName()
+}
+
+func (ctx *moduleContextImpl) CrateName() string {
+ return ctx.mod.CrateName()
+}
+
+type dependencyTag struct {
+ blueprint.BaseDependencyTag
+ name string
+ library bool
+ proc_macro bool
+}
+
+var (
+ rlibDepTag = dependencyTag{name: "rlibTag", library: true}
+ dylibDepTag = dependencyTag{name: "dylib", library: true}
+ procMacroDepTag = dependencyTag{name: "procMacro", proc_macro: true}
+)
+
+func (mod *Module) depsToPaths(ctx android.ModuleContext) PathDeps {
+ var depPaths PathDeps
+
+ directRlibDeps := []*Module{}
+ directDylibDeps := []*Module{}
+ directProcMacroDeps := []*Module{}
+ directSharedLibDeps := []*(cc.Module){}
+ directStaticLibDeps := []*(cc.Module){}
+
+ ctx.VisitDirectDeps(func(dep android.Module) {
+ depName := ctx.OtherModuleName(dep)
+ depTag := ctx.OtherModuleDependencyTag(dep)
+ if dep.Target().Os != ctx.Os() {
+ ctx.ModuleErrorf("OS mismatch between %q and %q", ctx.ModuleName(), depName)
+ return
+ }
+ if dep.Target().Arch.ArchType != ctx.Arch().ArchType {
+ ctx.ModuleErrorf("Arch mismatch between %q and %q", ctx.ModuleName(), depName)
+ return
+ }
+
+ if rustDep, ok := dep.(*Module); ok {
+ //Handle Rust Modules
+ linkFile := rustDep.outputFile
+ if !linkFile.Valid() {
+ ctx.ModuleErrorf("Invalid output file when adding dep %q to %q", depName, ctx.ModuleName())
+ }
+
+ switch depTag {
+ case dylibDepTag:
+ dylib, ok := rustDep.compiler.(libraryInterface)
+ if !ok || !dylib.dylib() {
+ ctx.ModuleErrorf("mod %q not an dylib library", depName)
+ return
+ }
+ directDylibDeps = append(directDylibDeps, rustDep)
+ mod.Properties.AndroidMkDylibs = append(mod.Properties.AndroidMkDylibs, depName)
+ case rlibDepTag:
+ rlib, ok := rustDep.compiler.(libraryInterface)
+ if !ok || !rlib.rlib() {
+ ctx.ModuleErrorf("mod %q not an rlib library", depName)
+ return
+ }
+ directRlibDeps = append(directRlibDeps, rustDep)
+ mod.Properties.AndroidMkRlibs = append(mod.Properties.AndroidMkRlibs, depName)
+ case procMacroDepTag:
+ directProcMacroDeps = append(directProcMacroDeps, rustDep)
+ mod.Properties.AndroidMkProcMacroLibs = append(mod.Properties.AndroidMkProcMacroLibs, depName)
+ }
+
+ //Append the dependencies exportedDirs
+ if lib, ok := rustDep.compiler.(*libraryDecorator); ok {
+ depPaths.linkDirs = append(depPaths.linkDirs, lib.exportedDirs()...)
+ depPaths.depFlags = append(depPaths.depFlags, lib.exportedDepFlags()...)
+ } else if procMacro, ok := rustDep.compiler.(*libraryDecorator); ok {
+ depPaths.linkDirs = append(depPaths.linkDirs, procMacro.exportedDirs()...)
+ depPaths.depFlags = append(depPaths.depFlags, procMacro.exportedDepFlags()...)
+ }
+
+ // Append this dependencies output to this mod's linkDirs so they can be exported to dependencies
+ // This can be probably be refactored by defining a common exporter interface similar to cc's
+ if depTag == dylibDepTag || depTag == rlibDepTag || depTag == procMacroDepTag {
+ linkDir := linkPathFromFilePath(linkFile.Path())
+ if lib, ok := mod.compiler.(*libraryDecorator); ok {
+ lib.linkDirs = append(lib.linkDirs, linkDir)
+ } else if procMacro, ok := mod.compiler.(*procMacroDecorator); ok {
+ procMacro.linkDirs = append(procMacro.linkDirs, linkDir)
+ }
+ }
+
+ } else if ccDep, ok := dep.(*cc.Module); ok {
+
+ //Handle C dependencies
+ linkFile := ccDep.OutputFile()
+ linkPath := linkPathFromFilePath(linkFile.Path())
+ libName := libNameFromFilePath(linkFile.Path())
+ if !linkFile.Valid() {
+ ctx.ModuleErrorf("Invalid output file when adding dep %q to %q", depName, ctx.ModuleName())
+ }
+
+ exportDep := false
+
+ switch depTag {
+ case cc.StaticDepTag():
+ depPaths.linkDirs = append(depPaths.linkDirs, linkPath)
+ depPaths.depFlags = append(depPaths.depFlags, "-l"+libName)
+ directStaticLibDeps = append(directStaticLibDeps, ccDep)
+ mod.Properties.AndroidMkStaticLibs = append(mod.Properties.AndroidMkStaticLibs, depName)
+ case cc.SharedDepTag():
+ depPaths.linkDirs = append(depPaths.linkDirs, linkPath)
+ depPaths.depFlags = append(depPaths.depFlags, "-l"+libName)
+ directSharedLibDeps = append(directSharedLibDeps, ccDep)
+ mod.Properties.AndroidMkSharedLibs = append(mod.Properties.AndroidMkSharedLibs, depName)
+ exportDep = true
+ }
+
+ // Make sure these dependencies are propagated
+ if lib, ok := mod.compiler.(*libraryDecorator); ok && (exportDep || lib.rlib()) {
+ lib.linkDirs = append(lib.linkDirs, linkPath)
+ lib.depFlags = append(lib.depFlags, "-l"+libName)
+ } else if procMacro, ok := mod.compiler.(*procMacroDecorator); ok && exportDep {
+ procMacro.linkDirs = append(procMacro.linkDirs, linkPath)
+ procMacro.depFlags = append(procMacro.depFlags, "-l"+libName)
+ }
+
+ }
+ })
+
+ var rlibDepFiles RustLibraries
+ for _, dep := range directRlibDeps {
+ rlibDepFiles = append(rlibDepFiles, RustLibrary{Path: dep.outputFile.Path(), CrateName: dep.CrateName()})
+ }
+ var dylibDepFiles RustLibraries
+ for _, dep := range directDylibDeps {
+ dylibDepFiles = append(dylibDepFiles, RustLibrary{Path: dep.outputFile.Path(), CrateName: dep.CrateName()})
+ }
+ var procMacroDepFiles RustLibraries
+ for _, dep := range directProcMacroDeps {
+ procMacroDepFiles = append(procMacroDepFiles, RustLibrary{Path: dep.outputFile.Path(), CrateName: dep.CrateName()})
+ }
+
+ var staticLibDepFiles android.Paths
+ for _, dep := range directStaticLibDeps {
+ staticLibDepFiles = append(staticLibDepFiles, dep.OutputFile().Path())
+ }
+
+ var sharedLibDepFiles android.Paths
+ for _, dep := range directSharedLibDeps {
+ sharedLibDepFiles = append(sharedLibDepFiles, dep.OutputFile().Path())
+ }
+
+ depPaths.RLibs = append(depPaths.RLibs, rlibDepFiles...)
+ depPaths.DyLibs = append(depPaths.DyLibs, dylibDepFiles...)
+ depPaths.SharedLibs = append(depPaths.SharedLibs, sharedLibDepFiles...)
+ depPaths.StaticLibs = append(depPaths.StaticLibs, staticLibDepFiles...)
+ depPaths.ProcMacros = append(depPaths.ProcMacros, procMacroDepFiles...)
+
+ // Dedup exported flags from dependencies
+ depPaths.linkDirs = android.FirstUniqueStrings(depPaths.linkDirs)
+ depPaths.depFlags = android.FirstUniqueStrings(depPaths.depFlags)
+
+ return depPaths
+}
+
+func linkPathFromFilePath(filepath android.Path) string {
+ return strings.Split(filepath.String(), filepath.Base())[0]
+}
+func libNameFromFilePath(filepath android.Path) string {
+ libName := strings.Split(filepath.Base(), filepath.Ext())[0]
+ if strings.Contains(libName, "lib") {
+ libName = strings.Split(libName, "lib")[1]
+ }
+ return libName
+}
+func (mod *Module) DepsMutator(actx android.BottomUpMutatorContext) {
+ ctx := &depsContext{
+ BottomUpMutatorContext: actx,
+ moduleContextImpl: moduleContextImpl{
+ mod: mod,
+ },
+ }
+ ctx.ctx = ctx
+
+ deps := mod.deps(ctx)
+
+ actx.AddVariationDependencies([]blueprint.Variation{{Mutator: "rust_libraries", Variation: "rlib"}}, rlibDepTag, deps.Rlibs...)
+ actx.AddVariationDependencies([]blueprint.Variation{{Mutator: "rust_libraries", Variation: "dylib"}}, dylibDepTag, deps.Dylibs...)
+
+ ccDepVariations := []blueprint.Variation{}
+ ccDepVariations = append(ccDepVariations, blueprint.Variation{Mutator: "version", Variation: ""})
+ if !mod.Host() {
+ ccDepVariations = append(ccDepVariations, blueprint.Variation{Mutator: "image", Variation: "core"})
+ }
+ actx.AddVariationDependencies(append(ccDepVariations, blueprint.Variation{Mutator: "link", Variation: "shared"}), cc.SharedDepTag(), deps.SharedLibs...)
+ actx.AddVariationDependencies(append(ccDepVariations, blueprint.Variation{Mutator: "link", Variation: "static"}), cc.StaticDepTag(), deps.StaticLibs...)
+ actx.AddDependency(mod, procMacroDepTag, deps.ProcMacros...)
+}
+
+func (mod *Module) Name() string {
+ name := mod.ModuleBase.Name()
+ if p, ok := mod.compiler.(interface {
+ Name(string) string
+ }); ok {
+ name = p.Name(name)
+ }
+ return name
+}
+
+var Bool = proptools.Bool
+var BoolDefault = proptools.BoolDefault
+var String = proptools.String
+var StringPtr = proptools.StringPtr
diff --git a/rust/rust_test.go b/rust/rust_test.go
new file mode 100644
index 0000000..c68cfe7
--- /dev/null
+++ b/rust/rust_test.go
@@ -0,0 +1,167 @@
+// Copyright 2019 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 (
+ "io/ioutil"
+ "os"
+ "testing"
+
+ "android/soong/android"
+)
+
+var (
+ buildDir string
+)
+
+func setUp() {
+ var err error
+ buildDir, err = ioutil.TempDir("", "soong_rust_test")
+ if err != nil {
+ panic(err)
+ }
+}
+
+func tearDown() {
+ os.RemoveAll(buildDir)
+}
+
+func TestMain(m *testing.M) {
+ run := func() int {
+ setUp()
+ defer tearDown()
+
+ return m.Run()
+ }
+
+ os.Exit(run())
+}
+
+func testRust(t *testing.T, bp string) *android.TestContext {
+ t.Helper()
+ config := android.TestArchConfig(buildDir, nil)
+
+ t.Helper()
+ ctx := CreateTestContext(bp)
+ ctx.Register()
+
+ _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
+ android.FailIfErrored(t, errs)
+ _, errs = ctx.PrepareBuildActions(config)
+ android.FailIfErrored(t, errs)
+
+ return ctx
+}
+
+func testRustError(t *testing.T, pattern string, bp string) {
+ t.Helper()
+ config := android.TestArchConfig(buildDir, nil)
+
+ ctx := CreateTestContext(bp)
+ ctx.Register()
+
+ _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
+ if len(errs) > 0 {
+ android.FailIfNoMatchingErrors(t, pattern, errs)
+ return
+ }
+
+ _, errs = ctx.PrepareBuildActions(config)
+ if len(errs) > 0 {
+ android.FailIfNoMatchingErrors(t, pattern, errs)
+ return
+ }
+
+ t.Fatalf("missing expected error %q (0 errors are returned)", pattern)
+}
+
+// Test that we can extract the lib name from a lib path.
+func TestLibNameFromFilePath(t *testing.T) {
+ barPath := android.PathForTesting("out/soong/.intermediates/external/libbar/libbar/linux_glibc_x86_64_shared/libbar.so")
+ libName := libNameFromFilePath(barPath)
+ expectedResult := "bar"
+
+ if libName != expectedResult {
+ t.Errorf("libNameFromFilePath returned the wrong name; expected '%#v', got '%#v'", expectedResult, libName)
+ }
+}
+
+// Test that we can extract the link path from a lib path.
+func TestLinkPathFromFilePath(t *testing.T) {
+ barPath := android.PathForTesting("out/soong/.intermediates/external/libbar/libbar/linux_glibc_x86_64_shared/libbar.so")
+ libName := linkPathFromFilePath(barPath)
+ expectedResult := "out/soong/.intermediates/external/libbar/libbar/linux_glibc_x86_64_shared/"
+
+ if libName != expectedResult {
+ t.Errorf("libNameFromFilePath returned the wrong name; expected '%#v', got '%#v'", expectedResult, libName)
+ }
+}
+
+// Test default crate names from module names are generated correctly.
+func TestDefaultCrateName(t *testing.T) {
+ ctx := testRust(t, `
+ rust_library_host_dylib {
+ name: "fizz-buzz",
+ srcs: ["foo.rs"],
+ }`)
+ module := ctx.ModuleForTests("fizz-buzz", "linux_glibc_x86_64_dylib").Module().(*Module)
+ crateName := module.CrateName()
+ expectedResult := "fizz_buzz"
+
+ if crateName != expectedResult {
+ t.Errorf("CrateName() returned the wrong default crate name; expected '%#v', got '%#v'", expectedResult, crateName)
+ }
+}
+
+// Test to make sure dependencies are being picked up correctly.
+func TestDepsTracking(t *testing.T) {
+ ctx := testRust(t, `
+ rust_library_host_dylib {
+ name: "libfoo",
+ srcs: ["foo.rs"],
+ }
+ rust_library_host_rlib {
+ name: "libbar",
+ srcs: ["foo.rs"],
+ }
+ rust_proc_macro {
+ name: "libpm",
+ srcs: ["foo.rs"],
+ host_supported: true,
+ }
+ rust_binary_host {
+ name: "fizz-buzz",
+ dylibs: ["libfoo"],
+ rlibs: ["libbar"],
+ proc_macros: ["libpm"],
+ srcs: ["foo.rs"],
+ }
+ `)
+ module := ctx.ModuleForTests("fizz-buzz", "linux_glibc_x86_64").Module().(*Module)
+
+ // Since dependencies are added to AndroidMk* properties, we can check these to see if they've been picked up.
+ if !android.InList("libfoo", module.Properties.AndroidMkDylibs) {
+ t.Errorf("Dylib dependency not detected (dependency missing from AndroidMkDylibs)")
+ }
+
+ if !android.InList("libbar", module.Properties.AndroidMkRlibs) {
+ t.Errorf("Rlib dependency not detected (dependency missing from AndroidMkRlibs)")
+ }
+
+ if !android.InList("libpm", module.Properties.AndroidMkProcMacroLibs) {
+ t.Errorf("Proc_macro dependency not detected (dependency missing from AndroidMkProcMacroLibs)")
+ }
+
+}
diff --git a/rust/testing.go b/rust/testing.go
new file mode 100644
index 0000000..a38697f
--- /dev/null
+++ b/rust/testing.go
@@ -0,0 +1,105 @@
+// Copyright (C) 2019 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 (
+ "android/soong/android"
+)
+
+func GatherRequiredDepsForTest() string {
+ bp := `
+ rust_prebuilt_dylib {
+ name: "libarena_x86_64-unknown-linux-gnu",
+ srcs: [""],
+ host_supported: true,
+ }
+ rust_prebuilt_dylib {
+ name: "libfmt_macros_x86_64-unknown-linux-gnu",
+ srcs: [""],
+ host_supported: true,
+ }
+ rust_prebuilt_dylib {
+ name: "libgraphviz_x86_64-unknown-linux-gnu",
+ srcs: [""],
+ host_supported: true,
+ }
+ rust_prebuilt_dylib {
+ name: "libserialize_x86_64-unknown-linux-gnu",
+ srcs: [""],
+ host_supported: true,
+ }
+ rust_prebuilt_dylib {
+ name: "libstd_x86_64-unknown-linux-gnu",
+ srcs: [""],
+ host_supported: true,
+ }
+ rust_prebuilt_dylib {
+ name: "libsyntax_x86_64-unknown-linux-gnu",
+ srcs: [""],
+ host_supported: true,
+ }
+ rust_prebuilt_dylib {
+ name: "libsyntax_ext_x86_64-unknown-linux-gnu",
+ srcs: [""],
+ host_supported: true,
+ }
+ rust_prebuilt_dylib {
+ name: "libsyntax_pos_x86_64-unknown-linux-gnu",
+ srcs: [""],
+ host_supported: true,
+ }
+ rust_prebuilt_dylib {
+ name: "libterm_x86_64-unknown-linux-gnu",
+ srcs: [""],
+ host_supported: true,
+ }
+ rust_prebuilt_dylib {
+ name: "libtest_x86_64-unknown-linux-gnu",
+ srcs: [""],
+ host_supported: true,
+ }
+ `
+ return bp
+}
+
+func CreateTestContext(bp string) *android.TestContext {
+ ctx := android.NewTestArchContext()
+ ctx.RegisterModuleType("rust_binary", android.ModuleFactoryAdaptor(RustBinaryFactory))
+ ctx.RegisterModuleType("rust_binary_host", android.ModuleFactoryAdaptor(RustBinaryHostFactory))
+ ctx.RegisterModuleType("rust_library", android.ModuleFactoryAdaptor(RustLibraryFactory))
+ ctx.RegisterModuleType("rust_library_host", android.ModuleFactoryAdaptor(RustLibraryHostFactory))
+ ctx.RegisterModuleType("rust_library_host_rlib", android.ModuleFactoryAdaptor(RustLibraryRlibHostFactory))
+ ctx.RegisterModuleType("rust_library_host_dylib", android.ModuleFactoryAdaptor(RustLibraryDylibHostFactory))
+ ctx.RegisterModuleType("rust_library_rlib", android.ModuleFactoryAdaptor(RustLibraryRlibFactory))
+ ctx.RegisterModuleType("rust_library_dylib", android.ModuleFactoryAdaptor(RustLibraryDylibFactory))
+ ctx.RegisterModuleType("rust_proc_macro", android.ModuleFactoryAdaptor(ProcMacroFactory))
+ ctx.RegisterModuleType("rust_prebuilt_dylib", android.ModuleFactoryAdaptor(PrebuiltDylibFactory))
+ ctx.PreDepsMutators(func(ctx android.RegisterMutatorsContext) {
+ ctx.BottomUp("rust_libraries", LibraryMutator).Parallel()
+ })
+ bp = bp + GatherRequiredDepsForTest()
+
+ mockFS := map[string][]byte{
+ "Android.bp": []byte(bp),
+ "foo.rs": nil,
+ "src/bar.rs": nil,
+ "liby.so": nil,
+ "libz.so": nil,
+ }
+
+ ctx.MockFileSystem(mockFS)
+
+ return ctx
+}
diff --git a/tradefed/autogen.go b/tradefed/autogen.go
index 952b022..7cd7c5a 100644
--- a/tradefed/autogen.go
+++ b/tradefed/autogen.go
@@ -24,6 +24,8 @@
"android/soong/android"
)
+const test_xml_indent = " "
+
func getTestConfigTemplate(ctx android.ModuleContext, prop *string) android.OptionalPath {
return ctx.ExpandOptionalSource(prop, "test_config_template")
}
@@ -72,13 +74,25 @@
}
type Preparer struct {
- Class string
+ Class string
+ Options []Option
}
var _ Config = Preparer{}
func (p Preparer) Config() string {
- return fmt.Sprintf(`<target_preparer class="%s" />`, p.Class)
+ var optionStrings []string
+ for _, option := range p.Options {
+ optionStrings = append(optionStrings, option.Config())
+ }
+ var options string
+ if len(p.Options) == 0 {
+ options = ""
+ } else {
+ optionDelimiter := fmt.Sprintf("\\n%s%s", test_xml_indent, test_xml_indent)
+ options = optionDelimiter + strings.Join(optionStrings, optionDelimiter)
+ }
+ return fmt.Sprintf(`<target_preparer class="%s">%s\n%s</target_preparer>`, p.Class, options, test_xml_indent)
}
func autogenTemplate(ctx android.ModuleContext, output android.WritablePath, template string, configs []Config) {
@@ -86,7 +100,7 @@
for _, config := range configs {
configStrings = append(configStrings, config.Config())
}
- extraConfigs := strings.Join(configStrings, "\n ")
+ extraConfigs := strings.Join(configStrings, fmt.Sprintf("\\n%s", test_xml_indent))
extraConfigs = proptools.NinjaAndShellEscape(extraConfigs)
ctx.Build(pctx, android.BuildParams{