Loading rust/project_json.go +63 −37 Original line number Diff line number Diff line Loading @@ -30,16 +30,6 @@ import ( // // $ SOONG_GEN_RUST_PROJECT=1 m nothing func init() { android.RegisterSingletonType("rust_project_generator", rustProjectGeneratorSingleton) } func rustProjectGeneratorSingleton() android.Singleton { return &projectGeneratorSingleton{} } type projectGeneratorSingleton struct{} const ( // Environment variables used to control the behavior of this singleton. envVariableCollectRustDeps = "SOONG_GEN_RUST_PROJECT" Loading @@ -49,6 +39,7 @@ const ( // The format of rust-project.json is not yet finalized. A current description is available at: // https://github.com/rust-analyzer/rust-analyzer/blob/master/docs/user/manual.adoc#non-cargo-based-projects type rustProjectDep struct { // The Crate attribute is the index of the dependency in the Crates array in rustProjectJson. Crate int `json:"crate"` Name string `json:"name"` } Loading @@ -71,12 +62,50 @@ type crateInfo struct { Deps map[string]int } func mergeDependencies(ctx android.SingletonContext, project *rustProjectJson, knownCrates map[string]crateInfo, module android.Module, crate *rustProjectCrate, deps map[string]int) { type projectGeneratorSingleton struct { project rustProjectJson knownCrates map[string]crateInfo } func rustProjectGeneratorSingleton() android.Singleton { return &projectGeneratorSingleton{} } func init() { android.RegisterSingletonType("rust_project_generator", rustProjectGeneratorSingleton) } // librarySource finds the main source file (.rs) for a crate. func librarySource(ctx android.SingletonContext, rModule *Module, rustLib *libraryDecorator) (string, bool) { srcs := rustLib.baseCompiler.Properties.Srcs if len(srcs) != 0 { return path.Join(ctx.ModuleDir(rModule), srcs[0]), true } if !rustLib.source() { 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 } default: if rModule.Target().String() != ctx.Config().Targets[android.Android][0].String() { return "", false } } src := rustLib.sourceProvider.Srcs()[0] return src.String(), true } func (singleton *projectGeneratorSingleton) mergeDependencies(ctx android.SingletonContext, module android.Module, crate *rustProjectCrate, deps map[string]int) { ctx.VisitDirectDeps(module, func(child android.Module) { childId, childCrateName, ok := appendLibraryAndDeps(ctx, project, knownCrates, child) childId, childCrateName, ok := singleton.appendLibraryAndDeps(ctx, child) if !ok { return } Loading @@ -88,12 +117,10 @@ func mergeDependencies(ctx android.SingletonContext, project *rustProjectJson, }) } // appendLibraryAndDeps creates a rustProjectCrate for the module argument and // appends it to the rustProjectJson struct. It visits the dependencies of the // module depth-first. If the current module is already in knownCrates, its // dependencies are merged. Returns a tuple (id, crate_name, ok). func appendLibraryAndDeps(ctx android.SingletonContext, project *rustProjectJson, knownCrates map[string]crateInfo, module android.Module) (int, string, bool) { // 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) { rModule, ok := module.(*Module) if !ok { return 0, "", false Loading @@ -107,46 +134,45 @@ func appendLibraryAndDeps(ctx android.SingletonContext, project *rustProjectJson } moduleName := ctx.ModuleName(module) crateName := rModule.CrateName() if cInfo, ok := knownCrates[moduleName]; ok { if cInfo, ok := singleton.knownCrates[moduleName]; ok { // We have seen this crate already; merge any new dependencies. crate := project.Crates[cInfo.ID] mergeDependencies(ctx, project, knownCrates, module, &crate, cInfo.Deps) project.Crates[cInfo.ID] = crate 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)} srcs := rustLib.baseCompiler.Properties.Srcs if len(srcs) == 0 { rootModule, ok := librarySource(ctx, rModule, rustLib) if !ok { return 0, "", false } crate.RootModule = path.Join(ctx.ModuleDir(rModule), srcs[0]) crate.RootModule = rootModule crate.Edition = rustLib.baseCompiler.edition() deps := make(map[string]int) mergeDependencies(ctx, project, knownCrates, module, &crate, deps) singleton.mergeDependencies(ctx, module, &crate, deps) id := len(project.Crates) knownCrates[moduleName] = crateInfo{ID: id, Deps: deps} project.Crates = append(project.Crates, crate) id := len(singleton.project.Crates) singleton.knownCrates[moduleName] = crateInfo{ID: id, 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. project.Roots = append(project.Roots, path.Dir(crate.RootModule)) singleton.project.Roots = append(singleton.project.Roots, path.Dir(crate.RootModule)) return id, crateName, true } func (r *projectGeneratorSingleton) GenerateBuildActions(ctx android.SingletonContext) { func (singleton *projectGeneratorSingleton) GenerateBuildActions(ctx android.SingletonContext) { if !ctx.Config().IsEnvTrue(envVariableCollectRustDeps) { return } project := rustProjectJson{} knownCrates := make(map[string]crateInfo) singleton.knownCrates = make(map[string]crateInfo) ctx.VisitAllModules(func(module android.Module) { appendLibraryAndDeps(ctx, &project, knownCrates, module) singleton.appendLibraryAndDeps(ctx, module) }) path := android.PathForOutput(ctx, rustProjectJsonFileName) err := createJsonFile(project, path) err := createJsonFile(singleton.project, path) if err != nil { ctx.Errorf(err.Error()) } Loading rust/project_json_test.go +34 −5 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ import ( "encoding/json" "io/ioutil" "path/filepath" "strings" "testing" "android/soong/android" Loading Loading @@ -100,22 +101,50 @@ func TestProjectJsonBindGen(t *testing.T) { rust_library { name: "liba", srcs: ["src/lib.rs"], rlibs: ["libbindings"], rlibs: ["libbindings1"], crate_name: "a" } rust_bindgen { name: "libbindings", crate_name: "bindings", source_stem: "bindings", name: "libbindings1", crate_name: "bindings1", source_stem: "bindings1", host_supported: true, wrapper_src: "src/any.h", } rust_library_host { name: "libb", srcs: ["src/lib.rs"], rustlibs: ["libbindings2"], crate_name: "b" } rust_bindgen_host { name: "libbindings2", crate_name: "bindings2", source_stem: "bindings2", wrapper_src: "src/any.h", } ` + GatherRequiredDepsForTest() fs := map[string][]byte{ "src/lib.rs": nil, } jsonContent := testProjectJson(t, bp, fs) validateJsonCrates(t, jsonContent) crates := validateJsonCrates(t, jsonContent) for _, c := range crates { crate, ok := c.(map[string]interface{}) if !ok { t.Fatalf("Unexpected type for crate: %v", c) } rootModule, ok := crate["root_module"].(string) if !ok { t.Fatalf("Unexpected type for root_module: %v", crate["root_module"]) } if strings.Contains(rootModule, "libbindings1") && !strings.Contains(rootModule, "android_arm64") { t.Errorf("The source for libbindings1 does not contain android_arm64, got %v", rootModule) } if strings.Contains(rootModule, "libbindings2") && !strings.Contains(rootModule, "linux_glibc") { t.Errorf("The source for libbindings2 does not contain linux_glibc, got %v", rootModule) } } } func TestProjectJsonMultiVersion(t *testing.T) { Loading rust/testing.go +1 −0 Original line number Diff line number Diff line Loading @@ -117,6 +117,7 @@ func CreateTestContext() *android.TestContext { ctx.RegisterModuleType("rust_binary", RustBinaryFactory) ctx.RegisterModuleType("rust_binary_host", RustBinaryHostFactory) ctx.RegisterModuleType("rust_bindgen", RustBindgenFactory) ctx.RegisterModuleType("rust_bindgen_host", RustBindgenHostFactory) ctx.RegisterModuleType("rust_test", RustTestFactory) ctx.RegisterModuleType("rust_test_host", RustTestHostFactory) ctx.RegisterModuleType("rust_library", RustLibraryFactory) Loading Loading
rust/project_json.go +63 −37 Original line number Diff line number Diff line Loading @@ -30,16 +30,6 @@ import ( // // $ SOONG_GEN_RUST_PROJECT=1 m nothing func init() { android.RegisterSingletonType("rust_project_generator", rustProjectGeneratorSingleton) } func rustProjectGeneratorSingleton() android.Singleton { return &projectGeneratorSingleton{} } type projectGeneratorSingleton struct{} const ( // Environment variables used to control the behavior of this singleton. envVariableCollectRustDeps = "SOONG_GEN_RUST_PROJECT" Loading @@ -49,6 +39,7 @@ const ( // The format of rust-project.json is not yet finalized. A current description is available at: // https://github.com/rust-analyzer/rust-analyzer/blob/master/docs/user/manual.adoc#non-cargo-based-projects type rustProjectDep struct { // The Crate attribute is the index of the dependency in the Crates array in rustProjectJson. Crate int `json:"crate"` Name string `json:"name"` } Loading @@ -71,12 +62,50 @@ type crateInfo struct { Deps map[string]int } func mergeDependencies(ctx android.SingletonContext, project *rustProjectJson, knownCrates map[string]crateInfo, module android.Module, crate *rustProjectCrate, deps map[string]int) { type projectGeneratorSingleton struct { project rustProjectJson knownCrates map[string]crateInfo } func rustProjectGeneratorSingleton() android.Singleton { return &projectGeneratorSingleton{} } func init() { android.RegisterSingletonType("rust_project_generator", rustProjectGeneratorSingleton) } // librarySource finds the main source file (.rs) for a crate. func librarySource(ctx android.SingletonContext, rModule *Module, rustLib *libraryDecorator) (string, bool) { srcs := rustLib.baseCompiler.Properties.Srcs if len(srcs) != 0 { return path.Join(ctx.ModuleDir(rModule), srcs[0]), true } if !rustLib.source() { 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 } default: if rModule.Target().String() != ctx.Config().Targets[android.Android][0].String() { return "", false } } src := rustLib.sourceProvider.Srcs()[0] return src.String(), true } func (singleton *projectGeneratorSingleton) mergeDependencies(ctx android.SingletonContext, module android.Module, crate *rustProjectCrate, deps map[string]int) { ctx.VisitDirectDeps(module, func(child android.Module) { childId, childCrateName, ok := appendLibraryAndDeps(ctx, project, knownCrates, child) childId, childCrateName, ok := singleton.appendLibraryAndDeps(ctx, child) if !ok { return } Loading @@ -88,12 +117,10 @@ func mergeDependencies(ctx android.SingletonContext, project *rustProjectJson, }) } // appendLibraryAndDeps creates a rustProjectCrate for the module argument and // appends it to the rustProjectJson struct. It visits the dependencies of the // module depth-first. If the current module is already in knownCrates, its // dependencies are merged. Returns a tuple (id, crate_name, ok). func appendLibraryAndDeps(ctx android.SingletonContext, project *rustProjectJson, knownCrates map[string]crateInfo, module android.Module) (int, string, bool) { // 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) { rModule, ok := module.(*Module) if !ok { return 0, "", false Loading @@ -107,46 +134,45 @@ func appendLibraryAndDeps(ctx android.SingletonContext, project *rustProjectJson } moduleName := ctx.ModuleName(module) crateName := rModule.CrateName() if cInfo, ok := knownCrates[moduleName]; ok { if cInfo, ok := singleton.knownCrates[moduleName]; ok { // We have seen this crate already; merge any new dependencies. crate := project.Crates[cInfo.ID] mergeDependencies(ctx, project, knownCrates, module, &crate, cInfo.Deps) project.Crates[cInfo.ID] = crate 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)} srcs := rustLib.baseCompiler.Properties.Srcs if len(srcs) == 0 { rootModule, ok := librarySource(ctx, rModule, rustLib) if !ok { return 0, "", false } crate.RootModule = path.Join(ctx.ModuleDir(rModule), srcs[0]) crate.RootModule = rootModule crate.Edition = rustLib.baseCompiler.edition() deps := make(map[string]int) mergeDependencies(ctx, project, knownCrates, module, &crate, deps) singleton.mergeDependencies(ctx, module, &crate, deps) id := len(project.Crates) knownCrates[moduleName] = crateInfo{ID: id, Deps: deps} project.Crates = append(project.Crates, crate) id := len(singleton.project.Crates) singleton.knownCrates[moduleName] = crateInfo{ID: id, 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. project.Roots = append(project.Roots, path.Dir(crate.RootModule)) singleton.project.Roots = append(singleton.project.Roots, path.Dir(crate.RootModule)) return id, crateName, true } func (r *projectGeneratorSingleton) GenerateBuildActions(ctx android.SingletonContext) { func (singleton *projectGeneratorSingleton) GenerateBuildActions(ctx android.SingletonContext) { if !ctx.Config().IsEnvTrue(envVariableCollectRustDeps) { return } project := rustProjectJson{} knownCrates := make(map[string]crateInfo) singleton.knownCrates = make(map[string]crateInfo) ctx.VisitAllModules(func(module android.Module) { appendLibraryAndDeps(ctx, &project, knownCrates, module) singleton.appendLibraryAndDeps(ctx, module) }) path := android.PathForOutput(ctx, rustProjectJsonFileName) err := createJsonFile(project, path) err := createJsonFile(singleton.project, path) if err != nil { ctx.Errorf(err.Error()) } Loading
rust/project_json_test.go +34 −5 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ import ( "encoding/json" "io/ioutil" "path/filepath" "strings" "testing" "android/soong/android" Loading Loading @@ -100,22 +101,50 @@ func TestProjectJsonBindGen(t *testing.T) { rust_library { name: "liba", srcs: ["src/lib.rs"], rlibs: ["libbindings"], rlibs: ["libbindings1"], crate_name: "a" } rust_bindgen { name: "libbindings", crate_name: "bindings", source_stem: "bindings", name: "libbindings1", crate_name: "bindings1", source_stem: "bindings1", host_supported: true, wrapper_src: "src/any.h", } rust_library_host { name: "libb", srcs: ["src/lib.rs"], rustlibs: ["libbindings2"], crate_name: "b" } rust_bindgen_host { name: "libbindings2", crate_name: "bindings2", source_stem: "bindings2", wrapper_src: "src/any.h", } ` + GatherRequiredDepsForTest() fs := map[string][]byte{ "src/lib.rs": nil, } jsonContent := testProjectJson(t, bp, fs) validateJsonCrates(t, jsonContent) crates := validateJsonCrates(t, jsonContent) for _, c := range crates { crate, ok := c.(map[string]interface{}) if !ok { t.Fatalf("Unexpected type for crate: %v", c) } rootModule, ok := crate["root_module"].(string) if !ok { t.Fatalf("Unexpected type for root_module: %v", crate["root_module"]) } if strings.Contains(rootModule, "libbindings1") && !strings.Contains(rootModule, "android_arm64") { t.Errorf("The source for libbindings1 does not contain android_arm64, got %v", rootModule) } if strings.Contains(rootModule, "libbindings2") && !strings.Contains(rootModule, "linux_glibc") { t.Errorf("The source for libbindings2 does not contain linux_glibc, got %v", rootModule) } } } func TestProjectJsonMultiVersion(t *testing.T) { Loading
rust/testing.go +1 −0 Original line number Diff line number Diff line Loading @@ -117,6 +117,7 @@ func CreateTestContext() *android.TestContext { ctx.RegisterModuleType("rust_binary", RustBinaryFactory) ctx.RegisterModuleType("rust_binary_host", RustBinaryHostFactory) ctx.RegisterModuleType("rust_bindgen", RustBindgenFactory) ctx.RegisterModuleType("rust_bindgen_host", RustBindgenHostFactory) ctx.RegisterModuleType("rust_test", RustTestFactory) ctx.RegisterModuleType("rust_test_host", RustTestHostFactory) ctx.RegisterModuleType("rust_library", RustLibraryFactory) Loading