Browse Source

Merge "Split Rust crate builds into compile and link phases."

Treehugger Robot 1 year ago
parent
commit
f5cd477841

+ 2 - 2
apex/apex_test.go

@@ -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
rust/binary.go

@@ -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
rust/binary_test.go

@@ -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
rust/builder.go

@@ -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,
+			Rule:        clippyDriver,
+			Description: "clippy " + main.Rel(),
+			Output:      clippyFile,
+			Inputs:      inputs,
+			Implicits:   implicits,
 			Args: map[string]string{
 				"rustcFlags":  strings.Join(rustcFlags, " "),
 				"libFlags":    strings.Join(libFlags, " "),
@@ -349,24 +351,41 @@ 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,
-		Inputs:          inputs,
-		Implicits:       implicits,
-		OrderOnly:       orderOnly,
+		Rule:        rustc,
+		Description: "rustc " + main.Rel(),
+		Output:      rustcOutputFile,
+		Inputs:      inputs,
+		Implicits:   implicits,
 		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, " "),
 		},
 	})
 
+	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(), " "),
+			},
+		})
+	}
+
 	if flags.EmitXrefs {
 		kytheFile := android.PathForModuleOut(ctx, outputFile.Base()+".kzip")
 		ctx.Build(pctx, android.BuildParams{
@@ -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
rust/coverage.go

@@ -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() {

+ 13 - 9
rust/coverage_test.go

@@ -55,6 +55,10 @@ func TestCoverageFlags(t *testing.T) {
 	libbarNoCov := ctx.ModuleForTests("libbar_nocov", "android_arm64_armv8-a_dylib").Rule("rustc")
 	fizzCov := ctx.ModuleForTests("fizz_cov", "android_arm64_armv8-a_cov").Rule("rustc")
 	buzzNoCov := ctx.ModuleForTests("buzzNoCov", "android_arm64_armv8-a").Rule("rustc")
+	libfooCovLink := ctx.ModuleForTests("libfoo_cov", "android_arm64_armv8-a_dylib_cov").Rule("rustLink")
+	libbarNoCovLink := ctx.ModuleForTests("libbar_nocov", "android_arm64_armv8-a_dylib").Rule("rustLink")
+	fizzCovLink := ctx.ModuleForTests("fizz_cov", "android_arm64_armv8-a_cov").Rule("rustLink")
+	buzzNoCovLink := ctx.ModuleForTests("buzzNoCov", "android_arm64_armv8-a").Rule("rustLink")
 
 	rustcCoverageFlags := []string{"-C instrument-coverage", " -g "}
 	for _, flag := range rustcCoverageFlags {
@@ -80,17 +84,17 @@ func TestCoverageFlags(t *testing.T) {
 		missingErrorStr := "missing rust linker flag '%s' for '%s' module with coverage enabled; rustcFlags: %#v"
 		containsErrorStr := "contains rust linker flag '%s' for '%s' module with coverage disabled; rustcFlags: %#v"
 
-		if !strings.Contains(fizzCov.Args["linkFlags"], flag) {
-			t.Fatalf(missingErrorStr, flag, "fizz_cov", fizzCov.Args["linkFlags"])
+		if !strings.Contains(fizzCovLink.Args["linkFlags"], flag) {
+			t.Fatalf(missingErrorStr, flag, "fizz_cov", fizzCovLink.Args["linkFlags"])
 		}
-		if !strings.Contains(libfooCov.Args["linkFlags"], flag) {
-			t.Fatalf(missingErrorStr, flag, "libfoo_cov dylib", libfooCov.Args["linkFlags"])
+		if !strings.Contains(libfooCovLink.Args["linkFlags"], flag) {
+			t.Fatalf(missingErrorStr, flag, "libfoo_cov dylib", libfooCovLink.Args["linkFlags"])
 		}
-		if strings.Contains(buzzNoCov.Args["linkFlags"], flag) {
-			t.Fatalf(containsErrorStr, flag, "buzzNoCov", buzzNoCov.Args["linkFlags"])
+		if strings.Contains(buzzNoCovLink.Args["linkFlags"], flag) {
+			t.Fatalf(containsErrorStr, flag, "buzzNoCov", buzzNoCovLink.Args["linkFlags"])
 		}
-		if strings.Contains(libbarNoCov.Args["linkFlags"], flag) {
-			t.Fatalf(containsErrorStr, flag, "libbar_cov", libbarNoCov.Args["linkFlags"])
+		if strings.Contains(libbarNoCovLink.Args["linkFlags"], flag) {
+			t.Fatalf(containsErrorStr, flag, "libbar_cov", libbarNoCovLink.Args["linkFlags"])
 		}
 	}
 
@@ -103,7 +107,7 @@ func TestCoverageDeps(t *testing.T) {
 			srcs: ["foo.rs"],
 		}`)
 
-	fizz := ctx.ModuleForTests("fizz", "android_arm64_armv8-a_cov").Rule("rustc")
+	fizz := ctx.ModuleForTests("fizz", "android_arm64_armv8-a_cov").Rule("rustLink")
 	if !strings.Contains(fizz.Args["linkFlags"], "libprofile-clang-extras.a") {
 		t.Fatalf("missing expected coverage 'libprofile-clang-extras' dependency in linkFlags: %#v", fizz.Args["linkFlags"])
 	}

+ 2 - 1
rust/library.go

@@ -520,7 +520,7 @@ func (library *libraryDecorator) compile(ctx ModuleContext, flags Flags, deps Pa
 
 	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 library.dylib() {
 		// We need prefer-dynamic for now to avoid linking in the static stdlib. See:
@@ -543,6 +543,7 @@ func (library *libraryDecorator) compile(ctx ModuleContext, flags Flags, deps Pa
 	if library.rlib() || library.dylib() {
 		library.flagExporter.exportLinkDirs(deps.linkDirs...)
 		library.flagExporter.exportLinkObjects(deps.linkObjects...)
+		library.flagExporter.exportLibDeps(deps.LibDeps...)
 	}
 
 	if library.static() || library.shared() {

+ 1 - 1
rust/library_test.go

@@ -148,7 +148,7 @@ func TestSharedLibrary(t *testing.T) {
 
 	libfoo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared")
 
-	libfooOutput := libfoo.Rule("rustc")
+	libfooOutput := libfoo.Rule("rustLink")
 	if !strings.Contains(libfooOutput.Args["linkFlags"], "-Wl,-soname=libfoo.so") {
 		t.Errorf("missing expected -Wl,-soname linker flag for libfoo shared lib, linkFlags: %#v",
 			libfooOutput.Args["linkFlags"])

+ 32 - 27
rust/rust.go

@@ -420,13 +420,12 @@ type Deps struct {
 }
 
 type PathDeps struct {
-	DyLibs        RustLibraries
-	RLibs         RustLibraries
-	SharedLibs    android.Paths
-	SharedLibDeps android.Paths
-	StaticLibs    android.Paths
-	ProcMacros    RustLibraries
-	AfdoProfiles  android.Paths
+	DyLibs          RustLibraries
+	RLibs           RustLibraries
+	LibDeps         android.Paths
+	WholeStaticLibs android.Paths
+	ProcMacros      RustLibraries
+	AfdoProfiles    android.Paths
 
 	// depFlags and depLinkFlags are rustc and linker (clang) flags.
 	depFlags     []string
@@ -435,7 +434,7 @@ type PathDeps struct {
 	// linkDirs are link paths passed via -L to rustc. linkObjects are objects passed directly to the linker.
 	// Both of these are exported and propagate to dependencies.
 	linkDirs    []string
-	linkObjects []string
+	linkObjects android.Paths
 
 	// Used by bindgen modules which call clang
 	depClangFlags         []string
@@ -498,7 +497,7 @@ type compiler interface {
 
 type exportedFlagsProducer interface {
 	exportLinkDirs(...string)
-	exportLinkObjects(...string)
+	exportLinkObjects(...android.Path)
 }
 
 type xref interface {
@@ -507,21 +506,27 @@ type xref interface {
 
 type flagExporter struct {
 	linkDirs    []string
-	linkObjects []string
+	linkObjects android.Paths
+	libDeps     android.Paths
 }
 
 func (flagExporter *flagExporter) exportLinkDirs(dirs ...string) {
 	flagExporter.linkDirs = android.FirstUniqueStrings(append(flagExporter.linkDirs, dirs...))
 }
 
-func (flagExporter *flagExporter) exportLinkObjects(flags ...string) {
-	flagExporter.linkObjects = android.FirstUniqueStrings(append(flagExporter.linkObjects, flags...))
+func (flagExporter *flagExporter) exportLinkObjects(flags ...android.Path) {
+	flagExporter.linkObjects = android.FirstUniquePaths(append(flagExporter.linkObjects, flags...))
+}
+
+func (flagExporter *flagExporter) exportLibDeps(paths ...android.Path) {
+	flagExporter.libDeps = android.FirstUniquePaths(append(flagExporter.libDeps, paths...))
 }
 
 func (flagExporter *flagExporter) setProvider(ctx ModuleContext) {
 	ctx.SetProvider(FlagExporterInfoProvider, FlagExporterInfo{
 		LinkDirs:    flagExporter.linkDirs,
 		LinkObjects: flagExporter.linkObjects,
+		LibDeps:     flagExporter.libDeps,
 	})
 }
 
@@ -534,7 +539,8 @@ func NewFlagExporter() *flagExporter {
 type FlagExporterInfo struct {
 	Flags       []string
 	LinkDirs    []string // TODO: this should be android.Paths
-	LinkObjects []string // TODO: this should be android.Paths
+	LinkObjects android.Paths
+	LibDeps     android.Paths
 }
 
 var FlagExporterInfoProvider = blueprint.NewProvider(FlagExporterInfo{})
@@ -1250,6 +1256,7 @@ func (mod *Module) depsToPaths(ctx android.ModuleContext) PathDeps {
 				depPaths.linkDirs = append(depPaths.linkDirs, exportedInfo.LinkDirs...)
 				depPaths.depFlags = append(depPaths.depFlags, exportedInfo.Flags...)
 				depPaths.linkObjects = append(depPaths.linkObjects, exportedInfo.LinkObjects...)
+				depPaths.LibDeps = append(depPaths.LibDeps, exportedInfo.LibDeps...)
 			}
 
 			if depTag == dylibDepTag || depTag == rlibDepTag || depTag == procMacroDepTag {
@@ -1293,6 +1300,7 @@ func (mod *Module) depsToPaths(ctx android.ModuleContext) PathDeps {
 						depPaths.depLinkFlags = append(depPaths.depLinkFlags, []string{"-Wl,--whole-archive", linkObject.Path().String(), "-Wl,--no-whole-archive"}...)
 					} else if libName, ok := libNameFromFilePath(linkObject.Path()); ok {
 						depPaths.depFlags = append(depPaths.depFlags, "-lstatic="+libName)
+						depPaths.WholeStaticLibs = append(depPaths.WholeStaticLibs, linkObject.Path())
 					} else {
 						ctx.ModuleErrorf("'%q' cannot be listed as a whole_static_library in Rust modules unless the output is prefixed by 'lib'", depName, ctx.ModuleName())
 					}
@@ -1300,7 +1308,7 @@ func (mod *Module) depsToPaths(ctx android.ModuleContext) PathDeps {
 
 				// Add this to linkObjects to pass the library directly to the linker as well. This propagates
 				// to dependencies to avoid having to redeclare static libraries for dependents of the dylib variant.
-				depPaths.linkObjects = append(depPaths.linkObjects, linkObject.String())
+				depPaths.linkObjects = append(depPaths.linkObjects, linkObject.AsPaths()...)
 				depPaths.linkDirs = append(depPaths.linkDirs, linkPath)
 
 				exportedInfo := ctx.OtherModuleProvider(dep, cc.FlagExporterInfoProvider).(cc.FlagExporterInfo)
@@ -1326,7 +1334,7 @@ func (mod *Module) depsToPaths(ctx android.ModuleContext) PathDeps {
 				linkPath = linkPathFromFilePath(linkObject.Path())
 
 				depPaths.linkDirs = append(depPaths.linkDirs, linkPath)
-				depPaths.linkObjects = append(depPaths.linkObjects, linkObject.String())
+				depPaths.linkObjects = append(depPaths.linkObjects, linkObject.AsPaths()...)
 				depPaths.depIncludePaths = append(depPaths.depIncludePaths, exportedInfo.IncludeDirs...)
 				depPaths.depSystemIncludePaths = append(depPaths.depSystemIncludePaths, exportedInfo.SystemIncludeDirs...)
 				depPaths.depClangFlags = append(depPaths.depClangFlags, exportedInfo.Flags...)
@@ -1352,7 +1360,9 @@ func (mod *Module) depsToPaths(ctx android.ModuleContext) PathDeps {
 			// Make sure these dependencies are propagated
 			if lib, ok := mod.compiler.(exportedFlagsProducer); ok && exportDep {
 				lib.exportLinkDirs(linkPath)
-				lib.exportLinkObjects(linkObject.String())
+				if linkObject.Valid() {
+					lib.exportLinkObjects(linkObject.Path())
+				}
 			}
 		} else {
 			switch {
@@ -1384,19 +1394,16 @@ func (mod *Module) depsToPaths(ctx android.ModuleContext) PathDeps {
 		procMacroDepFiles = append(procMacroDepFiles, RustLibrary{Path: dep.UnstrippedOutputFile(), CrateName: dep.CrateName()})
 	}
 
-	var staticLibDepFiles android.Paths
+	var libDepFiles android.Paths
 	for _, dep := range directStaticLibDeps {
-		staticLibDepFiles = append(staticLibDepFiles, dep.OutputFile().Path())
+		libDepFiles = append(libDepFiles, dep.OutputFile().Path())
 	}
 
-	var sharedLibFiles android.Paths
-	var sharedLibDepFiles android.Paths
 	for _, dep := range directSharedLibDeps {
-		sharedLibFiles = append(sharedLibFiles, dep.SharedLibrary)
 		if dep.TableOfContents.Valid() {
-			sharedLibDepFiles = append(sharedLibDepFiles, dep.TableOfContents.Path())
+			libDepFiles = append(libDepFiles, dep.TableOfContents.Path())
 		} else {
-			sharedLibDepFiles = append(sharedLibDepFiles, dep.SharedLibrary)
+			libDepFiles = append(libDepFiles, dep.SharedLibrary)
 		}
 	}
 
@@ -1412,15 +1419,13 @@ func (mod *Module) depsToPaths(ctx android.ModuleContext) PathDeps {
 
 	depPaths.RLibs = append(depPaths.RLibs, rlibDepFiles...)
 	depPaths.DyLibs = append(depPaths.DyLibs, dylibDepFiles...)
-	depPaths.SharedLibs = append(depPaths.SharedLibs, sharedLibFiles...)
-	depPaths.SharedLibDeps = append(depPaths.SharedLibDeps, sharedLibDepFiles...)
-	depPaths.StaticLibs = append(depPaths.StaticLibs, staticLibDepFiles...)
+	depPaths.LibDeps = append(depPaths.LibDeps, libDepFiles...)
 	depPaths.ProcMacros = append(depPaths.ProcMacros, procMacroDepFiles...)
 	depPaths.SrcDeps = append(depPaths.SrcDeps, srcProviderDepFiles...)
 
 	// Dedup exported flags from dependencies
 	depPaths.linkDirs = android.FirstUniqueStrings(depPaths.linkDirs)
-	depPaths.linkObjects = android.FirstUniqueStrings(depPaths.linkObjects)
+	depPaths.linkObjects = android.FirstUniquePaths(depPaths.linkObjects)
 	depPaths.depFlags = android.FirstUniqueStrings(depPaths.depFlags)
 	depPaths.depClangFlags = android.FirstUniqueStrings(depPaths.depClangFlags)
 	depPaths.depIncludePaths = android.FirstUniquePaths(depPaths.depIncludePaths)

+ 7 - 6
rust/rust_test.go

@@ -258,6 +258,7 @@ func TestDepsTracking(t *testing.T) {
 	`)
 	module := ctx.ModuleForTests("fizz-buzz", "linux_glibc_x86_64").Module().(*Module)
 	rustc := ctx.ModuleForTests("librlib", "linux_glibc_x86_64_rlib_rlib-std").Rule("rustc")
+	rustLink := ctx.ModuleForTests("fizz-buzz", "linux_glibc_x86_64").Rule("rustLink")
 
 	// Since dependencies are added to AndroidMk* properties, we can check these to see if they've been picked up.
 	if !android.InList("libdylib", module.Properties.AndroidMkDylibs) {
@@ -284,16 +285,16 @@ func TestDepsTracking(t *testing.T) {
 		t.Errorf("-lstatic flag not being passed to rustc for static library %#v", rustc.Args["rustcFlags"])
 	}
 
-	if !strings.Contains(rustc.Args["linkFlags"], "cc_stubs_dep.so") {
-		t.Errorf("shared cc_library not being passed to rustc linkFlags %#v", rustc.Args["linkFlags"])
+	if !strings.Contains(rustLink.Args["linkFlags"], "cc_stubs_dep.so") {
+		t.Errorf("shared cc_library not being passed to rustc linkFlags %#v", rustLink.Args["linkFlags"])
 	}
 
-	if !android.SuffixInList(rustc.OrderOnly.Strings(), "cc_stubs_dep.so") {
-		t.Errorf("shared cc dep not being passed as order-only to rustc %#v", rustc.OrderOnly.Strings())
+	if !android.SuffixInList(rustLink.OrderOnly.Strings(), "cc_stubs_dep.so") {
+		t.Errorf("shared cc dep not being passed as order-only to rustc %#v", rustLink.OrderOnly.Strings())
 	}
 
-	if !android.SuffixInList(rustc.Implicits.Strings(), "cc_stubs_dep.so.toc") {
-		t.Errorf("shared cc dep TOC not being passed as implicit to rustc %#v", rustc.Implicits.Strings())
+	if !android.SuffixInList(rustLink.Implicits.Strings(), "cc_stubs_dep.so.toc") {
+		t.Errorf("shared cc dep TOC not being passed as implicit to rustc %#v", rustLink.Implicits.Strings())
 	}
 }
 

+ 1 - 1
rust/sanitize_test.go

@@ -35,7 +35,7 @@ func checkHasMemtagNote(t *testing.T, m android.TestingModule, expected MemtagNo
 	note_sync := "note_memtag_heap_sync"
 
 	found := None
-	implicits := m.Rule("rustc").Implicits
+	implicits := m.Rule("rustLink").Implicits
 	for _, lib := range implicits {
 		if strings.Contains(lib.Rel(), note_async) {
 			found = Async

+ 2 - 2
rust/vendor_snapshot_test.go

@@ -941,7 +941,7 @@ func TestVendorSnapshotUse(t *testing.T) {
 	ctx := testRustVndkFsVersions(t, "", mockFS, "30", "current", "31")
 
 	// libclient uses libvndk.vndk.30.arm64, libvendor.vendor_static.30.arm64, libvendor_without_snapshot
-	libclientLdFlags := ctx.ModuleForTests("libclient", sharedVariant).Rule("rustc").Args["linkFlags"]
+	libclientLdFlags := ctx.ModuleForTests("libclient", sharedVariant).Rule("rustLink").Args["linkFlags"]
 	for _, input := range [][]string{
 		[]string{sharedVariant, "libvndk.vndk.30.arm64"},
 		[]string{staticVariant, "libvendor.vendor_static.30.arm64"},
@@ -997,7 +997,7 @@ func TestVendorSnapshotUse(t *testing.T) {
 		t.Errorf("Unexpected rust rlib name in AndroidMk: %q, expected: %q\n", rustVendorBinMkRlibName, expectedRustVendorSnapshotName)
 	}
 
-	binWithoutSnapshotLdFlags := ctx.ModuleForTests("bin_without_snapshot", binaryVariant).Rule("rustc").Args["linkFlags"]
+	binWithoutSnapshotLdFlags := ctx.ModuleForTests("bin_without_snapshot", binaryVariant).Rule("rustLink").Args["linkFlags"]
 	libVndkStaticOutputPaths := cc.GetOutputPaths(ctx, staticVariant, []string{"libvndk.vendor_static.30.arm64"})
 	if !strings.Contains(binWithoutSnapshotLdFlags, libVndkStaticOutputPaths[0].String()) {
 		t.Errorf("libflags for bin_without_snapshot must contain %#v, but was %#v",

+ 79 - 0
scripts/mkcratersp.py

@@ -0,0 +1,79 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2023 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.
+#
+
+"""
+This script is used as a replacement for the Rust linker. It converts a linker
+command line into a rspfile that can be used during the link phase.
+"""
+
+import os
+import shutil
+import subprocess
+import sys
+
+def create_archive(out, objects, archives):
+  mricmd = f'create {out}\n'
+  for o in objects:
+    mricmd += f'addmod {o}\n'
+  for a in archives:
+    mricmd += f'addlib {a}\n'
+  mricmd += 'save\nend\n'
+  subprocess.run([os.getenv('AR'), '-M'], encoding='utf-8', input=mricmd, check=True)
+
+objects = []
+archives = []
+linkdirs = []
+libs = []
+temp_archives = []
+version_script = None
+
+for i, arg in enumerate(sys.argv):
+  if arg == '-o':
+    out = sys.argv[i+1]
+  if arg == '-L':
+    linkdirs.append(sys.argv[i+1])
+  if arg.startswith('-l') or arg == '-shared':
+    libs.append(arg)
+  if arg.startswith('-Wl,--version-script='):
+    version_script = arg[21:]
+  if arg[0] == '-':
+    continue
+  if arg.endswith('.o') or arg.endswith('.rmeta'):
+    objects.append(arg)
+  if arg.endswith('.rlib'):
+    if arg.startswith(os.getenv('TMPDIR')):
+      temp_archives.append(arg)
+    else:
+      archives.append(arg)
+
+create_archive(f'{out}.whole.a', objects, [])
+create_archive(f'{out}.a', [], temp_archives)
+
+with open(out, 'w') as f:
+  print(f'-Wl,--whole-archive', file=f)
+  print(f'{out}.whole.a', file=f)
+  print(f'-Wl,--no-whole-archive', file=f)
+  print(f'{out}.a', file=f)
+  for a in archives:
+    print(a, file=f)
+  for linkdir in linkdirs:
+    print(f'-L{linkdir}', file=f)
+  for l in libs:
+    print(l, file=f)
+  if version_script:
+    shutil.copyfile(version_script, f'{out}.version_script')
+    print(f'-Wl,--version-script={out}.version_script', file=f)