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

Commit e7c71c34 authored by Peter Collingbourne's avatar Peter Collingbourne
Browse files

Split Rust crate builds into compile and link phases.

Teach rustc to generate object files instead of a linked executable
for binary and shared library crates. This lets us skip the lengthy
Rust compilation phase when only the C++ dependencies of these crates
have changed.

This works using a Python script that replaces the linker invoked by
rustc and converts the linker command line into a rspfile that can be
used during the link phase. The script makes certain assumptions about
how rustc invokes the linker, but since we control the version of Rust
we use, we can update the script if those assumptions ever break. I've
asked the Rust developers about adding an official way to do this so
hopefully we won't need to rely on this hack in the future.

The rustc rule for Kythe has been altered to pass linker=true, somewhat
consistent with the main rustc rule, which now doesn't invoke the actual
linker either. `m xref_rust` still succeeds, but hopefully we can find
someone who knows more about the rest of the Kythe pipeline who can let
us know whether it should still work. Otherwise, let's use this CL as
an opportunity to find out whether the rest of the pipeline is happy
with linker=true, since otherwise we will find it hard to maintain the
Kythe rule in the future.

Change-Id: Ifb9c8689c1bd78c0afcf6fdfd111140d20377536
parent 38c39aff
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -1001,7 +1001,7 @@ func TestApexWithStubs(t *testing.T) {
	// Ensure that stub dependency from a rust module is not included
	ensureNotContains(t, copyCmds, "image.apex/lib64/libfoo.shared_from_rust.so")
	// The rust module is linked to the stub cc library
	rustDeps := ctx.ModuleForTests("foo.rust", "android_arm64_armv8-a_apex10000").Rule("rustc").Args["linkFlags"]
	rustDeps := ctx.ModuleForTests("foo.rust", "android_arm64_armv8-a_apex10000").Rule("rustLink").Args["linkFlags"]
	ensureContains(t, rustDeps, "libfoo.shared_from_rust/android_arm64_armv8-a_shared_current/libfoo.shared_from_rust.so")
	ensureNotContains(t, rustDeps, "libfoo.shared_from_rust/android_arm64_armv8-a_shared/libfoo.shared_from_rust.so")

@@ -1077,7 +1077,7 @@ func TestApexCanUsePrivateApis(t *testing.T) {
	mylibLdFlags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_shared_apex10000").Rule("ld").Args["libFlags"]
	ensureNotContains(t, mylibLdFlags, "mylib2/android_arm64_armv8-a_shared_current/mylib2.so")
	ensureContains(t, mylibLdFlags, "mylib2/android_arm64_armv8-a_shared/mylib2.so")
	rustDeps := ctx.ModuleForTests("foo.rust", "android_arm64_armv8-a_apex10000").Rule("rustc").Args["linkFlags"]
	rustDeps := ctx.ModuleForTests("foo.rust", "android_arm64_armv8-a_apex10000").Rule("rustLink").Args["linkFlags"]
	ensureNotContains(t, rustDeps, "libfoo.shared_from_rust/android_arm64_armv8-a_shared_current/libfoo.shared_from_rust.so")
	ensureContains(t, rustDeps, "libfoo.shared_from_rust/android_arm64_armv8-a_shared/libfoo.shared_from_rust.so")
}
+5 −2
Original line number Diff line number Diff line
@@ -72,11 +72,14 @@ func NewRustBinary(hod android.HostOrDeviceSupported) (*Module, *binaryDecorator
func (binary *binaryDecorator) compilerFlags(ctx ModuleContext, flags Flags) Flags {
	flags = binary.baseCompiler.compilerFlags(ctx, flags)

	if ctx.Os().Linux() {
		flags.LinkFlags = append(flags.LinkFlags, "-Wl,--gc-sections")
	}

	if ctx.toolchain().Bionic() {
		// no-undefined-version breaks dylib compilation since __rust_*alloc* functions aren't defined,
		// but we can apply this to binaries.
		flags.LinkFlags = append(flags.LinkFlags,
			"-Wl,--gc-sections",
			"-Wl,-z,nocopyreloc",
			"-Wl,--no-undefined-version")

@@ -136,7 +139,7 @@ func (binary *binaryDecorator) compile(ctx ModuleContext, flags Flags, deps Path

	flags.RustFlags = append(flags.RustFlags, deps.depFlags...)
	flags.LinkFlags = append(flags.LinkFlags, deps.depLinkFlags...)
	flags.LinkFlags = append(flags.LinkFlags, deps.linkObjects...)
	flags.LinkFlags = append(flags.LinkFlags, deps.linkObjects.Strings()...)

	if binary.stripper.NeedsStrip(ctx) {
		strippedOutputFile := outputFile
+4 −3
Original line number Diff line number Diff line
@@ -123,7 +123,7 @@ func TestBootstrap(t *testing.T) {
			bootstrap: true,
		}`)

	foo := ctx.ModuleForTests("foo", "android_arm64_armv8-a").Rule("rustc")
	foo := ctx.ModuleForTests("foo", "android_arm64_armv8-a").Rule("rustLink")

	flag := "-Wl,-dynamic-linker,/system/bin/bootstrap/linker64"
	if !strings.Contains(foo.Args["linkFlags"], flag) {
@@ -140,10 +140,11 @@ func TestStaticBinaryFlags(t *testing.T) {
		}`)

	fizzOut := ctx.ModuleForTests("fizz", "android_arm64_armv8-a").Rule("rustc")
	fizzOutLink := ctx.ModuleForTests("fizz", "android_arm64_armv8-a").Rule("rustLink")
	fizzMod := ctx.ModuleForTests("fizz", "android_arm64_armv8-a").Module().(*Module)

	flags := fizzOut.Args["rustcFlags"]
	linkFlags := fizzOut.Args["linkFlags"]
	linkFlags := fizzOutLink.Args["linkFlags"]
	if !strings.Contains(flags, "-C relocation-model=static") {
		t.Errorf("static binary missing '-C relocation-model=static' in rustcFlags, found: %#v", flags)
	}
@@ -173,7 +174,7 @@ func TestLinkObjects(t *testing.T) {
			name: "libfoo",
		}`)

	fizzBuzz := ctx.ModuleForTests("fizz-buzz", "android_arm64_armv8-a").Rule("rustc")
	fizzBuzz := ctx.ModuleForTests("fizz-buzz", "android_arm64_armv8-a").Rule("rustLink")
	linkFlags := fizzBuzz.Args["linkFlags"]
	if !strings.Contains(linkFlags, "/libfoo.so") {
		t.Errorf("missing shared dependency 'libfoo.so' in linkFlags: %#v", linkFlags)
+52 −37
Original line number Diff line number Diff line
@@ -26,14 +26,14 @@ import (

var (
	_     = pctx.SourcePathVariable("rustcCmd", "${config.RustBin}/rustc")
	_     = pctx.SourcePathVariable("mkcraterspCmd", "build/soong/scripts/mkcratersp.py")
	rustc = pctx.AndroidStaticRule("rustc",
		blueprint.RuleParams{
			Command: "$envVars $rustcCmd " +
				"-C linker=${config.RustLinker} " +
				"-C link-args=\"${crtBegin} ${config.RustLinkerArgs} ${linkFlags} ${crtEnd}\" " +
				"-C linker=$mkcraterspCmd " +
				"--emit link -o $out --emit dep-info=$out.d.raw $in ${libFlags} $rustcFlags" +
				" && grep \"^$out:\" $out.d.raw > $out.d",
			CommandDeps: []string{"$rustcCmd"},
			CommandDeps: []string{"$rustcCmd", "$mkcraterspCmd"},
			// Rustc deps-info writes out make compatible dep files: https://github.com/rust-lang/rust/issues/7633
			// Rustc emits unneeded dependency lines for the .d and input .rs files.
			// Those extra lines cause ninja warning:
@@ -42,7 +42,12 @@ var (
			Deps:    blueprint.DepsGCC,
			Depfile: "$out.d",
		},
		"rustcFlags", "linkFlags", "libFlags", "crtBegin", "crtEnd", "envVars")
		"rustcFlags", "libFlags", "envVars")
	rustLink = pctx.AndroidStaticRule("rustLink",
		blueprint.RuleParams{
			Command: "${config.RustLinker} -o $out ${crtBegin} ${config.RustLinkerArgs} @$in ${linkFlags} ${crtEnd}",
		},
		"linkFlags", "crtBegin", "crtEnd")

	_       = pctx.SourcePathVariable("rustdocCmd", "${config.RustBin}/rustdoc")
	rustdoc = pctx.AndroidStaticRule("rustdoc",
@@ -101,14 +106,13 @@ var (
				`KYTHE_CANONICALIZE_VNAME_PATHS=prefer-relative ` +
				`$rustExtractor $envVars ` +
				`$rustcCmd ` +
				`-C linker=${config.RustLinker} ` +
				`-C link-args="${crtBegin} ${config.RustLinkerArgs} ${linkFlags} ${crtEnd}" ` +
				`-C linker=true ` +
				`$in ${libFlags} $rustcFlags`,
			CommandDeps:    []string{"$rustExtractor", "$kytheVnames"},
			Rspfile:        "${out}.rsp",
			RspfileContent: "$in",
		},
		"rustcFlags", "linkFlags", "libFlags", "crtBegin", "crtEnd", "envVars")
		"rustcFlags", "libFlags", "envVars")
)

type buildOutput struct {
@@ -220,11 +224,9 @@ func transformSrctoCrate(ctx ModuleContext, main android.Path, deps PathDeps, fl
	outputFile android.WritablePath, crateType string) buildOutput {

	var inputs android.Paths
	var implicits android.Paths
	var orderOnly android.Paths
	var implicits, linkImplicits, linkOrderOnly android.Paths
	var output buildOutput
	var rustcFlags, linkFlags []string
	var implicitOutputs android.WritablePaths

	output.outputFile = outputFile
	crateName := ctx.RustModule().CrateName()
@@ -281,15 +283,15 @@ func transformSrctoCrate(ctx ModuleContext, main android.Path, deps PathDeps, fl
	implicits = append(implicits, rustLibsToPaths(deps.RLibs)...)
	implicits = append(implicits, rustLibsToPaths(deps.DyLibs)...)
	implicits = append(implicits, rustLibsToPaths(deps.ProcMacros)...)
	implicits = append(implicits, deps.StaticLibs...)
	implicits = append(implicits, deps.SharedLibDeps...)
	implicits = append(implicits, deps.srcProviderFiles...)
	implicits = append(implicits, deps.AfdoProfiles...)
	implicits = append(implicits, deps.srcProviderFiles...)
	implicits = append(implicits, deps.WholeStaticLibs...)

	implicits = append(implicits, deps.CrtBegin...)
	implicits = append(implicits, deps.CrtEnd...)
	linkImplicits = append(linkImplicits, deps.LibDeps...)
	linkImplicits = append(linkImplicits, deps.CrtBegin...)
	linkImplicits = append(linkImplicits, deps.CrtEnd...)

	orderOnly = append(orderOnly, deps.SharedLibs...)
	linkOrderOnly = append(linkOrderOnly, deps.linkObjects...)

	if len(deps.SrcDeps) > 0 {
		moduleGenDir := ctx.RustModule().compiler.CargoOutDir()
@@ -328,16 +330,16 @@ func transformSrctoCrate(ctx ModuleContext, main android.Path, deps PathDeps, fl
		}
	}

	envVars = append(envVars, "AR=${cc_config.ClangBin}/llvm-ar")

	if flags.Clippy {
		clippyFile := android.PathForModuleOut(ctx, outputFile.Base()+".clippy")
		ctx.Build(pctx, android.BuildParams{
			Rule:        clippyDriver,
			Description: "clippy " + main.Rel(),
			Output:      clippyFile,
			ImplicitOutputs: nil,
			Inputs:      inputs,
			Implicits:   implicits,
			OrderOnly:       orderOnly,
			Args: map[string]string{
				"rustcFlags":  strings.Join(rustcFlags, " "),
				"libFlags":    strings.Join(libFlags, " "),
@@ -349,23 +351,40 @@ func transformSrctoCrate(ctx ModuleContext, main android.Path, deps PathDeps, fl
		implicits = append(implicits, clippyFile)
	}

	rustcOutputFile := outputFile
	usesLinker := crateType == "bin" || crateType == "dylib" || crateType == "cdylib" || crateType == "proc-macro"
	if usesLinker {
		rustcOutputFile = android.PathForModuleOut(ctx, outputFile.Base()+".rsp")
	}

	ctx.Build(pctx, android.BuildParams{
		Rule:        rustc,
		Description: "rustc " + main.Rel(),
		Output:          outputFile,
		ImplicitOutputs: implicitOutputs,
		Output:      rustcOutputFile,
		Inputs:      inputs,
		Implicits:   implicits,
		OrderOnly:       orderOnly,
		Args: map[string]string{
			"rustcFlags": strings.Join(rustcFlags, " "),
			"linkFlags":  strings.Join(linkFlags, " "),
			"libFlags":   strings.Join(libFlags, " "),
			"envVars":    strings.Join(envVars, " "),
		},
	})

	if usesLinker {
		ctx.Build(pctx, android.BuildParams{
			Rule:        rustLink,
			Description: "rustLink " + main.Rel(),
			Output:      outputFile,
			Inputs:      android.Paths{rustcOutputFile},
			Implicits:   linkImplicits,
			OrderOnly:   linkOrderOnly,
			Args: map[string]string{
				"linkFlags": strings.Join(linkFlags, " "),
				"crtBegin":  strings.Join(deps.CrtBegin.Strings(), " "),
				"crtEnd":    strings.Join(deps.CrtEnd.Strings(), " "),
			"envVars":    strings.Join(envVars, " "),
			},
		})
	}

	if flags.EmitXrefs {
		kytheFile := android.PathForModuleOut(ctx, outputFile.Base()+".kzip")
@@ -375,13 +394,9 @@ func transformSrctoCrate(ctx ModuleContext, main android.Path, deps PathDeps, fl
			Output:      kytheFile,
			Inputs:      inputs,
			Implicits:   implicits,
			OrderOnly:   orderOnly,
			Args: map[string]string{
				"rustcFlags": strings.Join(rustcFlags, " "),
				"linkFlags":  strings.Join(linkFlags, " "),
				"libFlags":   strings.Join(libFlags, " "),
				"crtBegin":   strings.Join(deps.CrtBegin.Strings(), " "),
				"crtEnd":     strings.Join(deps.CrtEnd.Strings(), " "),
				"envVars":    strings.Join(envVars, " "),
			},
		})
+1 −1
Original line number Diff line number Diff line
@@ -65,7 +65,7 @@ func (cov *coverage) flags(ctx ModuleContext, flags Flags, deps PathDeps) (Flags
			"-C instrument-coverage", "-g")
		flags.LinkFlags = append(flags.LinkFlags,
			profileInstrFlag, "-g", coverage.OutputFile().Path().String(), "-Wl,--wrap,open")
		deps.StaticLibs = append(deps.StaticLibs, coverage.OutputFile().Path())
		deps.LibDeps = append(deps.LibDeps, coverage.OutputFile().Path())

		// no_std modules are missing libprofiler_builtins which provides coverage, so we need to add it as a dependency.
		if rustModule, ok := ctx.Module().(*Module); ok && rustModule.compiler.noStdlibs() {
Loading