Skip to content

Commit

Permalink
Merge #8494
Browse files Browse the repository at this point in the history
8494: CARGO: Retrieve config information r=vlad20012 a=avrong

Adds the ability to get config properties using `cargo config get` subcommand tracked in rust-lang/cargo#2362. It can be improved in the future as rust-lang/cargo#9301 features will be implemented.

Currently, it retrieves the config values of a specified path and parses them into a tree. Methods like `getBuildTarget` and `getEnvParams` return specific structured data

Co-authored-by: vlad20012 <beskvlad@gmail.com>
  • Loading branch information
bors[bot] and vlad20012 committed Apr 29, 2022
2 parents 2dcd91d + afa22ff commit 6ab14f3
Show file tree
Hide file tree
Showing 10 changed files with 213 additions and 20 deletions.
5 changes: 5 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,11 @@ project(":") {
exclude(module = "kotlin-stdlib")
exclude(module = "kotlin-stdlib-common")
}
implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-toml:2.13.2"){
exclude(module = "jackson-core")
exclude(module = "jackson-databind")
exclude(module = "jackson-annotations")
}
api("com.vdurmont:semver4j:3.1.0")
testImplementation("com.squareup.okhttp3:mockwebserver:4.9.3")
}
Expand Down
30 changes: 30 additions & 0 deletions src/main/kotlin/org/rust/cargo/CargoConfig.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* Use of this source code is governed by the MIT license that can be
* found in the LICENSE file.
*/

package org.rust.cargo

import com.fasterxml.jackson.annotation.JsonProperty

/**
* https://doc.rust-lang.org/cargo/reference/config.html
*/
data class CargoConfig(
val buildTarget: String?,
val env: Map<String, EnvValue>,

) {
data class EnvValue(
@JsonProperty("value")
val value: String,
@JsonProperty("forced")
val isForced: Boolean = false,
@JsonProperty("relative")
val isRelative: Boolean = false
)

companion object {
val DEFAULT = CargoConfig(null, emptyMap())
}
}
38 changes: 30 additions & 8 deletions src/main/kotlin/org/rust/cargo/project/model/impl/CargoSyncTask.kt
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import com.intellij.openapi.util.Key
import com.intellij.util.io.exists
import com.intellij.util.text.SemVer
import org.rust.RsTask
import org.rust.cargo.CargoConfig
import org.rust.cargo.project.model.CargoProject
import org.rust.cargo.project.model.ProcessProgressListener
import org.rust.cargo.project.model.RustcInfo
Expand Down Expand Up @@ -324,6 +325,25 @@ private fun fetchCargoWorkspace(context: CargoSyncTask.SyncContext, rustcInfo: R
}
val projectDirectory = childContext.oldCargoProject.workingDirectory
val cargo = toolchain.cargoOrWrapper(projectDirectory)
val rustcVersion = rustcInfo?.version?.semver

val cargoConfig = if (rustcVersion == null || rustcVersion >= RUST_1_53) {
val cargoConfigResult = UnitTestRustcCacheService.cached(
rustcInfo?.version,
cacheIf = { !projectDirectory.resolve(".cargo").exists() }
) { cargo.getConfig(childContext.project, projectDirectory) }

when (cargoConfigResult) {
is RsResult.Ok -> cargoConfigResult.ok
is RsResult.Err -> {
val message = "Fetching Cargo Config failed.\n\n" + cargoConfigResult.err.message.orEmpty()
childContext.warning("Fetching Cargo Config", message)
CargoConfig.DEFAULT
}
}
} else {
CargoConfig.DEFAULT
}

