rust: Resolve crate roots outside rust-project

Previously, we manually re-computed crate roots inside project_json for
rendering rust-project.json. In addition to added complexity, this meant
that generated sources and glob sources would not render correctly - it
would select e.g. `src/**.rs` or `:foo` as a crate root.

Use a centralized computation of crate roots instead.

Bug: 309943184
Test: SOONG_GEN_RUST_PROJECT=1 m nothing, compare rust-project.json
Change-Id: I0caddbf600d025a0041f45e69812cdd6f1761234
This commit is contained in:
Matthew Maurer 2023-11-21 00:20:02 +00:00
parent a28404a7b0
commit db72f7ed80
3 changed files with 40 additions and 123 deletions

View file

@ -44,6 +44,8 @@ type compiler interface {
compile(ctx ModuleContext, flags Flags, deps PathDeps) buildOutput compile(ctx ModuleContext, flags Flags, deps PathDeps) buildOutput
compilerDeps(ctx DepsContext, deps Deps) Deps compilerDeps(ctx DepsContext, deps Deps) Deps
crateName() string crateName() string
edition() string
features() []string
rustdoc(ctx ModuleContext, flags Flags, deps PathDeps) android.OptionalPath rustdoc(ctx ModuleContext, flags Flags, deps PathDeps) android.OptionalPath
// Output directory in which source-generated code from dependencies is // Output directory in which source-generated code from dependencies is
@ -307,9 +309,13 @@ func (compiler *baseCompiler) cfgsToFlags() []string {
return flags return flags
} }
func (compiler *baseCompiler) features() []string {
return compiler.Properties.Features
}
func (compiler *baseCompiler) featuresToFlags() []string { func (compiler *baseCompiler) featuresToFlags() []string {
flags := []string{} flags := []string{}
for _, feature := range compiler.Properties.Features { for _, feature := range compiler.features() {
flags = append(flags, "--cfg 'feature=\""+feature+"\"'") flags = append(flags, "--cfg 'feature=\""+feature+"\"'")
} }

View file

@ -78,7 +78,6 @@ func (procMacro *procMacroDecorator) compilerFlags(ctx ModuleContext, flags Flag
func (procMacro *procMacroDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) buildOutput { func (procMacro *procMacroDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) buildOutput {
fileName := procMacro.getStem(ctx) + ctx.toolchain().ProcMacroSuffix() fileName := procMacro.getStem(ctx) + ctx.toolchain().ProcMacroSuffix()
outputFile := android.PathForModuleOut(ctx, fileName) outputFile := android.PathForModuleOut(ctx, fileName)
srcPath := crateRootPath(ctx, procMacro) srcPath := crateRootPath(ctx, procMacro)
ret := TransformSrctoProcMacro(ctx, srcPath, deps, flags, outputFile) ret := TransformSrctoProcMacro(ctx, srcPath, deps, flags, outputFile)
procMacro.baseCompiler.unstrippedOutputFile = outputFile procMacro.baseCompiler.unstrippedOutputFile = outputFile

View file

@ -17,7 +17,6 @@ package rust
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"path"
"android/soong/android" "android/soong/android"
) )
@ -60,8 +59,9 @@ type rustProjectJson struct {
// crateInfo is used during the processing to keep track of the known crates. // crateInfo is used during the processing to keep track of the known crates.
type crateInfo struct { type crateInfo struct {
Idx int // Index of the crate in rustProjectJson.Crates slice. Idx int // Index of the crate in rustProjectJson.Crates slice.
Deps map[string]int // The keys are the module names and not the crate names. Deps map[string]int // The keys are the module names and not the crate names.
Device bool // True if the crate at idx was a device crate
} }
type projectGeneratorSingleton struct { type projectGeneratorSingleton struct {
@ -77,85 +77,6 @@ func init() {
android.RegisterParallelSingletonType("rust_project_generator", rustProjectGeneratorSingleton) android.RegisterParallelSingletonType("rust_project_generator", rustProjectGeneratorSingleton)
} }
// sourceProviderVariantSource returns the path to the source file if this
// module variant should be used as a priority.
//
// SourceProvider modules may have multiple variants considered as source
// (e.g., x86_64 and armv8). For a module available on device, use the source
// generated for the target. For a host-only module, use the source generated
// for the host.
func sourceProviderVariantSource(ctx android.SingletonContext, rModule *Module) (string, bool) {
rustLib, ok := rModule.compiler.(*libraryDecorator)
if !ok {
return "", false
}
if rustLib.source() {
switch rModule.hod {
case android.HostSupported, android.HostSupportedNoCross:
if rModule.Target().String() == ctx.Config().BuildOSTarget.String() {
src := rustLib.sourceProvider.Srcs()[0]
return src.String(), true
}
default:
if rModule.Target().String() == ctx.Config().AndroidFirstDeviceTarget.String() {
src := rustLib.sourceProvider.Srcs()[0]
return src.String(), true
}
}
}
return "", false
}
// sourceProviderSource finds the main source file of a source-provider crate.
func sourceProviderSource(ctx android.SingletonContext, rModule *Module) (string, bool) {
rustLib, ok := rModule.compiler.(*libraryDecorator)
if !ok {
return "", false
}
if rustLib.source() {
// This is a source-variant, check if we are the right variant
// depending on the module configuration.
if src, ok := sourceProviderVariantSource(ctx, rModule); ok {
return src, true
}
}
foundSource := false
sourceSrc := ""
// Find the variant with the source and return its.
ctx.VisitAllModuleVariants(rModule, func(variant android.Module) {
if foundSource {
return
}
// All variants of a source provider library are libraries.
rVariant, _ := variant.(*Module)
variantLib, _ := rVariant.compiler.(*libraryDecorator)
if variantLib.source() {
sourceSrc, ok = sourceProviderVariantSource(ctx, rVariant)
if ok {
foundSource = true
}
}
})
if !foundSource {
ctx.Errorf("No valid source for source provider found: %v\n", rModule)
}
return sourceSrc, foundSource
}
// crateSource finds the main source file (.rs) for a crate.
func crateSource(ctx android.SingletonContext, rModule *Module, comp *baseCompiler) (string, bool) {
// Basic libraries, executables and tests.
srcs := comp.Properties.Srcs
if len(srcs) != 0 {
return path.Join(ctx.ModuleDir(rModule), srcs[0]), true
}
// SourceProvider libraries.
if rModule.sourceProvider != nil {
return sourceProviderSource(ctx, rModule)
}
return "", false
}
// mergeDependencies visits all the dependencies for module and updates crate and deps // mergeDependencies visits all the dependencies for module and updates crate and deps
// with any new dependency. // with any new dependency.
func (singleton *projectGeneratorSingleton) mergeDependencies(ctx android.SingletonContext, func (singleton *projectGeneratorSingleton) mergeDependencies(ctx android.SingletonContext,
@ -167,7 +88,7 @@ func (singleton *projectGeneratorSingleton) mergeDependencies(ctx android.Single
return return
} }
// Skip unsupported modules. // Skip unsupported modules.
rChild, compChild, ok := isModuleSupported(ctx, child) rChild, ok := isModuleSupported(ctx, child)
if !ok { if !ok {
return return
} }
@ -175,7 +96,7 @@ func (singleton *projectGeneratorSingleton) mergeDependencies(ctx android.Single
var childId int var childId int
cInfo, known := singleton.knownCrates[rChild.Name()] cInfo, known := singleton.knownCrates[rChild.Name()]
if !known { if !known {
childId, ok = singleton.addCrate(ctx, rChild, compChild) childId, ok = singleton.addCrate(ctx, rChild, make(map[string]int))
if !ok { if !ok {
return return
} }
@ -191,41 +112,22 @@ func (singleton *projectGeneratorSingleton) mergeDependencies(ctx android.Single
}) })
} }
// isModuleSupported returns the RustModule and baseCompiler if the module // isModuleSupported returns the RustModule if the module
// should be considered for inclusion in rust-project.json. // should be considered for inclusion in rust-project.json.
func isModuleSupported(ctx android.SingletonContext, module android.Module) (*Module, *baseCompiler, bool) { func isModuleSupported(ctx android.SingletonContext, module android.Module) (*Module, bool) {
rModule, ok := module.(*Module) rModule, ok := module.(*Module)
if !ok { if !ok {
return nil, nil, false return nil, false
} }
if rModule.compiler == nil { return rModule, true
return nil, nil, false
}
var comp *baseCompiler
switch c := rModule.compiler.(type) {
case *libraryDecorator:
comp = c.baseCompiler
case *binaryDecorator:
comp = c.baseCompiler
case *testDecorator:
comp = c.binaryDecorator.baseCompiler
case *procMacroDecorator:
comp = c.baseCompiler
case *toolchainLibraryDecorator:
comp = c.baseCompiler
default:
return nil, nil, false
}
return rModule, comp, true
} }
// addCrate adds a crate to singleton.project.Crates ensuring that required // addCrate adds a crate to singleton.project.Crates ensuring that required
// dependencies are also added. It returns the index of the new crate in // dependencies are also added. It returns the index of the new crate in
// singleton.project.Crates // singleton.project.Crates
func (singleton *projectGeneratorSingleton) addCrate(ctx android.SingletonContext, rModule *Module, comp *baseCompiler) (int, bool) { func (singleton *projectGeneratorSingleton) addCrate(ctx android.SingletonContext, rModule *Module, deps map[string]int) (int, bool) {
rootModule, ok := crateSource(ctx, rModule, comp) rootModule, err := rModule.compiler.checkedCrateRootPath()
if !ok { if err != nil {
ctx.Errorf("Unable to find source for valid module: %v", rModule)
return 0, false return 0, false
} }
@ -233,28 +135,33 @@ func (singleton *projectGeneratorSingleton) addCrate(ctx android.SingletonContex
crate := rustProjectCrate{ crate := rustProjectCrate{
DisplayName: rModule.Name(), DisplayName: rModule.Name(),
RootModule: rootModule, RootModule: rootModule.String(),
Edition: comp.edition(), Edition: rModule.compiler.edition(),
Deps: make([]rustProjectDep, 0), Deps: make([]rustProjectDep, 0),
Cfg: make([]string, 0), Cfg: make([]string, 0),
Env: make(map[string]string), Env: make(map[string]string),
ProcMacro: procMacro, ProcMacro: procMacro,
} }
if comp.cargoOutDir().Valid() { if rModule.compiler.cargoOutDir().Valid() {
crate.Env["OUT_DIR"] = comp.cargoOutDir().String() crate.Env["OUT_DIR"] = rModule.compiler.cargoOutDir().String()
} }
for _, feature := range comp.Properties.Features { for _, feature := range rModule.compiler.features() {
crate.Cfg = append(crate.Cfg, "feature=\""+feature+"\"") crate.Cfg = append(crate.Cfg, "feature=\""+feature+"\"")
} }
deps := make(map[string]int)
singleton.mergeDependencies(ctx, rModule, &crate, deps) singleton.mergeDependencies(ctx, rModule, &crate, deps)
idx := len(singleton.project.Crates) var idx int
singleton.knownCrates[rModule.Name()] = crateInfo{Idx: idx, Deps: deps} if cInfo, ok := singleton.knownCrates[rModule.Name()]; ok {
singleton.project.Crates = append(singleton.project.Crates, crate) idx = cInfo.Idx
singleton.project.Crates[idx] = crate
} else {
idx = len(singleton.project.Crates)
singleton.project.Crates = append(singleton.project.Crates, crate)
}
singleton.knownCrates[rModule.Name()] = crateInfo{Idx: idx, Deps: deps, Device: rModule.Device()}
return idx, true return idx, true
} }
@ -262,18 +169,23 @@ func (singleton *projectGeneratorSingleton) addCrate(ctx android.SingletonContex
// It visits the dependencies of the module depth-first so the dependency ID can be added to the current module. If the // It visits the dependencies of the module depth-first so the dependency ID can be added to the current module. If the
// current module is already in singleton.knownCrates, its dependencies are merged. // current module is already in singleton.knownCrates, its dependencies are merged.
func (singleton *projectGeneratorSingleton) appendCrateAndDependencies(ctx android.SingletonContext, module android.Module) { func (singleton *projectGeneratorSingleton) appendCrateAndDependencies(ctx android.SingletonContext, module android.Module) {
rModule, comp, ok := isModuleSupported(ctx, module) rModule, ok := isModuleSupported(ctx, module)
if !ok { if !ok {
return return
} }
// If we have seen this crate already; merge any new dependencies. // If we have seen this crate already; merge any new dependencies.
if cInfo, ok := singleton.knownCrates[module.Name()]; ok { if cInfo, ok := singleton.knownCrates[module.Name()]; ok {
// If we have a new device variant, override the old one
if !cInfo.Device && rModule.Device() {
singleton.addCrate(ctx, rModule, cInfo.Deps)
return
}
crate := singleton.project.Crates[cInfo.Idx] crate := singleton.project.Crates[cInfo.Idx]
singleton.mergeDependencies(ctx, rModule, &crate, cInfo.Deps) singleton.mergeDependencies(ctx, rModule, &crate, cInfo.Deps)
singleton.project.Crates[cInfo.Idx] = crate singleton.project.Crates[cInfo.Idx] = crate
return return
} }
singleton.addCrate(ctx, rModule, comp) singleton.addCrate(ctx, rModule, make(map[string]int))
} }
func (singleton *projectGeneratorSingleton) GenerateBuildActions(ctx android.SingletonContext) { func (singleton *projectGeneratorSingleton) GenerateBuildActions(ctx android.SingletonContext) {