Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Android/build/build/soong/rust/   (Android Betriebssystem Version 17©)  Datei vom 26.5.2026 mit Größe 32 kB image not shown  

Quelle  rust_test.go   Sprache: unbekannt

 
Spracherkennung für: .go vermutete Sprache: Unknown {[0] [0] [0]} [Methode: Schwerpunktbildung, einfache Gewichte, sechs Dimensionen]

// Copyright 2019 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 (
 "os"
 "runtime"
 "strings"
 "testing"

 "github.com/google/blueprint/proptools"

 "android/soong/android"
 "android/soong/genrule"
)

func TestMain(m *testing.M) {
 os.Exit(m.Run())
}

var prepareForRustTest = android.GroupFixturePreparers(
 android.PrepareForTestWithArchMutator,
 android.PrepareForTestWithDefaults,
 android.PrepareForTestWithPrebuilts,

 genrule.PrepareForTestWithGenRuleBuildComponents,

 PrepareForIntegrationTestWithRust,

 android.PrepareForTestWithBuildFlag("RELEASE_SOONG_RUST_VARIANT_ON_DEMAND", "true"),
)

var rustMockedFiles = android.MockFS{
 "foo.rs":                       nil,
 "foo.c":                        nil,
 "src/bar.rs":                   nil,
 "src/any.h":                    nil,
 "c_includes/c_header.h":        nil,
 "rust_includes/rust_headers.h": nil,
 "proto.proto":                  nil,
 "proto/buf.proto":              nil,
 "buf.proto":                    nil,
 "foo.proto":                    nil,
 "liby.so":                      nil,
 "libz.so":                      nil,
 "data.txt":                     nil,
 "liblog.map.txt":               nil,
}

// testRust returns a TestContext in which a basic environment has been setup.
// This environment contains a few mocked files. See rustMockedFiles for the list of these files.
func testRust(t *testing.T, bp string, preparers ...android.FixturePreparer) *android.TestContext {
 t.Parallel()
 t.Helper()
 skipTestIfOsNotSupported(t)
 result := android.GroupFixturePreparers(
  prepareForRustTest,
  rustMockedFiles.AddToFixture(),
  android.GroupFixturePreparers(preparers...),
 ).
  RunTestWithBp(t, bp)
 return result.TestContext
}

const (
 sharedVendorVariant        = "android_vendor_arm64_armv8-a_shared"
 rlibVendorVariant          = "android_vendor_arm64_armv8-a_rlib_rlib-std"
 rlibDylibStdVendorVariant  = "android_vendor_arm64_armv8-a_rlib_rlib-std"
 dylibVendorVariant         = "android_vendor_arm64_armv8-a_dylib"
 sharedRecoveryVariant      = "android_recovery_arm64_armv8-a_shared"
 rlibRecoveryVariant        = "android_recovery_arm64_armv8-a_rlib_dylib-std"
 rlibRlibStdRecoveryVariant = "android_recovery_arm64_armv8-a_rlib_rlib-std"
 dylibRecoveryVariant       = "android_recovery_arm64_armv8-a_dylib"
 binaryCoreVariant          = "android_arm64_armv8-a"
 binaryVendorVariant        = "android_vendor_arm64_armv8-a"
 binaryProductVariant       = "android_product_arm64_armv8-a"
 binaryRecoveryVariant      = "android_recovery_arm64_armv8-a"
)

// testRustCov returns a TestContext in which a basic environment has been
// setup. This environment explicitly enables coverage.
func testRustCov(t *testing.T, bp string) *android.TestContext {
 t.Parallel()
 t.Helper()
 skipTestIfOsNotSupported(t)
 result := android.GroupFixturePreparers(
  prepareForRustTest,
  rustMockedFiles.AddToFixture(),
  android.FixtureModifyProductVariables(
   func(variables android.FixtureProductVariables) {
    variables.ClangCoverage = proptools.BoolPtr(true)
    variables.Native_coverage = proptools.BoolPtr(true)
    variables.NativeCoveragePaths = []string{"*"}
   },
  ),
 ).RunTestWithBp(t, bp)
 return result.TestContext
}

// testRustError ensures that at least one error was raised and its value
// matches the pattern provided. The error can be either in the parsing of the
// Blueprint or when generating the build actions.
func testRustError(t *testing.T, pattern string, bp string) {
 skipTestIfOsNotSupported(t)
 android.GroupFixturePreparers(
  prepareForRustTest,
  rustMockedFiles.AddToFixture(),
 ).
  ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(pattern)).
  RunTestWithBp(t, bp)
}

// testRustCtx is used to build a particular test environment. Unless your
// tests requires a specific setup, prefer the wrapping functions: testRust,
// testRustCov or testRustError.
type testRustCtx struct {
 bp     string
 fs     map[string][]byte
 env    map[string]string
 config *android.Config
}

func skipTestIfOsNotSupported(t *testing.T) {
 // TODO (b/140435149)
 if runtime.GOOS != "linux" {
  t.Skip("Rust Soong tests can only be run on Linux hosts currently")
 }
}

