From 684c15f39c9f1e3f3e77d07e63cf6a1598621ed8 Mon Sep 17 00:00:00 2001 From: Fahad Zubair Date: Tue, 1 Oct 2024 01:02:27 -0400 Subject: [PATCH] Set MSRV in rust-toolchain.toml using the gradle.property (#3841) The `rust-toolchain.toml` file for tests now uses the `rust.msrv` value from the `gradle.properties` file. This PR also fixes an issue where the `rust-toolchain.toml` file was not created in the overridden test directory when `overrideTestDir` was set. This caused the installed compiler version to be used, resulting in errors with the latest compiler and preventing the use of `overrideTestDir`. Closes: #2048 --------- Co-authored-by: Fahad Zubair --- .../smithy/rust/codegen/core/testutil/Rust.kt | 52 +++++++++-- .../core/util/RustToolChainTomlTest.kt | 88 +++++++++++++++++++ 2 files changed, 131 insertions(+), 9 deletions(-) create mode 100644 codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/util/RustToolChainTomlTest.kt diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/Rust.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/Rust.kt index 5941d8cf87..9565c9ab85 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/Rust.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/Rust.kt @@ -40,9 +40,11 @@ import software.amazon.smithy.rust.codegen.core.util.letIf import software.amazon.smithy.rust.codegen.core.util.orNullIfEmpty import software.amazon.smithy.rust.codegen.core.util.runCommand import java.io.File +import java.io.FileInputStream import java.nio.file.Files import java.nio.file.Files.createTempDirectory import java.nio.file.Path +import java.util.Properties import kotlin.io.path.absolutePathString import kotlin.io.path.writeText @@ -54,6 +56,8 @@ val TestModuleDocProvider = } } +val projectRootDir by lazy { File("git rev-parse --show-toplevel".runCommand().replace("\n", "")) } + /** * Waiting for Kotlin to stabilize their temp directory functionality */ @@ -65,6 +69,38 @@ private fun tempDir(directory: File? = null): File { } } +/** + * This function returns the minimum supported Rust version, as specified in the `gradle.properties` file + * located at the root of the project. + */ +fun msrv(): String { + val properties = Properties() + val propertiesFilePath = projectRootDir.resolve("gradle.properties") + + FileInputStream(propertiesFilePath).use { inputStream -> + properties.load(inputStream) + } + + return properties.getProperty("rust.msrv") +} + +/** + * Generates the `rust-toolchain.toml` file in the specified directory. + * + * The compiler version is set in `gradle.properties` under the `rust.msrv` property. + * The Gradle task `GenerateMsrvTask` generates the Kotlin class + * `software.amazon.smithy.rust.codegen.core.Msrv` and writes the value of `rust.msrv` into it. + */ +private fun File.generateRustToolchainToml() { + resolve("rust-toolchain.toml").writeText( + // Help rust select the right version when we run cargo test. + """ + [toolchain] + channel = "${msrv()}" + """.trimIndent(), + ) +} + /** * Creates a Cargo workspace shared among all tests * @@ -87,8 +123,7 @@ object TestWorkspace { private val subprojects = mutableListOf() private val cargoLock: File by lazy { - val projectDir = "git rev-parse --show-toplevel".runCommand().replace("\n", "") - File(projectDir).resolve("aws/sdk/Cargo.lock") + projectRootDir.resolve("aws/sdk/Cargo.lock") } init { @@ -121,12 +156,7 @@ object TestWorkspace { version = "0.0.1" """.trimIndent(), ) - newProject.resolve("rust-toolchain.toml").writeText( - // help rust select the right version when we run cargo test - // TODO(https://github.com/smithy-lang/smithy-rs/issues/2048): load this from the msrv property using a - // method as we do for runtime crate versions - "[toolchain]\nchannel = \"1.78.0\"\n", - ) + newProject.generateRustToolchainToml() // ensure there at least an empty lib.rs file to avoid broken crates newProject.resolve("src").mkdirs() newProject.resolve("src/lib.rs").writeText("") @@ -181,7 +211,11 @@ fun generatePluginContext( runtimeConfig: RuntimeConfig? = null, overrideTestDir: File? = null, ): Pair { - val testDir = overrideTestDir ?: TestWorkspace.subproject() + val testDir = + overrideTestDir?.apply { + mkdirs() + generateRustToolchainToml() + } ?: TestWorkspace.subproject() val moduleName = "test_${testDir.nameWithoutExtension}" val testPath = testDir.toPath() val manifest = FileManifest.create(testPath) diff --git a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/util/RustToolChainTomlTest.kt b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/util/RustToolChainTomlTest.kt new file mode 100644 index 0000000000..832762e664 --- /dev/null +++ b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/util/RustToolChainTomlTest.kt @@ -0,0 +1,88 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.core.util + +import io.kotest.matchers.booleans.shouldBeTrue +import io.kotest.matchers.paths.shouldExist +import io.kotest.matchers.shouldNotBe +import org.junit.jupiter.api.Test +import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel +import software.amazon.smithy.rust.codegen.core.testutil.generatePluginContext +import software.amazon.smithy.rust.codegen.core.testutil.projectRootDir +import java.nio.file.Files.createTempDirectory +import java.util.regex.Pattern + +internal class RustToolChainTomlTest { + val model = + """ + namespace test + + service TestService { + version: "123", + operations: [TestOperation] + } + + operation TestOperation { + input:= {} + output:= {} + } + """.asSmithyModel(smithyVersion = "2") + + @Test + fun `override test directory in integration test has a rust-toolchain toml file`() { + val dir = createTempDirectory("smithy-test").toFile() + val (_, path) = generatePluginContext(model, overrideTestDir = dir) + path.shouldExist() + val rustToolchainTomlPath = path.resolve("rust-toolchain.toml") + rustToolchainTomlPath.shouldExist() + } + + @Test + fun `rust-toolchain toml file has correct value from gradle properties for rust-msrv`() { + val (_, path) = generatePluginContext(model) + val rustToolchainTomlPath = path.resolve("rust-toolchain.toml") + rustToolchainTomlPath.shouldExist() + + // Read the MSRV written in `gradle.properties` file. + val msrvPattern = Pattern.compile("rust\\.msrv=(.+)") + val gradlePropertiesPath = projectRootDir.resolve("gradle.properties") + val msrv = + gradlePropertiesPath.useLines { lines -> + lines.firstNotNullOfOrNull { line -> + msrvPattern.matcher(line).let { matcher -> + if (matcher.find()) matcher.group(1) else null + } + } + } + msrv shouldNotBe null + + // Read `channel = (\d+)` from `rust-toolchain.toml` file, and + // ensure it matches the one in `gradle.properties`. + val toolchainPattern = Pattern.compile("\\[toolchain]") + val channelPattern = Pattern.compile("channel\\s*=\\s*\"(.+)\"") + + val channelMatches = + rustToolchainTomlPath.toFile().useLines { lines -> + // Skip lines until the [toolchain] table is found, then take all lines until the next table. + val toolchainSection = + lines + .dropWhile { !toolchainPattern.matcher(it).find() } + .drop(1) + .takeWhile { !it.trim().startsWith("[") } + + // There should be a [toolchain] table, and it must have a key called 'channel' whose value must + // match the `rust.msrv` specified in gradle.properties. + toolchainSection != null && + toolchainSection.any { line -> + channelPattern.matcher(line).let { matcher -> + matcher.find() && matcher.group(1) == msrv + } + } + } + + channelMatches.shouldBeTrue() + } +}