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 <fahadzub@amazon.com>
This commit is contained in:
Fahad Zubair 2024-10-01 01:02:27 -04:00 committed by GitHub
parent abd28bc71e
commit 684c15f39c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 131 additions and 9 deletions

View File

@ -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<String>()
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<PluginContext, Path> {
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)

View File

@ -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()
}
}