// Test that we can extract the link path from a lib path.
func TestLinkPathFromFilePath(t *testing.T) {
 barPath := android.PathForTesting("out/soong/.intermediates/external/libbar/libbar/linux_glibc_x86_64_shared/libbar.so")
 libName := linkPathFromFilePath(barPath)
 expectedResult := "out/soong/.intermediates/external/libbar/libbar/linux_glibc_x86_64_shared/"

 if libName != expectedResult {
  t.Errorf("libNameFromFilePath returned the wrong name; expected '%#v', got '%#v'", expectedResult, libName)
 }
}

// Test to make sure dependencies are being picked up correctly.
func TestDepsTracking(t *testing.T) {
 ctx := testRust(t, `
  cc_library {
   host_supported: true,
   name: "cc_stubs_dep",
  }
  cc_library_host_static {
   name: "libstatic",
  }
  cc_library_host_static {
   name: "libwholestatic",
  }
  rust_ffi_host_shared {
   name: "libshared",
   srcs: ["foo.rs"],
   crate_name: "shared",
  }
  rust_library_host_rlib {
   name: "librlib",
   srcs: ["foo.rs"],
   crate_name: "rlib",
   static_libs: ["libstatic"],
   whole_static_libs: ["libwholestatic"],
   shared_libs: ["cc_stubs_dep"],
  }
  rust_proc_macro {
   name: "libpm",
   srcs: ["foo.rs"],
   crate_name: "pm",
  }
  rust_binary_host {
   name: "fizz-buzz",
   rlibs: ["librlib"],
   proc_macros: ["libpm"],
   static_libs: ["libstatic"],
   shared_libs: ["libshared"],
   srcs: ["foo.rs"],
  }
 `)
 module := ctx.ModuleForTests(t, "fizz-buzz", "linux_glibc_x86_64").Module().(*Module)
 rustc := ctx.ModuleForTests(t, "librlib", "linux_glibc_x86_64_rlib_rlib-std").Rule("rustc")

 // Since dependencies are added to AndroidMk* properties, we can check these to see if they've been picked up.
 if !android.InList("librlib.rlib-std", module.Properties.AndroidMkRlibs) {
  t.Errorf("Rlib dependency not detected (dependency missing from AndroidMkRlibs)")
 }

 if !android.InList("libpm", module.Properties.AndroidMkProcMacroLibs) {
  t.Errorf("Proc_macro dependency not detected (dependency missing from AndroidMkProcMacroLibs)")
 }

 if !android.InList("libshared", module.transitiveAndroidMkSharedLibs.ToList()) {
  t.Errorf("Shared library dependency not detected (dependency missing from AndroidMkSharedLibs)")
 }

 if !android.InList("libstatic", module.Properties.AndroidMkStaticLibs) {
  t.Errorf("Static library dependency not detected (dependency missing from AndroidMkStaticLibs)")
 }

 if !strings.Contains(rustc.Args["rustcFlags"], "-lstatic:+whole-archive=wholestatic") {
  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 !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(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())
 }
}

