diff options
author | ThiƩbaud Weksteen <tweek@google.com> | 2020-12-07 13:40:19 +0100 |
---|---|---|
committer | ThiƩbaud Weksteen <tweek@google.com> | 2020-12-07 14:45:09 +0100 |
commit | fa5feae43c3102ce62efcb6fe305e37b523d269b (patch) | |
tree | dcaa441cc9ff218ed21dd9902aad8b144f869c99 /rust/project_json.go | |
parent | 5e2697117f3381c30de36df8789109d82cbea326 (diff) |
Add dependencies for source-generated crates
When using SourceProviders, the dependency tree does not include
directly the source variant, only the built variant. For instance:
liba --> libbingena_rlib --> libbingena_source
However, libbindgena_rlib did not have a source associated with the
module, and was therefore not added as a dependency. Modify the logic so
that a SourceProvider library will find the right variant and always
have a source defined.
Adds display_name fields to the crate description to ease debugging.
Test: rust-analyzer analysis-stats .
Bug: 174158339
Change-Id: Id65708d57cd176f7e1da353f4a5f7ad65b003090
Diffstat (limited to 'rust/project_json.go')
-rw-r--r-- | rust/project_json.go | 206 |
1 files changed, 146 insertions, 60 deletions
diff --git a/rust/project_json.go b/rust/project_json.go index c4d60ad53..32ce6f4b0 100644 --- a/rust/project_json.go +++ b/rust/project_json.go @@ -45,10 +45,11 @@ type rustProjectDep struct { } type rustProjectCrate struct { - RootModule string `json:"root_module"` - Edition string `json:"edition,omitempty"` - Deps []rustProjectDep `json:"deps"` - Cfgs []string `json:"cfgs"` + DisplayName string `json:"display_name"` + RootModule string `json:"root_module"` + Edition string `json:"edition,omitempty"` + Deps []rustProjectDep `json:"deps"` + Cfgs []string `json:"cfgs"` } type rustProjectJson struct { @@ -58,13 +59,13 @@ type rustProjectJson struct { // crateInfo is used during the processing to keep track of the known crates. type crateInfo struct { - ID int - Deps map[string]int + 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. } type projectGeneratorSingleton struct { project rustProjectJson - knownCrates map[string]crateInfo + knownCrates map[string]crateInfo // Keys are module names. } func rustProjectGeneratorSingleton() android.Singleton { @@ -75,66 +76,129 @@ func init() { android.RegisterSingletonType("rust_project_generator", rustProjectGeneratorSingleton) } -// crateSource finds the main source file (.rs) for a crate. -func crateSource(ctx android.SingletonContext, rModule *Module, comp *baseCompiler) (string, bool) { - srcs := comp.Properties.Srcs - if len(srcs) != 0 { - return path.Join(ctx.ModuleDir(rModule), srcs[0]), true - } +// 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() { + 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 } - // It is a SourceProvider module. If this module is host only, uses the variation for the host. - // Otherwise, use the variation for the primary target. - switch rModule.hod { - case android.HostSupported: - case android.HostSupportedNoCross: - if rModule.Target().String() != ctx.Config().BuildOSTarget.String() { - 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 } - default: - if rModule.Target().String() != ctx.Config().AndroidFirstDeviceTarget.String() { - return "", false + } + 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 { + fmt.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) } - src := rustLib.sourceProvider.Srcs()[0] - return src.String(), true + return "", false } +// mergeDependencies visits all the dependencies for module and updates crate and deps +// with any new dependency. func (singleton *projectGeneratorSingleton) mergeDependencies(ctx android.SingletonContext, - module android.Module, crate *rustProjectCrate, deps map[string]int) { + module *Module, crate *rustProjectCrate, deps map[string]int) { ctx.VisitDirectDeps(module, func(child android.Module) { - childId, childCrateName, ok := singleton.appendLibraryAndDeps(ctx, child) - if !ok { - return - } // Skip intra-module dependencies (i.e., generated-source library depending on the source variant). if module.Name() == child.Name() { return } - if _, ok = deps[ctx.ModuleName(child)]; ok { + // Skip unsupported modules. + rChild, compChild, ok := isModuleSupported(ctx, child) + if !ok { + return + } + // For unknown dependency, add it first. + var childId int + cInfo, known := singleton.knownCrates[rChild.Name()] + if !known { + childId, ok = singleton.addCrate(ctx, rChild, compChild) + if !ok { + return + } + } else { + childId = cInfo.Idx + } + // Is this dependency known already? + if _, ok = deps[child.Name()]; ok { return } - crate.Deps = append(crate.Deps, rustProjectDep{Crate: childId, Name: childCrateName}) - deps[ctx.ModuleName(child)] = childId + crate.Deps = append(crate.Deps, rustProjectDep{Crate: childId, Name: rChild.CrateName()}) + deps[child.Name()] = childId }) } -// appendLibraryAndDeps creates a rustProjectCrate for the module argument and appends it to singleton.project. -// 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. Returns a tuple (id, crate_name, ok). -func (singleton *projectGeneratorSingleton) appendLibraryAndDeps(ctx android.SingletonContext, module android.Module) (int, string, bool) { +// isModuleSupported returns the RustModule and baseCompiler if the module +// should be considered for inclusion in rust-project.json. +func isModuleSupported(ctx android.SingletonContext, module android.Module) (*Module, *baseCompiler, bool) { rModule, ok := module.(*Module) if !ok { - return 0, "", false + return nil, nil, false } if rModule.compiler == nil { - return 0, "", false + return nil, nil, false } var comp *baseCompiler switch c := rModule.compiler.(type) { @@ -145,35 +209,57 @@ func (singleton *projectGeneratorSingleton) appendLibraryAndDeps(ctx android.Sin case *testDecorator: comp = c.binaryDecorator.baseCompiler default: - return 0, "", false - } - moduleName := ctx.ModuleName(module) - crateName := rModule.CrateName() - if cInfo, ok := singleton.knownCrates[moduleName]; ok { - // We have seen this crate already; merge any new dependencies. - crate := singleton.project.Crates[cInfo.ID] - singleton.mergeDependencies(ctx, module, &crate, cInfo.Deps) - singleton.project.Crates[cInfo.ID] = crate - return cInfo.ID, crateName, true - } - crate := rustProjectCrate{Deps: make([]rustProjectDep, 0), Cfgs: make([]string, 0)} + return nil, nil, false + } + return rModule, comp, true +} + +// addCrate adds a crate to singleton.project.Crates ensuring that required +// dependencies are also added. It returns the index of the new crate in +// singleton.project.Crates +func (singleton *projectGeneratorSingleton) addCrate(ctx android.SingletonContext, rModule *Module, comp *baseCompiler) (int, bool) { rootModule, ok := crateSource(ctx, rModule, comp) if !ok { - return 0, "", false + fmt.Errorf("Unable to find source for valid module: %v", rModule) + return 0, false + } + + crate := rustProjectCrate{ + DisplayName: rModule.Name(), + RootModule: rootModule, + Edition: comp.edition(), + Deps: make([]rustProjectDep, 0), + Cfgs: make([]string, 0), } - crate.RootModule = rootModule - crate.Edition = comp.edition() deps := make(map[string]int) - singleton.mergeDependencies(ctx, module, &crate, deps) + singleton.mergeDependencies(ctx, rModule, &crate, deps) - id := len(singleton.project.Crates) - singleton.knownCrates[moduleName] = crateInfo{ID: id, Deps: deps} + idx := len(singleton.project.Crates) + singleton.knownCrates[rModule.Name()] = crateInfo{Idx: idx, Deps: deps} singleton.project.Crates = append(singleton.project.Crates, crate) // rust-analyzer requires that all crates belong to at least one root: // https://github.com/rust-analyzer/rust-analyzer/issues/4735. singleton.project.Roots = append(singleton.project.Roots, path.Dir(crate.RootModule)) - return id, crateName, true + return idx, true +} + +// appendCrateAndDependencies creates a rustProjectCrate for the module argument and appends it to singleton.project. +// 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. +func (singleton *projectGeneratorSingleton) appendCrateAndDependencies(ctx android.SingletonContext, module android.Module) { + rModule, comp, ok := isModuleSupported(ctx, module) + if !ok { + return + } + // If we have seen this crate already; merge any new dependencies. + if cInfo, ok := singleton.knownCrates[module.Name()]; ok { + crate := singleton.project.Crates[cInfo.Idx] + singleton.mergeDependencies(ctx, rModule, &crate, cInfo.Deps) + singleton.project.Crates[cInfo.Idx] = crate + return + } + singleton.addCrate(ctx, rModule, comp) } func (singleton *projectGeneratorSingleton) GenerateBuildActions(ctx android.SingletonContext) { @@ -183,7 +269,7 @@ func (singleton *projectGeneratorSingleton) GenerateBuildActions(ctx android.Sin singleton.knownCrates = make(map[string]crateInfo) ctx.VisitAllModules(func(module android.Module) { - singleton.appendLibraryAndDeps(ctx, module) + singleton.appendCrateAndDependencies(ctx, module) }) path := android.PathForOutput(ctx, rustProjectJsonFileName) |