diff options
-rw-r--r-- | cc/vendor_snapshot.go | 8 | ||||
-rw-r--r-- | java/droiddoc.go | 54 | ||||
-rw-r--r-- | java/java_test.go | 56 | ||||
-rw-r--r-- | rust/Android.bp | 1 | ||||
-rw-r--r-- | rust/bindgen.go | 25 | ||||
-rw-r--r-- | rust/bindgen_test.go | 26 | ||||
-rw-r--r-- | rust/project_json.go | 21 | ||||
-rw-r--r-- | rust/project_json_test.go | 142 | ||||
-rw-r--r-- | rust/rust.go | 16 | ||||
-rw-r--r-- | rust/source_provider.go | 13 | ||||
-rw-r--r-- | rust/source_provider_test.go | 31 | ||||
-rw-r--r-- | rust/test.go | 11 |
12 files changed, 340 insertions, 64 deletions
diff --git a/cc/vendor_snapshot.go b/cc/vendor_snapshot.go index 0af22580a..6df940c79 100644 --- a/cc/vendor_snapshot.go +++ b/cc/vendor_snapshot.go @@ -484,18 +484,22 @@ type vendorSnapshotSingleton struct { var ( // Modules under following directories are ignored. They are OEM's and vendor's - // proprietary modules(device/, vendor/, and hardware/). + // proprietary modules(device/, kernel/, vendor/, and hardware/). // TODO(b/65377115): Clean up these with more maintainable way vendorProprietaryDirs = []string{ "device", + "kernel", "vendor", "hardware", } // Modules under following directories are included as they are in AOSP, - // although hardware/ is normally for vendor's own. + // although hardware/ and kernel/ are normally for vendor's own. // TODO(b/65377115): Clean up these with more maintainable way aospDirsUnderProprietary = []string{ + "kernel/configs", + "kernel/prebuilts", + "kernel/tests", "hardware/interfaces", "hardware/libhardware", "hardware/libhardware_legacy", diff --git a/java/droiddoc.go b/java/droiddoc.go index d2f8d8331..0840d5040 100644 --- a/java/droiddoc.go +++ b/java/droiddoc.go @@ -294,6 +294,9 @@ type DroidstubsProperties struct { // the dirs which Metalava extracts API levels annotations from. Api_levels_annotations_dirs []string + // the filename which Metalava extracts API levels annotations from. Defaults to android.jar. + Api_levels_jar_filename *string + // if set to true, collect the values used by the Dev tools and // write them in files packaged with the SDK. Defaults to false. Write_sdk_values *bool @@ -1407,34 +1410,37 @@ func (d *Droidstubs) inclusionAnnotationsFlags(ctx android.ModuleContext, cmd *a } func (d *Droidstubs) apiLevelsAnnotationsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand) { - if Bool(d.properties.Api_levels_annotations_enabled) { - d.apiVersionsXml = android.PathForModuleOut(ctx, "api-versions.xml") + if !Bool(d.properties.Api_levels_annotations_enabled) { + return + } - if len(d.properties.Api_levels_annotations_dirs) == 0 { - ctx.PropertyErrorf("api_levels_annotations_dirs", - "has to be non-empty if api levels annotations was enabled!") - } + d.apiVersionsXml = android.PathForModuleOut(ctx, "api-versions.xml") + + if len(d.properties.Api_levels_annotations_dirs) == 0 { + ctx.PropertyErrorf("api_levels_annotations_dirs", + "has to be non-empty if api levels annotations was enabled!") + } + + cmd.FlagWithOutput("--generate-api-levels ", d.apiVersionsXml) + cmd.FlagWithInput("--apply-api-levels ", d.apiVersionsXml) + cmd.FlagWithArg("--current-version ", ctx.Config().PlatformSdkVersion()) + cmd.FlagWithArg("--current-codename ", ctx.Config().PlatformSdkCodename()) + + filename := proptools.StringDefault(d.properties.Api_levels_jar_filename, "android.jar") - cmd.FlagWithOutput("--generate-api-levels ", d.apiVersionsXml) - cmd.FlagWithInput("--apply-api-levels ", d.apiVersionsXml) - cmd.FlagWithArg("--current-version ", ctx.Config().PlatformSdkVersion()) - cmd.FlagWithArg("--current-codename ", ctx.Config().PlatformSdkCodename()) - - ctx.VisitDirectDepsWithTag(metalavaAPILevelsAnnotationsDirTag, func(m android.Module) { - if t, ok := m.(*ExportedDroiddocDir); ok { - for _, dep := range t.deps { - if strings.HasSuffix(dep.String(), "android.jar") { - cmd.Implicit(dep) - } + ctx.VisitDirectDepsWithTag(metalavaAPILevelsAnnotationsDirTag, func(m android.Module) { + if t, ok := m.(*ExportedDroiddocDir); ok { + for _, dep := range t.deps { + if strings.HasSuffix(dep.String(), filename) { + cmd.Implicit(dep) } - cmd.FlagWithArg("--android-jar-pattern ", t.dir.String()+"/%/public/android.jar") - } else { - ctx.PropertyErrorf("api_levels_annotations_dirs", - "module %q is not a metalava api-levels-annotations dir", ctx.OtherModuleName(m)) } - }) - - } + cmd.FlagWithArg("--android-jar-pattern ", t.dir.String()+"/%/public/"+filename) + } else { + ctx.PropertyErrorf("api_levels_annotations_dirs", + "module %q is not a metalava api-levels-annotations dir", ctx.OtherModuleName(m)) + } + }) } func (d *Droidstubs) apiToXmlFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand) { diff --git a/java/java_test.go b/java/java_test.go index a3c78540f..50c40c38c 100644 --- a/java/java_test.go +++ b/java/java_test.go @@ -1170,6 +1170,62 @@ func TestDroiddocArgsAndFlagsCausesError(t *testing.T) { `) } +func TestDroidstubs(t *testing.T) { + ctx, _ := testJavaWithFS(t, ` + droiddoc_exported_dir { + name: "droiddoc-templates-sdk", + path: ".", + } + + droidstubs { + name: "bar-stubs", + srcs: [ + "bar-doc/a.java", + ], + api_levels_annotations_dirs: [ + "droiddoc-templates-sdk", + ], + api_levels_annotations_enabled: true, + } + + droidstubs { + name: "bar-stubs-other", + srcs: [ + "bar-doc/a.java", + ], + api_levels_annotations_dirs: [ + "droiddoc-templates-sdk", + ], + api_levels_annotations_enabled: true, + api_levels_jar_filename: "android.other.jar", + } + `, + map[string][]byte{ + "bar-doc/a.java": nil, + }) + testcases := []struct { + moduleName string + expectedJarFilename string + }{ + { + moduleName: "bar-stubs", + expectedJarFilename: "android.jar", + }, + { + moduleName: "bar-stubs-other", + expectedJarFilename: "android.other.jar", + }, + } + for _, c := range testcases { + m := ctx.ModuleForTests(c.moduleName, "android_common") + metalava := m.Rule("metalava") + expected := "--android-jar-pattern ./%/public/" + c.expectedJarFilename + if actual := metalava.RuleParams.Command; !strings.Contains(actual, expected) { + t.Errorf("For %q, expected metalava argument %q, but was not found %q", c.moduleName, expected, actual) + } + } +} + func TestDroidstubsWithSystemModules(t *testing.T) { ctx, _ := testJava(t, ` droidstubs { diff --git a/rust/Android.bp b/rust/Android.bp index e03bf4f6c..bbeb28d0b 100644 --- a/rust/Android.bp +++ b/rust/Android.bp @@ -34,6 +34,7 @@ bootstrap_go_package { "library_test.go", "project_json_test.go", "rust_test.go", + "source_provider_test.go", "test_test.go", ], pluginFor: ["soong_build"], diff --git a/rust/bindgen.go b/rust/bindgen.go index 403f4665a..2224a9cca 100644 --- a/rust/bindgen.go +++ b/rust/bindgen.go @@ -41,12 +41,12 @@ var ( bindgen = pctx.AndroidStaticRule("bindgen", blueprint.RuleParams{ Command: "CLANG_PATH=$bindgenClang LIBCLANG_PATH=$bindgenLibClang RUSTFMT=${config.RustBin}/rustfmt " + - "$bindgenCmd $flags $in -o $out -- -MD -MF $out.d $cflags", - CommandDeps: []string{"$bindgenCmd"}, + "$cmd $flags $in -o $out -- -MD -MF $out.d $cflags", + CommandDeps: []string{"$cmd"}, Deps: blueprint.DepsGCC, Depfile: "$out.d", }, - "flags", "cflags") + "cmd", "flags", "cflags") ) func init() { @@ -76,6 +76,12 @@ type BindgenProperties struct { // list of shared libraries that provide headers for this binding. Shared_libs []string `android:"arch_variant"` + // module name of a custom binary/script which should be used instead of the 'bindgen' binary. This custom + // binary must expect arguments in a similar fashion to bindgen, e.g. + // + // "my_bindgen [flags] wrapper_header.h -o [output_path] -- [clang flags]" + Custom_bindgen string `android:"path"` + //TODO(b/161141999) Add support for headers from cc_library_header modules. } @@ -130,17 +136,28 @@ func (b *bindgenDecorator) generateSource(ctx android.ModuleContext, deps PathDe outputFile := android.PathForModuleOut(ctx, b.baseSourceProvider.getStem(ctx)+".rs") + var cmd, cmdDesc string + if b.Properties.Custom_bindgen != "" { + cmd = ctx.GetDirectDepWithTag(b.Properties.Custom_bindgen, customBindgenDepTag).(*Module).HostToolPath().String() + cmdDesc = b.Properties.Custom_bindgen + } else { + cmd = "$bindgenCmd" + cmdDesc = "bindgen" + } + ctx.Build(pctx, android.BuildParams{ Rule: bindgen, - Description: "bindgen " + wrapperFile.Path().Rel(), + Description: strings.Join([]string{cmdDesc, wrapperFile.Path().Rel()}, " "), Output: outputFile, Input: wrapperFile.Path(), Implicits: implicits, Args: map[string]string{ + "cmd": cmd, "flags": strings.Join(bindgenFlags, " "), "cflags": strings.Join(cflags, " "), }, }) + b.baseSourceProvider.outputFile = outputFile return outputFile } diff --git a/rust/bindgen_test.go b/rust/bindgen_test.go index c42834843..0b529ca5a 100644 --- a/rust/bindgen_test.go +++ b/rust/bindgen_test.go @@ -55,3 +55,29 @@ func TestRustBindgen(t *testing.T) { t.Errorf("missing static_libs exported includes in rust_bindgen rule: cflags %#v", libbindgen.Args["cflags"]) } } + +func TestRustBindgenCustomBindgen(t *testing.T) { + ctx := testRust(t, ` + rust_bindgen { + name: "libbindgen", + wrapper_src: "src/any.h", + crate_name: "bindgen", + stem: "libbindgen", + source_stem: "bindings", + custom_bindgen: "my_bindgen" + } + rust_binary_host { + name: "my_bindgen", + srcs: ["foo.rs"], + } + `) + + libbindgen := ctx.ModuleForTests("libbindgen", "android_arm64_armv8-a").Output("bindings.rs") + + // The rule description should contain the custom binary name rather than bindgen, so checking the description + // should be sufficient. + if !strings.Contains(libbindgen.Description, "my_bindgen") { + t.Errorf("Custom bindgen binary %s not used for libbindgen: rule description %#v", "my_bindgen", + libbindgen.Description) + } +} diff --git a/rust/project_json.go b/rust/project_json.go index 41dd194ee..831047938 100644 --- a/rust/project_json.go +++ b/rust/project_json.go @@ -75,17 +75,16 @@ func mergeDependencies(ctx android.SingletonContext, project *rustProjectJson, knownCrates map[string]crateInfo, module android.Module, crate *rustProjectCrate, deps map[string]int) { - //TODO(tweek): The stdlib dependencies do not appear here. We need to manually add them. ctx.VisitDirectDeps(module, func(child android.Module) { - childId, childName, ok := appendLibraryAndDeps(ctx, project, knownCrates, child) + childId, childCrateName, ok := appendLibraryAndDeps(ctx, project, knownCrates, child) if !ok { return } - if _, ok = deps[childName]; ok { + if _, ok = deps[ctx.ModuleName(child)]; ok { return } - crate.Deps = append(crate.Deps, rustProjectDep{Crate: childId, Name: childName}) - deps[childName] = childId + crate.Deps = append(crate.Deps, rustProjectDep{Crate: childId, Name: childCrateName}) + deps[ctx.ModuleName(child)] = childId }) } @@ -106,8 +105,9 @@ func appendLibraryAndDeps(ctx android.SingletonContext, project *rustProjectJson if !ok { return 0, "", false } + moduleName := ctx.ModuleName(module) crateName := rModule.CrateName() - if cInfo, ok := knownCrates[crateName]; ok { + if cInfo, ok := 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) @@ -115,15 +115,18 @@ func appendLibraryAndDeps(ctx android.SingletonContext, project *rustProjectJson return cInfo.ID, crateName, true } crate := rustProjectCrate{Deps: make([]rustProjectDep, 0), Cfgs: make([]string, 0)} - src := rustLib.baseCompiler.Properties.Srcs[0] - crate.RootModule = path.Join(ctx.ModuleDir(rModule), src) + srcs := rustLib.baseCompiler.Properties.Srcs + if len(srcs) == 0 { + return 0, "", false + } + crate.RootModule = path.Join(ctx.ModuleDir(rModule), srcs[0]) crate.Edition = rustLib.baseCompiler.edition() deps := make(map[string]int) mergeDependencies(ctx, project, knownCrates, module, &crate, deps) id := len(project.Crates) - knownCrates[crateName] = crateInfo{ID: id, Deps: deps} + knownCrates[moduleName] = crateInfo{ID: id, Deps: deps} project.Crates = append(project.Crates, crate) // rust-analyzer requires that all crates belong to at least one root: // https://github.com/rust-analyzer/rust-analyzer/issues/4735. diff --git a/rust/project_json_test.go b/rust/project_json_test.go index 6786e72c7..85219404a 100644 --- a/rust/project_json_test.go +++ b/rust/project_json_test.go @@ -15,6 +15,7 @@ package rust import ( + "encoding/json" "io/ioutil" "path/filepath" "testing" @@ -23,20 +24,12 @@ import ( "android/soong/cc" ) -func TestProjectJson(t *testing.T) { - bp := `rust_library { - name: "liba", - srcs: ["src/lib.rs"], - crate_name: "a" - }` + GatherRequiredDepsForTest() - env := map[string]string{"SOONG_GEN_RUST_PROJECT": "1"} - fs := map[string][]byte{ - "foo.rs": nil, - "src/lib.rs": nil, - } - +// testProjectJson run the generation of rust-project.json. It returns the raw +// content of the generated file. +func testProjectJson(t *testing.T, bp string, fs map[string][]byte) []byte { cc.GatherRequiredFilesForTest(fs) + env := map[string]string{"SOONG_GEN_RUST_PROJECT": "1"} config := android.TestArchConfig(buildDir, env, bp, fs) ctx := CreateTestContext() ctx.Register(config) @@ -48,8 +41,131 @@ func TestProjectJson(t *testing.T) { // The JSON file is generated via WriteFileToOutputDir. Therefore, it // won't appear in the Output of the TestingSingleton. Manually verify // it exists. - _, err := ioutil.ReadFile(filepath.Join(buildDir, "rust-project.json")) + content, err := ioutil.ReadFile(filepath.Join(buildDir, rustProjectJsonFileName)) if err != nil { t.Errorf("rust-project.json has not been generated") } + return content +} + +// validateJsonCrates validates that content follows the basic structure of +// rust-project.json. It returns the crates attribute if the validation +// succeeded. +// It uses an empty interface instead of relying on a defined structure to +// avoid a strong dependency on our implementation. +func validateJsonCrates(t *testing.T, rawContent []byte) []interface{} { + var content interface{} + err := json.Unmarshal(rawContent, &content) + if err != nil { + t.Errorf("Unable to parse the rust-project.json as JSON: %v", err) + } + root, ok := content.(map[string]interface{}) + if !ok { + t.Errorf("Unexpected JSON format: %v", content) + } + if _, ok = root["crates"]; !ok { + t.Errorf("No crates attribute in rust-project.json: %v", root) + } + crates, ok := root["crates"].([]interface{}) + if !ok { + t.Errorf("Unexpected crates format: %v", root["crates"]) + } + return crates +} + +func TestProjectJsonDep(t *testing.T) { + bp := ` + rust_library { + name: "liba", + srcs: ["a/src/lib.rs"], + crate_name: "a" + } + rust_library { + name: "libb", + srcs: ["b/src/lib.rs"], + crate_name: "b", + rlibs: ["liba"], + } + ` + GatherRequiredDepsForTest() + fs := map[string][]byte{ + "a/src/lib.rs": nil, + "b/src/lib.rs": nil, + } + jsonContent := testProjectJson(t, bp, fs) + validateJsonCrates(t, jsonContent) +} + +func TestProjectJsonBindGen(t *testing.T) { + bp := ` + rust_library { + name: "liba", + srcs: ["src/lib.rs"], + rlibs: ["libbindings"], + crate_name: "a" + } + rust_bindgen { + name: "libbindings", + crate_name: "bindings", + source_stem: "bindings", + host_supported: true, + wrapper_src: "src/any.h", + } + ` + GatherRequiredDepsForTest() + fs := map[string][]byte{ + "src/lib.rs": nil, + } + jsonContent := testProjectJson(t, bp, fs) + validateJsonCrates(t, jsonContent) +} + +func TestProjectJsonMultiVersion(t *testing.T) { + bp := ` + rust_library { + name: "liba1", + srcs: ["a1/src/lib.rs"], + crate_name: "a" + } + rust_library { + name: "liba2", + srcs: ["a2/src/lib.rs"], + crate_name: "a", + } + rust_library { + name: "libb", + srcs: ["b/src/lib.rs"], + crate_name: "b", + rustlibs: ["liba1", "liba2"], + } + ` + GatherRequiredDepsForTest() + fs := map[string][]byte{ + "a1/src/lib.rs": nil, + "a2/src/lib.rs": nil, + "b/src/lib.rs": nil, + } + jsonContent := testProjectJson(t, bp, fs) + crates := validateJsonCrates(t, jsonContent) + for _, crate := range crates { + c := crate.(map[string]interface{}) + if c["root_module"] == "b/src/lib.rs" { + deps, ok := c["deps"].([]interface{}) + if !ok { + t.Errorf("Unexpected format for deps: %v", c["deps"]) + } + aCount := 0 + for _, dep := range deps { + d, ok := dep.(map[string]interface{}) + if !ok { + t.Errorf("Unexpected format for dep: %v", dep) + } + if d["name"] == "a" { + aCount++ + } + } + if aCount != 2 { + t.Errorf("Unexpected number of liba dependencies want %v, got %v: %v", 2, aCount, deps) + } + return + } + } + t.Errorf("libb crate has not been found: %v", crates) } diff --git a/rust/rust.go b/rust/rust.go index 01952477e..7ec2d736b 100644 --- a/rust/rust.go +++ b/rust/rust.go @@ -722,10 +722,11 @@ type dependencyTag struct { } var ( - rlibDepTag = dependencyTag{name: "rlibTag", library: true} - dylibDepTag = dependencyTag{name: "dylib", library: true} - procMacroDepTag = dependencyTag{name: "procMacro", proc_macro: true} - testPerSrcDepTag = dependencyTag{name: "rust_unit_tests"} + customBindgenDepTag = dependencyTag{name: "customBindgenTag"} + rlibDepTag = dependencyTag{name: "rlibTag", library: true} + dylibDepTag = dependencyTag{name: "dylib", library: true} + procMacroDepTag = dependencyTag{name: "procMacro", proc_macro: true} + testPerSrcDepTag = dependencyTag{name: "rust_unit_tests"} ) type autoDep struct { @@ -1009,6 +1010,13 @@ func (mod *Module) DepsMutator(actx android.BottomUpMutatorContext) { actx.AddVariationDependencies(commonDepVariations, cc.CrtEndDepTag, deps.CrtEnd) } + if mod.sourceProvider != nil { + if bindgen, ok := mod.sourceProvider.(*bindgenDecorator); ok && + bindgen.Properties.Custom_bindgen != "" { + actx.AddFarVariationDependencies(ctx.Config().BuildOSTarget.Variations(), customBindgenDepTag, + bindgen.Properties.Custom_bindgen) + } + } // proc_macros are compiler plugins, and so we need the host arch variant as a dependendcy. actx.AddFarVariationDependencies(ctx.Config().BuildOSTarget.Variations(), procMacroDepTag, deps.ProcMacros...) } diff --git a/rust/source_provider.go b/rust/source_provider.go index 8bb784951..503880f84 100644 --- a/rust/source_provider.go +++ b/rust/source_provider.go @@ -19,9 +19,8 @@ import ( ) type SourceProviderProperties struct { - // name for the generated source file. Defaults to module name (e.g. moduleNameFoo.rs is produced by default). - // Importantly, the inherited "stem" property for this module sets the output filename for the generated library - // variants only + // filename for the generated source file (<source_stem>.rs). This field is required. + // The inherited "stem" property sets the output filename for the generated library variants only. Source_stem *string `android:"arch_variant"` // crate name, used for the library variant of this source provider. See additional details in rust_library. @@ -65,11 +64,11 @@ func NewSourceProvider() *baseSourceProvider { } func (sp *baseSourceProvider) getStem(ctx android.ModuleContext) string { - stem := ctx.ModuleName() - if String(sp.Properties.Source_stem) != "" { - stem = String(sp.Properties.Source_stem) + if String(sp.Properties.Source_stem) == "" { + ctx.PropertyErrorf("source_stem", + "source_stem property is undefined but required for rust_bindgen modules") } - return stem + return String(sp.Properties.Source_stem) } func (sp *baseSourceProvider) sourceProviderDeps(ctx DepsContext, deps Deps) Deps { diff --git a/rust/source_provider_test.go b/rust/source_provider_test.go new file mode 100644 index 000000000..6e68ae62c --- /dev/null +++ b/rust/source_provider_test.go @@ -0,0 +1,31 @@ +// Copyright 2020 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. + +package rust + +import ( + "testing" +) + +var stemRequiredError = "source_stem property is undefined but required for rust_bindgen modules" + +func TestSourceProviderRequiredFields(t *testing.T) { + testRustError(t, stemRequiredError, ` + rust_bindgen { + name: "libbindgen", + wrapper_src: "src/any.h", + crate_name: "bindgen", + } + `) +} diff --git a/rust/test.go b/rust/test.go index e27a70cac..05c361e87 100644 --- a/rust/test.go +++ b/rust/test.go @@ -41,6 +41,9 @@ type TestProperties struct { // doesn't exist next to the Android.bp, this attribute doesn't need to be set to true // explicitly. Auto_gen_config *bool + + // if set, build with the standard Rust test harness. Defaults to true. + Test_harness *bool } // A test module is a binary module with extra --test compiler flag @@ -56,6 +59,10 @@ func (test *testDecorator) nativeCoverage() bool { return true } +func (test *testDecorator) testHarness() bool { + return BoolDefault(test.Properties.Test_harness, true) +} + func NewRustTest(hod android.HostOrDeviceSupported) (*Module, *testDecorator) { // Build both 32 and 64 targets for device tests. // Cannot build both for host tests yet if the test depends on @@ -101,7 +108,9 @@ func (test *testDecorator) install(ctx ModuleContext, file android.Path) { func (test *testDecorator) compilerFlags(ctx ModuleContext, flags Flags) Flags { flags = test.binaryDecorator.compilerFlags(ctx, flags) - flags.RustFlags = append(flags.RustFlags, "--test") + if test.testHarness() { + flags.RustFlags = append(flags.RustFlags, "--test") + } return flags } |