func TestSourceProviderDeps(t *testing.T) {
 ctx := testRust(t, `
  rust_binary {
   name: "fizz-buzz-dep",
   srcs: [
    "foo.rs",
    ":my_generator",
    ":libbindings",
   ],
   rlibs: ["libbindings"],
  }
  rust_proc_macro {
   name: "libprocmacro",
   srcs: [
    "foo.rs",
    ":my_generator",
    ":libbindings",
   ],
   rlibs: ["libbindings"],
   crate_name: "procmacro",
  }
  rust_library {
   name: "libfoo",
   srcs: [
    "foo.rs",
    ":my_generator",
    ":libbindings",
   ],
   rlibs: ["libbindings"],
   crate_name: "foo",
   split_all_variants: true,
  }
  genrule {
   name: "my_generator",
   tools: ["any_rust_binary"],
   cmd: "$(location) -o $(out) $(in)",
   srcs: ["src/any.h"],
   out: ["src/any.rs"],
  }
  rust_binary_host {
   name: "any_rust_binary",
   srcs: [
    "foo.rs",
   ],
  }
  rust_bindgen {
   name: "libbindings",
   crate_name: "bindings",
   source_stem: "bindings",
   host_supported: true,
   wrapper_src: "src/any.h",
  }
 `)

 libfoo := ctx.ModuleForTests(t, "libfoo", "android_arm64_armv8-a_rlib_dylib-std").Rule("rustc")
 if !android.SuffixInList(libfoo.Implicits.Strings(), "/out/bindings.rs") {
  t.Errorf("rust_bindgen generated source not included as implicit input for libfoo; Implicits %#v", libfoo.Implicits.Strings())
 }
 if !android.SuffixInList(libfoo.Implicits.Strings(), "/out/any.rs") {
  t.Errorf("genrule generated source not included as implicit input for libfoo; Implicits %#v", libfoo.Implicits.Strings())
 }

 fizzBuzz := ctx.ModuleForTests(t, "fizz-buzz-dep", "android_arm64_armv8-a").Rule("rustc")
 if !android.SuffixInList(fizzBuzz.Implicits.Strings(), "/out/bindings.rs") {
  t.Errorf("rust_bindgen generated source not included as implicit input for fizz-buzz-dep; Implicits %#v", libfoo.Implicits.Strings())
 }
 if !android.SuffixInList(fizzBuzz.Implicits.Strings(), "/out/any.rs") {
  t.Errorf("genrule generated source not included as implicit input for fizz-buzz-dep; Implicits %#v", libfoo.Implicits.Strings())
 }

 libprocmacro := ctx.ModuleForTests(t, "libprocmacro", "linux_glibc_x86_64").Rule("rustc")
 if !android.SuffixInList(libprocmacro.Implicits.Strings(), "/out/bindings.rs") {
  t.Errorf("rust_bindgen generated source not included as implicit input for libprocmacro; Implicits %#v", libfoo.Implicits.Strings())
 }
 if !android.SuffixInList(libprocmacro.Implicits.Strings(), "/out/any.rs") {
  t.Errorf("genrule generated source not included as implicit input for libprocmacro; Implicits %#v", libfoo.Implicits.Strings())
 }

 // Check that our bindings are picked up as crate dependencies as well
 libfooMod := ctx.ModuleForTests(t, "libfoo", "android_arm64_armv8-a_dylib").Module().(*Module)
 if !android.InList("libbindings", libfooMod.Properties.AndroidMkRlibs) {
  t.Errorf("bindgen dependency not detected as a rlib dependency (dependency missing from AndroidMkRlibs)")
 }
 fizzBuzzMod := ctx.ModuleForTests(t, "fizz-buzz-dep", "android_arm64_armv8-a").Module().(*Module)
 if !android.InList("libbindings", fizzBuzzMod.Properties.AndroidMkRlibs) {
  t.Errorf("bindgen dependency not detected as a rlib dependency (dependency missing from AndroidMkRlibs)")
 }
 libprocmacroMod := ctx.ModuleForTests(t, "libprocmacro", "linux_glibc_x86_64").Module().(*Module)
 if !android.InList("libbindings.rlib-std", libprocmacroMod.Properties.AndroidMkRlibs) {
  t.Errorf("bindgen dependency not detected as a rlib dependency (dependency missing from AndroidMkRlibs)")
 }
}

func TestSourceProviderTargetMismatch(t *testing.T) {
 // This might error while building the dependency tree or when calling depsToPaths() depending on the lunched
 // target, which results in two different errors. So don't check the error, just confirm there is one.
 testRustError(t, ".*", `
  rust_proc_macro {
   name: "libprocmacro",
   srcs: [
    "foo.rs",
    ":libbindings",
   ],
   crate_name: "procmacro",
  }
  rust_bindgen {
   name: "libbindings",
   crate_name: "bindings",
   source_stem: "bindings",
   wrapper_src: "src/any.h",
  }
 `)
}

// Test to make sure proc_macros use host variants when building device modules.
func TestProcMacroDeviceDeps(t *testing.T) {
 ctx := testRust(t, `
  rust_library_host_rlib {
   name: "libbar",
   srcs: ["foo.rs"],
   crate_name: "bar",
  }
  rust_proc_macro {
   name: "libpm",
   rlibs: ["libbar"],
   srcs: ["foo.rs"],
   crate_name: "pm",
  }
  rust_binary {
   name: "fizz-buzz",
   proc_macros: ["libpm"],
   srcs: ["foo.rs"],
  }
 `)
 rustc := ctx.ModuleForTests(t, "libpm", "linux_glibc_x86_64").Rule("rustc")

 if !strings.Contains(rustc.Args["libFlags"], "libbar/linux_glibc_x86_64") {
  t.Errorf("Proc_macro is not using host variant of dependent modules.")
 }
}

// Test that no_stdlibs suppresses dependencies on rust standard libraries
func TestNoStdlibs(t *testing.T) {
 ctx := testRust(t, `
  rust_binary {
   name: "fizz-buzz",
   srcs: ["foo.rs"],
   no_stdlibs: true,
  }`)
 module := ctx.ModuleForTests(t, "fizz-buzz", "android_arm64_armv8-a").Module().(*Module)

 if android.InList("libstd", module.Properties.AndroidMkDylibs) {
  t.Errorf("no_stdlibs did not suppress dependency on libstd")
 }
}

// Test that libraries provide both 32-bit and 64-bit variants.
func TestMultilib(t *testing.T) {
 ctx := testRust(t, `
  rust_library_rlib {
   name: "libfoo",
   srcs: ["foo.rs"],
   crate_name: "foo",
   split_all_variants: true,
  }`)

 _ = ctx.ModuleForTests(t, "libfoo", "android_arm64_armv8-a_rlib_dylib-std")
 _ = ctx.ModuleForTests(t, "libfoo", "android_arm_armv7-a-neon_rlib_dylib-std")
}

