Add Rust support to Soong.
Adds support to Soong for building rust modules. This currently only
supports x86_64 device and x86 linux host targets. The functionality
is sufficient to build crosvm.
Bug: 136189233
Test: Test module builds.
Test: crosvm builds.
Change-Id: I6ea04615834a6d673578ab10ea1a2eb04259fe09
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
+}