Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c6de2d94 authored by Ivan Lozano's avatar Ivan Lozano Committed by Gerrit Code Review
Browse files

Merge "Add Rust support to Soong."

parents 7eedfe00 ffee3347
Loading
Loading
Loading
Loading
+44 −0
Original line number Diff line number Diff line
@@ -325,6 +325,50 @@ bootstrap_go_package {
    ],
}

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",
+8 −0
Original line number Diff line number Diff line
@@ -392,6 +392,14 @@ func IsTestPerSrcDepTag(depTag blueprint.DependencyTag) bool {
	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

rust/androidmk.go

0 → 100644
+155 −0
Original line number Diff line number Diff line
// 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
}

rust/binary.go

0 → 100644
+110 −0
Original line number Diff line number Diff line
// 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
}

rust/binary_test.go

0 → 100644
+46 −0
Original line number Diff line number Diff line
// 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"])
	}
}
Loading