// Test that aliases are respected.
func TestRustAliases(t *testing.T) {
 ctx := testRust(t, `
  rust_library {
   name: "libbar",
   crate_name: "bar",
   srcs: ["src/lib.rs"],
  }
  rust_library {
   name: "libbaz",
   crate_name: "baz",
   srcs: ["src/lib.rs"],
  }
  rust_binary {
   name: "foo",
   srcs: ["src/main.rs"],
   rustlibs: ["libbar", "libbaz"],
   aliases: ["bar:bar_renamed"],
  }`)

 fooRustc := ctx.ModuleForTests(t, "foo", "android_arm64_armv8-a").Rule("rustc")
 if !strings.Contains(fooRustc.Args["libFlags"], "--extern force:bar_renamed=out/soong/.intermediates/libbar/android_arm64_armv8-a_dylib/unstripped/libbar.dylib.so") {
  t.Errorf("--extern force:bar_renamed=out/soong/.intermediates/libbar/android_arm64_armv8-a_dylib/unstripped/libbar.dylib.so flag not being passed to rustc for rust_binary with aliases. libFlags: %#v", fooRustc.Args["libFlags"])
 }
 if !strings.Contains(fooRustc.Args["libFlags"], "--extern force:baz=out/soong/.intermediates/libbaz/android_arm64_armv8-a_dylib/unstripped/libbaz.dylib.so") {
  t.Errorf("--extern force:baz=out/soong/.intermediates/libbaz/android_arm64_armv8-a_dylib/unstripped/libbaz.dylib.so flag not being passed to rustc for rust_binary with aliases. libFlags: %#v", fooRustc.Args["libFlags"])
 }
}