CargoEventService.getInstance(childContext.project).onMetadataCall(projectDirectory)
val (projectDescriptionData, status) = cargo.fullProjectDescription(
Expand Down Expand Up @@ -354,23 +374,24 @@ private fun fetchCargoWorkspace(context: CargoSyncTask.SyncContext, rustcInfo: R

val manifestPath = projectDirectory.resolve("Cargo.toml")

val result = UnitTestRustcCacheService.cached(rustcInfo?.version, cacheIf = { !projectDirectory.resolve(".cargo").exists() }) {
cargo.getCfgOption(childContext.project, projectDirectory)
}
val cfgOptions = when (result) {
is RsResult.Ok -> result.ok
val cfgOptionsResult = UnitTestRustcCacheService.cached(
rustcInfo?.version,
cacheIf = { !projectDirectory.resolve(".cargo").exists() }
) { cargo.getCfgOption(childContext.project, projectDirectory) }

val cfgOptions = when (cfgOptionsResult) {
is RsResult.Ok -> cfgOptionsResult.ok
is RsResult.Err -> {
val rustcVersion = rustcInfo?.version?.semver
if (rustcVersion == null || rustcVersion > RUST_1_51) {
val message = "Fetching target specific `cfg` options failed. Fallback to host options.\n\n" +
result.err.message.orEmpty()
cfgOptionsResult.err.message.orEmpty()
childContext.warning("Fetching target specific `cfg` options", message)
}
toolchain.rustc().getCfgOptions(projectDirectory)
}
}

val ws = CargoWorkspace.deserialize(manifestPath, projectDescriptionData, cfgOptions)
val ws = CargoWorkspace.deserialize(manifestPath, projectDescriptionData, cfgOptions, cargoConfig)
TaskResult.Ok(ws)
}
}
Expand Down Expand Up @@ -513,3 +534,4 @@ private fun CargoSyncTask.SyncContext.warning(
}

private val RUST_1_51: SemVer = "1.51.0".parseSemVer()
private val RUST_1_53: SemVer = "1.53.0".parseSemVer()
34 changes: 30 additions & 4 deletions src/main/kotlin/org/rust/cargo/project/workspace/CargoWorkspace.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import com.intellij.openapi.vfs.VirtualFile
import com.intellij.openapi.vfs.VirtualFileManager
import com.intellij.util.ThreeState
import org.jetbrains.annotations.TestOnly
import org.rust.cargo.CargoConfig
import org.rust.cargo.CfgOptions
import org.rust.cargo.project.model.CargoProjectsService
import org.rust.cargo.project.model.RustcInfo
Expand Down Expand Up @@ -38,6 +39,7 @@ interface CargoWorkspace {
val workspaceRoot: VirtualFile?

val cfgOptions: CfgOptions
val cargoConfig: CargoConfig

/**
* Flatten list of packages including workspace members, dependencies, transitive dependencies
Expand Down Expand Up @@ -222,8 +224,13 @@ interface CargoWorkspace {
}

companion object {
fun deserialize(manifestPath: Path, data: CargoWorkspaceData, cfgOptions: CfgOptions): CargoWorkspace =
WorkspaceImpl.deserialize(manifestPath, data, cfgOptions)
fun deserialize(
manifestPath: Path,
data: CargoWorkspaceData,
cfgOptions: CfgOptions = CfgOptions.DEFAULT,
cargoConfig: CargoConfig = CargoConfig.DEFAULT,
): CargoWorkspace =
WorkspaceImpl.deserialize(manifestPath, data, cfgOptions, cargoConfig)
}
}

Expand All @@ -233,6 +240,7 @@ private class WorkspaceImpl(
val workspaceRootUrl: String?,
packagesData: Collection<CargoWorkspaceData.Package>,
override val cfgOptions: CfgOptions,
override val cargoConfig: CargoConfig,
val featuresState: Map<PackageRoot, Map<FeatureName, FeatureState>>
) : CargoWorkspace {

Expand Down Expand Up @@ -346,6 +354,7 @@ private class WorkspaceImpl(
workspaceRootUrl,
newPackagesData,
cfgOptions,
cargoConfig,
featuresState
)

Expand Down Expand Up @@ -394,6 +403,7 @@ private class WorkspaceImpl(
workspaceRootUrl,
packages.map { it.asPackageData() },
cfgOptions,
cargoConfig,
featuresState
).withDependenciesOf(this)
}
Expand Down Expand Up @@ -485,6 +495,7 @@ private class WorkspaceImpl(
workspaceRootUrl,
newPackagesData,
cfgOptions,
cargoConfig,
featuresState
)

Expand Down Expand Up @@ -523,6 +534,7 @@ private class WorkspaceImpl(
pkg.asPackageData(packageEdition)
},
cfgOptions,
cargoConfig,
featuresState
).withDependenciesOf(this)

Expand All @@ -532,6 +544,7 @@ private class WorkspaceImpl(
workspaceRootUrl,
packages.map { it.asPackageData() },
cfgOptions,
cargoConfig,
featuresState
).withDependenciesOf(this)

Expand All @@ -545,6 +558,7 @@ private class WorkspaceImpl(
workspaceRootUrl,
packages.map { it.asPackageData().copy(features = packageToFeatures[it].orEmpty(), enabledFeatures = packageToFeatures[it].orEmpty().keys) },
cfgOptions,
cargoConfig,
featuresState
).withDependenciesOf(this).withDisabledFeatures(UserDisabledFeatures.EMPTY)
}
Expand All @@ -555,14 +569,26 @@ private class WorkspaceImpl(
}

companion object {
fun deserialize(manifestPath: Path, data: CargoWorkspaceData, cfgOptions: CfgOptions): WorkspaceImpl {
fun deserialize(
manifestPath: Path,
data: CargoWorkspaceData,
cfgOptions: CfgOptions,
cargoConfig: CargoConfig,
): WorkspaceImpl {
// Packages form mostly a DAG. "Why mostly?", you say.
// Well, a dev-dependency `X` of package `P` can depend on the `P` itself.
// This is ok, because cargo can compile `P` (without `X`, because dev-deps
// are used only for tests), then `X`, and then `P`s tests. So we need to
// handle cycles here.

val result = WorkspaceImpl(manifestPath, data.workspaceRootUrl, data.packages, cfgOptions, emptyMap())
val result = WorkspaceImpl(
manifestPath,
data.workspaceRootUrl,
data.packages,
cfgOptions,
cargoConfig,
emptyMap()
)

// Fill package dependencies
run {
Expand Down
47 changes: 47 additions & 0 deletions src/main/kotlin/org/rust/cargo/toolchain/tools/Cargo.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ package org.rust.cargo.toolchain.tools

import com.fasterxml.jackson.core.JacksonException
import com.fasterxml.jackson.databind.DeserializationFeature
import com.fasterxml.jackson.databind.JsonNode
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.dataformat.toml.TomlMapper
import com.fasterxml.jackson.module.kotlin.registerKotlinModule
import com.intellij.execution.configuration.EnvironmentVariablesData
import com.intellij.execution.configurations.GeneralCommandLine
Expand All @@ -25,6 +27,7 @@ import com.intellij.util.execution.ParametersListUtil
import com.intellij.util.net.HttpConfigurable
import com.intellij.util.text.SemVer
import org.jetbrains.annotations.TestOnly
import org.rust.cargo.CargoConfig
import org.rust.cargo.CargoConstants
import org.rust.cargo.CfgOptions
import org.rust.cargo.project.model.CargoProject
Expand Down Expand Up @@ -211,6 +214,49 @@ class Cargo(
).execute(owner).map { output -> CfgOptions.parse(output.stdoutLines) }
}

/**
* Execute `cargo config get <path>` to and parse output as Jackson Tree ([JsonNode]).
* Use [JsonNode.at] to get properties by path (in `/foo/bar` format)
*/
fun getConfig(
owner: Project,
projectDirectory: Path?
): RsResult<CargoConfig, RsProcessExecutionOrDeserializationException> {
val parameters = mutableListOf("-Z", "unstable-options", "config", "get")

val output = createBaseCommandLine(
parameters,
workingDirectory = projectDirectory,
environment = mapOf(RUSTC_BOOTSTRAP to "1")
).execute(owner).unwrapOrElse { return Err(it) }.stdout

val tree = try {
TOML_MAPPER.readTree(output)
} catch (e: JacksonException) {
return Err(RsDeserializationException(e))
}

val buildTarget = tree.at("/build/target").asText()
val env = tree.at("/env").fields().asSequence().toList().mapNotNull { field ->
// Value can be either string or object with additional `forced` and `relative` params.
// https://doc.rust-lang.org/cargo/reference/config.html#env
if (field.value.isTextual) {
field.key to CargoConfig.EnvValue(field.value.asText())
} else if (field.value.isObject) {
val valueParams = try {
TOML_MAPPER.treeToValue(field.value, CargoConfig.EnvValue::class.java)
} catch (e: JacksonException) {
return Err(RsDeserializationException(e))
}
field.key to CargoConfig.EnvValue(valueParams.value, valueParams.isForced, valueParams.isRelative)
} else {
null
}
}.toMap()

return Ok(CargoConfig(buildTarget, env))
}

private fun fetchBuildScriptsInfo(
owner: Project,
projectDirectory: Path,
Expand Down Expand Up @@ -462,6 +508,7 @@ class Cargo(
private val JSON_MAPPER: ObjectMapper = ObjectMapper()
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
.registerKotlinModule()
private val TOML_MAPPER = TomlMapper()

@JvmStatic
val TEST_NOCAPTURE_ENABLED_KEY: RegistryValue = Registry.get("org.rust.cargo.test.nocapture")
Expand Down
11 changes: 7 additions & 4 deletions src/test/kotlin/org/rust/RustProjectDescriptors.kt
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,10 @@ open class RustProjectDescriptorBase : LightProjectDescriptor() {

open fun testCargoProject(module: Module, contentRoot: String): CargoWorkspace {
val packages = listOf(testCargoPackage(contentRoot))
return CargoWorkspace.deserialize(Paths.get("${Urls.newFromIdea(contentRoot).path}/workspace/Cargo.toml"),
CargoWorkspaceData(packages, emptyMap(), emptyMap(), contentRoot), CfgOptions.DEFAULT)
return CargoWorkspace.deserialize(
Paths.get("${Urls.newFromIdea(contentRoot).path}/workspace/Cargo.toml"),
CargoWorkspaceData(packages, emptyMap(), emptyMap(), contentRoot),
)
}

protected open fun externalPackage(
Expand Down Expand Up @@ -406,7 +408,7 @@ object WithDependencyRustProjectDescriptor : RustProjectDescriptorBase() {
cyclicDepLibDevDep.id to setOf(
dep(depLibWithCyclicDep.id),
)
), emptyMap(), contentRoot), CfgOptions.DEFAULT)
), emptyMap(), contentRoot))
}

private fun dep(id: PackageId, name: String? = null, depKind: DepKind = DepKind.Normal): Dependency =
Expand All @@ -423,7 +425,8 @@ private class WithStdlibLikeDependencyRustProjectDescriptor : RustProjectDescrip
)
return CargoWorkspace.deserialize(
Paths.get("${Urls.newFromIdea(contentRoot).path}/workspace/Cargo.toml"),
CargoWorkspaceData(packages, emptyMap(), emptyMap(), contentRoot), CfgOptions.DEFAULT)
CargoWorkspaceData(packages, emptyMap(), emptyMap(), contentRoot),
)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,6 @@ abstract class RunConfigurationProducerTestBase : RsTestBase() {
rawDependencies = emptyMap(),
workspaceRootUrl = contentRootUrl
),
CfgOptions.DEFAULT
)

project.testCargoProjects.createTestProject(myFixture.findFileInTempDir("."), projectDescription)
Expand Down

0 comments on commit 6ab14f3

Please sign in to comment.