func TestRustFFIRlibs(t *testing.T) {
 ctx := testRust(t, `
  rust_ffi_static {
   name: "libbar",
   crate_name: "bar",
   srcs: ["src/lib.rs"],
   export_include_dirs: ["bar_includes"]
  }

  rust_ffi_static {
   name: "libfoo",
   crate_name: "foo",
   srcs: ["src/lib.rs"],
   export_include_dirs: ["foo_includes"]
  }

  rust_ffi_static {
   name: "libfoo_from_rlib",
   crate_name: "foo_from_rlib",
   srcs: ["src/lib.rs"],
   export_include_dirs: ["foo_includes"]
  }

  rust_ffi_static {
   name: "libfoo_from_rlib_whole",
   crate_name: "foo_from_rlib_whole",
   srcs: ["src/lib.rs"],
   export_include_dirs: ["foo_includes"]
  }

  rust_ffi_static {
   name: "libbuzz",
   crate_name: "buzz",
   srcs: ["src/lib.rs"],
   export_include_dirs: ["buzz_includes"]
  }

  rust_ffi_static {
   name: "libbuzz_from_rlib",
   crate_name: "buzz_from_rlib",
   srcs: ["src/lib.rs"],
   export_include_dirs: ["buzz_includes"]
  }

  rust_ffi_static {
   name: "libbuzz_from_rlib_whole",
   crate_name: "buzz_from_rlib_whole",
   srcs: ["src/lib.rs"],
   export_include_dirs: ["buzz_includes"]
  }

  cc_library_shared {
   name: "libcc_shared",
   srcs:["foo.c"],
   static_libs: ["libbar"],
  }

  cc_library_static {
   name: "libcc_static",
   srcs:["foo.c"],
   static_libs: ["libbuzz"],
   whole_static_libs: ["libfoo"],
  }

  cc_library_static {
   name: "libcc_static_from_rlib",
   srcs:["foo.c"],
   static_libs: ["libbuzz_from_rlib"],
   whole_static_libs: ["libfoo_from_rlib"],
  }

  cc_library_static {
   name: "libcc_whole_static_from_rlib",
   srcs:["foo.c"],
   static_libs: ["libbuzz_from_rlib_whole"],
   whole_static_libs: ["libfoo_from_rlib_whole"],
  }

  cc_binary {
   name: "ccBin",
   srcs:["foo.c"],
   static_libs: ["libcc_static", "libbar"],
  }

  rust_library {
   name: "librs",
   srcs:["src/foo.rs"],
   crate_name: "rs",
   static_libs: ["libcc_static_from_rlib"],
   whole_static_libs: ["libcc_whole_static_from_rlib"],
  }

  rust_library {
   name: "librs2",
   srcs:["src/foo.rs"],
   crate_name: "rs",
   rustlibs: ["librs"],
   split_all_variants: true,
  }

  rust_binary {
   name: "rsBin",
   srcs:["src/foo.rs"],
   crate_name: "rsBin",
   rlibs: ["librs", "libbar"],
   static_libs: ["libcc_static"],
  }
 `)

 libbar := ctx.ModuleForTests(t, "libbar", "android_arm64_armv8-a_rlib_rlib-std").Rule("rustc")
 libcc_shared_rustc := ctx.ModuleForTests(t, "libcc_shared", "android_arm64_armv8-a_shared").Rule("rustc")
 libcc_shared_ld := ctx.ModuleForTests(t, "libcc_shared", "android_arm64_armv8-a_shared").Rule("ld")
 libcc_shared_cc := ctx.ModuleForTests(t, "libcc_shared", "android_arm64_armv8-a_shared").Rule("cc")
 ccbin_rustc := ctx.ModuleForTests(t, "ccBin", "android_arm64_armv8-a").Rule("rustc")
 ccbin_ld := ctx.ModuleForTests(t, "ccBin", "android_arm64_armv8-a").Rule("ld")
 ccbin_cc := ctx.ModuleForTests(t, "ccBin", "android_arm64_armv8-a").Rule("cc")
 rustbin_genlib := ctx.ModuleForTests(t, "rsBin", "android_arm64_armv8-a").Output("generated_rust_staticlib/librustlibs.a")
 rustbin := ctx.ModuleForTests(t, "rsBin", "android_arm64_armv8-a").Output("unstripped/rsBin")
 librs_rlib := ctx.ModuleForTests(t, "librs", "android_arm64_armv8-a_rlib_dylib-std").MaybeOutput("generated_rust_staticlib/librustlibs.a")
 librs2_rlib := ctx.ModuleForTests(t, "librs2", "android_arm64_armv8-a_rlib_dylib-std").MaybeOutput("generated_rust_staticlib/librustlibs.a")
 librs_genlib := ctx.ModuleForTests(t, "librs", "android_arm64_armv8-a_dylib").Output("generated_rust_staticlib/librustlibs.a")
 librs2_genlib := ctx.ModuleForTests(t, "librs2", "android_arm64_armv8-a_dylib").Output("generated_rust_staticlib/librustlibs.a")
 librs2_dylib := ctx.ModuleForTests(t, "librs2", "android_arm64_armv8-a_dylib").Output("unstripped/librs2.dylib.so")

 if !strings.Contains(libbar.Args["rustcFlags"], "crate-type=rlib") {
  t.Errorf("missing crate-type for static variant, expecting %#v, rustcFlags: %#v", "rlib", libbar.Args["rustcFlags"])
 }

 // Make sure there's a rustc command, and it's producing a staticlib
 if !strings.Contains(libcc_shared_rustc.Args["rustcFlags"], "crate-type=staticlib") {
  t.Errorf("missing crate-type for static variant, expecting %#v, rustcFlags: %#v",
   "staticlib", libcc_shared_rustc.Args["rustcFlags"])
 }

 // Make sure the static lib is included in the ld command
 if !strings.Contains(libcc_shared_ld.Args["libFlags"], "generated_rust_staticlib/librustlibs.a") {
  t.Errorf("missing generated static library in linker step libFlags %#v, libFlags: %#v",
   "libcc_shared.generated_rust_staticlib.a", libcc_shared_ld.Args["libFlags"])
 }

 // Make sure the static lib includes are in the cc command
 if !strings.Contains(libcc_shared_cc.Args["cFlags"], "-Ibar_includes") {
  t.Errorf("missing rlibs includes, expecting %#v, cFlags: %#v",
   "-Ibar_includes", libcc_shared_cc.Args["cFlags"])
 }

 // Make sure there's a rustc command, and it's producing a staticlib
 if !strings.Contains(ccbin_rustc.Args["rustcFlags"], "crate-type=staticlib") {
  t.Errorf("missing crate-type for static variant, expecting %#v, rustcFlags: %#v", "staticlib", ccbin_rustc.Args["rustcFlags"])
 }

 // Make sure the static lib is included in the cc command
 if !strings.Contains(ccbin_ld.Args["libFlags"], "generated_rust_staticlib/librustlibs.a") {
  t.Errorf("missing generated static library in linker step libFlags, expecting %#v, libFlags: %#v",
   "generated_rust_staticlib/librustlibs.a", ccbin_ld.Args["libFlags"])
 }

 // Make sure the static lib includes are in the ld command
 if !strings.Contains(ccbin_cc.Args["cFlags"], "-Ibar_includes") {
  t.Errorf("missing rlibs includes, expecting %#v, cFlags: %#v",
   "-Ibar_includes", ccbin_cc.Args)
 }

 // Make sure that direct dependencies and indirect whole static dependencies are
 // propagating correctly to the generated rlib.
 if !strings.Contains(ccbin_rustc.Args["libFlags"], "--extern foo=") {
  t.Errorf("Missing indirect whole_static_lib dependency libfoo when writing generated Rust staticlib: %#v", ccbin_rustc.Args["libFlags"])
 }
 if strings.Contains(ccbin_rustc.Args["libFlags"], "--extern buzz=") {
  t.Errorf("Indirect static_lib dependency libbuzz found when writing generated Rust staticlib: %#v", ccbin_rustc.Args["libFlags"])
 }
 if !strings.Contains(ccbin_rustc.Args["libFlags"], "--extern bar=") {
  t.Errorf("Missing direct dependency libbar when writing generated Rust staticlib: %#v", ccbin_rustc.Args["libFlags"])
 }

 // Make sure the static lib is included in the rustc command
 if !strings.Contains(rustbin.Args["linkFlags"], "generated_rust_staticlib/librustlibs.a") {
  t.Errorf("missing generated static library in linker step libFlags in Rust module, expecting %#v, libFlags: %#v",
   "generated_rust_staticlib/librustlibs.a", rustbin.Args["libFlags"])
 }
 if !strings.Contains(librs2_dylib.Args["linkFlags"], "generated_rust_staticlib/librustlibs.a") {
  t.Errorf("missing generated static library in linker step libFlags in Rust module, expecting %#v, libFlags: %#v",
   "generated_rust_staticlib/librustlibs.a", librs2_dylib.Args["libFlags"])
 }

 // Make sure that direct dependencies and indirect whole static dependencies are
 // propagating correctly for the rlib -> cc_library_static -> rust_* generated library example.
 if !strings.Contains(rustbin_genlib.Args["libFlags"], "--extern foo=") {
  t.Errorf("Missing indirect whole_static_lib dependency libfoo from cc static_lib when writing generated Rust staticlib: %#v", rustbin_genlib.Args["libFlags"])
 }
 if strings.Contains(rustbin_genlib.Args["libFlags"], "--extern buzz=") {
  t.Errorf("Indirect rlib dependency libbuzz from cc static_lib found when writing generated Rust staticlib: %#v", rustbin_genlib.Args["libFlags"])
 }
 if strings.Contains(rustbin_genlib.Args["libFlags"], "--extern bar=") {
  t.Errorf("Direct rlib dependency libbar getting included in the generated Rust staticlib: %#v", rustbin_genlib.Args["libFlags"])
 }
 if !strings.Contains(rustbin_genlib.Args["libFlags"], "--extern foo_from_rlib=") {
  t.Errorf("Missing indirect whole_static_lib dependency libfoo_from_rlib from cc static_lib when writing generated Rust staticlib: %#v", rustbin_genlib.Args["libFlags"])
 }
 if strings.Contains(rustbin_genlib.Args["libFlags"], "--extern buzz_from_rlib=") {
  // While static-libs propagate for rust modules, this is not the
  // expected behavior for cc modules. Thus, libbuzz_from_rlib would
  // be expected to have to be re-declared as a direct rlib dependency.
  t.Errorf("Indirect rlib dependency libbuzz_from_rlib from cc static_lib found when writing generated Rust staticlib: %#v", rustbin_genlib.Args["libFlags"])
 }

 // Test indirect includes propagation
 if !strings.Contains(ccbin_cc.Args["cFlags"], "-Ifoo_includes") {
  t.Errorf("missing rlibs includes, expecting %#v, cFlags: %#v",
   "-Ifoo_includes", ccbin_cc.Args)
 }

 // Make sure we're not generating superfluous mto staticlibs.
 if librs_rlib.Rule != nil {
  t.Error("rlibs should not be generating mto staticlibs", "rlib", libbar.Args["rustcFlags"])
 }
 if librs2_rlib.Rule != nil {
  t.Error("rlibs should not be generating mto staticlibs", "rlib", libbar.Args["rustcFlags"])
 }

 // Make sure that direct whole static dependencies are propagating correctly downstream
 // foo_from_rlib_whole --(ws)--> libcc_whole_static_from_rlib --(ws)--> librs
 if !strings.Contains(librs_genlib.Args["libFlags"], "--extern foo_from_rlib_whole=") {
  t.Errorf("Missing direct whole_static_lib dependency libfoo_from_rlib_whole from rust dylib when writing generated Rust staticlib: %#v", librs_genlib.Args["libFlags"])
 }

 // Make sure that indirect whole static dependencies are propagating correctly downstream
 // foo_from_rlib_whole --(ws)--> libcc_whole_static_from_rlib --(ws)--> librs --> rust_*
 if !strings.Contains(librs2_genlib.Args["libFlags"], "--extern foo_from_rlib_whole=") {
  t.Errorf("Missing indirect whole_static_lib dependency libfoo_from_rlib_whole from rust dylib when writing generated Rust staticlib: %#v", librs2_genlib.Args["libFlags"])
 }
 if !strings.Contains(rustbin_genlib.Args["libFlags"], "--extern foo_from_rlib_whole=") {
  t.Errorf("Missing indirect whole_static_lib dependency libfoo_from_rlib_whole from rust dylib in rust binary when writing generated Rust staticlib: %#v", rustbin_genlib.Args["libFlags"])
 }

 // Make sure that normal static dependencies are not propagating through dylib dependencies
 // buzz_from_rlib_whole --(s)--> libcc_whole_static_from_rlib --(ws)--> librs --> rust_*
 if strings.Contains(librs2_genlib.Args["libFlags"], "--extern buzz_from_rlib_whole=") {
  t.Errorf("dependency from indirect cc staticlib from direct dylib dep found in rust dylib when writing generated Rust staticlib: %#v", librs2_genlib.Args["libFlags"])
 }

}

func TestStdLinkMismatch(t *testing.T) {
 // Test that we catch cases where the std linkage mismatches. This leads to
 // a confusing rustc error where a crate is declared missing despite being
 // passed in as a rustlib dependency / via the --extern flag. Thus, we want
 // to make sure we detect it in Soong.

 // libfoo depends on libbar as an rlib, but does not link libstd as an rlib.
 // libbar only links libstd as an rlib (prefer_rlib).
 testRustError(t, "wrong StdLinkage", `
  rust_library {
   name: "libfoo",
   crate_name: "foo",
   srcs: [
    "foo.rs",
   ],
   rlibs: ["libbar"],
   split_all_variants: true,
  }
  rust_library {
   name: "libbar",
   crate_name: "bar",
   srcs: [
    "bar.rs",
   ],
   prefer_rlib: true,
  }
 `)
}

func TestRustLinkPropagation(t *testing.T) {
 // Test static and whole static propagation behavior
 //
 //  Whole static libs propagate through rlibs and through dylibs to
 //  dependencies further down. rustc does not re-export whole-archived
 //  static libs for dylibs, so this simulates re-exporting those symbols.
 //
 //  Static libs only propagate through rlibs to some final dylib. We propagate
 //  normal static libs because we allow rustlib dependencies to represent
 //  either rlibs or dylibs. Not propagating static libs through rlibs would
 //  mean we'd need to always redeclare static libs throughout a dependency tree
 //  We don't propagate past dylibs because they represent a final link.

 ctx := testRust(t, `
 rust_library_rlib {
  name: "librlib1",
  crate_name: "rlib1",
  srcs: ["src/lib.rs"],
  static_libs: ["libcc_static_rlib1"],
  whole_static_libs: ["libcc_whole_static_rlib1"],
 }

 rust_library_dylib {
  name: "libdylib1",
  crate_name: "dylib1",
  static_libs: ["libcc_static_dylib1"],
  srcs: ["src/lib.rs"],
  whole_static_libs: ["libcc_whole_static_dylib1"],
 }

 rust_library_rlib {
  name: "librlib2",
  crate_name: "rlib2",
  srcs: ["src/lib.rs"],
  rlibs: ["librlib1"],
  static_libs: ["libcc_static_rlib2"],
  whole_static_libs: ["libcc_whole_static_rlib2"],
 }

 rust_library_dylib {
  name: "libdylib2",
  crate_name: "dylib2",
  srcs: ["src/lib.rs"],
  rlibs: ["librlib1"],
  rustlibs: ["libdylib1"],
  static_libs: ["libcc_static_dylib2"],
  whole_static_libs: ["libcc_whole_static_dylib2"],
 }

 cc_library_static {
  name: "libcc_static_rlib1",
  srcs:["foo.c"],
 }

 cc_library_static {
  name: "libcc_static_rlib2",
  srcs:["foo.c"],
 }

 cc_library_static {
  name: "libcc_static_dylib1",
  srcs:["foo.c"],
 }

 cc_library_static {
  name: "libcc_static_dylib2",
  srcs:["foo.c"],
 }

 cc_library_static {
  name: "libcc_whole_static_rlib1",
  srcs:["foo.c"],
 }

 cc_library_static {
  name: "libcc_whole_static_rlib2",
  srcs:["foo.c"],
 }

 cc_library_static {
  name: "libcc_whole_static_dylib1",
  srcs:["foo.c"],
 }

 cc_library_static {
  name: "libcc_whole_static_dylib2",
  srcs:["foo.c"],
 }

 rust_library_rlib {
  name: "librlib3",
  crate_name: "rlib3",
  srcs: ["src/lib.rs"],
  rlibs: ["librlib2"],
  split_all_variants: true,
 }

 rust_library_dylib {
  name: "libdylib3",
  crate_name: "dylib3",
  srcs: ["src/lib.rs"],
  rlibs: ["librlib2"],
  rustlibs: ["libdylib2"],
 }
 `)

 librlib3 := ctx.ModuleForTests(t, "librlib3", "android_arm64_armv8-a_rlib_dylib-std").Rule("rustc")
 libdylib3 := ctx.ModuleForTests(t, "libdylib3", "android_arm64_armv8-a_dylib").Rule("rustc")

 // Test static lib propagation from:
 // rlib -> rlib
 if !strings.Contains(librlib3.Args["linkFlags"], "libcc_static_rlib2.a") {
  t.Errorf("direct dependency static lib not propagating from rlib to rlib; linkFlags %#v",
   librlib3.Args["linkFlags"])
 }
 // rlib -> rlib -> rlib
 if !strings.Contains(librlib3.Args["linkFlags"], "libcc_static_rlib1.a") {
  t.Errorf("indirect dependency static lib not propagating from rlib to rlib: linkFlags %#v",
   librlib3.Args["linkFlags"])
 }
 // rlib -> rlib -> dylib
 if !strings.Contains(libdylib3.Args["linkFlags"], "libcc_static_rlib1.a") {
  t.Errorf("indirect dependency static lib not propagating from rlib to dylib: linkFlags %#v",
   libdylib3.Args["linkFlags"])
 }
 // rlib -> dylib
 if !strings.Contains(libdylib3.Args["linkFlags"], "libcc_static_rlib2.a") {
  t.Errorf("direct dependency static lib not propagating from rlib to dylib: linkFlags: %#v",
   libdylib3.Args["linkFlags"])
 }
 // dylib -> dylib (negative case, should not propagate)
 if strings.Contains(libdylib3.Args["linkFlags"], "libcc_static_dylib2.a") {
  t.Errorf("direct dependency static lib propagating from dylib to dylib: linkFlags: %#v",
   libdylib3.Args["linkFlags"])
 }
 // dylib -> dylib -> dylib (negative case, should not propagate)
 if strings.Contains(libdylib3.Args["linkFlags"], "libcc_static_dylib1.a") {
  t.Errorf("indirect dependency static lib propagating from dylib to dylib: linkFlags: %#v",
   libdylib3.Args["linkFlags"])
 }

 // Test whole static lib propagation from:
 // rlib -> rlib
 if !strings.Contains(librlib3.Args["linkFlags"], "libcc_whole_static_rlib2.a") {
  t.Errorf("direct dependency whole static lib not propagating from rlib to rlib: linkFlags %#v",
   librlib3.Args["linkFlags"])
 }
 // rlib -> rlib -> rlib
 if !strings.Contains(librlib3.Args["linkFlags"], "libcc_whole_static_rlib1.a") {
  t.Errorf("indirect dependency whole static lib not propagating from rlib to rlib: linkFlags %#v",
   librlib3.Args["linkFlags"])
 }
 // rlib -> dylib
 if !strings.Contains(libdylib3.Args["linkFlags"], "libcc_whole_static_rlib2.a") {
  t.Errorf("direct dependency whole static lib not propagating from rlib to dylib: linkFlags %#v",
   libdylib3.Args["linkFlags"])
 }
 // rlib -> rlib -> dylib
 if !strings.Contains(libdylib3.Args["linkFlags"], "libcc_whole_static_rlib1.a") {
  t.Errorf("indirect dependency whole static lib not propagating from rlib to dylib: linkFlags %#v",
   libdylib3.Args["linkFlags"])
 }
 // dylib -> dylib
 if !strings.Contains(libdylib3.Args["linkFlags"], "libcc_whole_static_dylib2.a") {
  t.Errorf("direct dependency whole static lib not propagating from dylib to dylib: linkFlags %#v",
   libdylib3.Args["linkFlags"])
 }
 // dylib -> dylib -> dylib
 if !strings.Contains(libdylib3.Args["linkFlags"], "libcc_whole_static_dylib1.a") {
  t.Errorf("indirect dependency whole static lib not propagating from dylib to dylib: linkFlags %#v",
   libdylib3.Args["linkFlags"])
 }
}

func TestNoStdRlibsWithStdVariant(t *testing.T) {
 ctx := testRust(t, `
  rust_library {
   name: "libfoo",
   crate_name: "foo",
   srcs: ["foo.rs"],
   no_std: {
    enabled: true,
   },
  }
  rust_binary {
   name: "foobar",
   srcs: ["foo.rs"],
   no_std_rlibs: ["libfoo"],
  }
 `)
 module := ctx.ModuleForTests(t, "foobar", "android_arm64_armv8-a").Module().(*Module)
 if !android.InList("libfoo.rlib-core", module.Properties.AndroidMkRlibs) {
  t.Errorf("no_std_rlibs dependency not detected: %v", module.Properties.AndroidMkRlibs)
 }
}

func TestNoStdFallback(t *testing.T) {
 ctx := testRust(t, `
  rust_library {
   name: "libfoo",
   crate_name: "foo",
   srcs: ["foo.rs"],
  }
  rust_binary {
   name: "foobar",
   srcs: ["foo.rs"],
   rlibs: ["libfoo_nostd"],
  }
 `)
 module := ctx.ModuleForTests(t, "foobar", "android_arm64_armv8-a").Module().(*Module)
 if !android.InList("libfoo", module.Properties.AndroidMkRlibs) {
  t.Errorf("nostd fallback dependency not detected (dependency missing from AndroidMkRlibs)")
 }
}

// Checks that we can have libfoo.dylib-std -> libcbar -> libbang -> libfoo.core-std without a loop
func TestVariantCycle(t *testing.T) {
 // We don't need to do anything with this, just loading it will crash if broken
 testRust(t, `
  rust_library {
   name: "libfoo",
   crate_name: "foo",
   srcs: ["foo.rs"],
   static_libs: ["libcbar"],
   no_std: {
    enabled: true,
    static_libs: [],
   }
  }
  rust_library_rlib {
   name: "libbang",
   crate_name: "bang",
   srcs: ["foo.rs"],
   rustlibs: ["libfoo"],
   no_stdlibs: true,
  }
  cc_library {
   name: "libcbar",
   static_libs: ["libbang"],
   srcs: ["foo.c"],
  }
 `)
}

[Dauer der Verarbeitung: 0.23 Sekunden, vorverarbeitet 2026-06-28]