Skip to content

Commit

Permalink
CARGO: Allow choosing which features are passed to "cargo metadata"
Browse files Browse the repository at this point in the history
We want to accurately track feature flags so lets not tell cargo
to always enable all features for us.
Always enabling all features can also causes problems as some non standard
feature dependency may not be available.

Instead let the user to configure which features should be enabled.
The new default behavior is to only enable default features.
This setting is stored per project and reachable via Settings >
Languages & Frameworks > Rust > Cargo.

We do not yet use this features setting for any other commands like
- cargo check
- cargo build
- cargo test
- cargo run
- ...

Fixes intellij-rust#2635
See also intellij-rust#651 ecce3fa.

The field cargoFeaturesAdditional in State needs to be a mutable list
as the deserialization writes to the list instead of allocating a new list.
  • Loading branch information
kumbayo committed Oct 21, 2018
1 parent 415a59b commit 5841890
Show file tree
Hide file tree
Showing 6 changed files with 105 additions and 2 deletions.
Expand Up @@ -7,9 +7,14 @@ package org.rust.cargo.project.configurable

import com.intellij.openapi.project.Project
import com.intellij.ui.components.JBCheckBox
import org.rust.cargo.project.settings.RustProjectSettingsService.FeaturesSetting
import org.rust.ide.ui.layout
import org.rust.openapiext.CheckboxDelegate
import javax.swing.ButtonGroup
import javax.swing.JComponent
import javax.swing.JRadioButton
import javax.swing.JTextField
import kotlin.reflect.KProperty

class CargoConfigurable(project: Project) : RsConfigurableBase(project) {

Expand All @@ -28,6 +33,15 @@ class CargoConfigurable(project: Project) : RsConfigurableBase(project) {
private val compileAllTargetsCheckBox = JBCheckBox()
private var compileAllTargets: Boolean by CheckboxDelegate(compileAllTargetsCheckBox)

private val cargoFeaturesAllRadio = JRadioButton()
private val cargoFeaturesDefaultRadio = JRadioButton()
private val cargoFeaturesNoDefaultRadio = JRadioButton()
private val cargoFeaturesGroup = ButtonGroup().apply { add(cargoFeaturesAllRadio); add(cargoFeaturesDefaultRadio); add(cargoFeaturesNoDefaultRadio) }
private var cargoFeatures: FeaturesSetting by FeaturesRadioButtonDelegate()

private val cargoFeaturesAdditionalField = JTextField()
private var cargoFeaturesAdditional: List<String> by FeaturesTextFieldDelegate()

override fun getDisplayName(): String = "Cargo"

override fun createComponent(): JComponent = layout {
Expand All @@ -49,13 +63,21 @@ class CargoConfigurable(project: Project) : RsConfigurableBase(project) {
Pass `-Z offline` option to cargo not to perform network requests.
Used only for nightly toolchain.
""")
block("Cargo features") {
row("All", cargoFeaturesAllRadio, "Activate all available features.")
row("Default", cargoFeaturesDefaultRadio, "Activate `default` features.")
row("No Default", cargoFeaturesNoDefaultRadio, "Do not activate the `default` features.")
row("Additional features", cargoFeaturesAdditionalField, "Additional features to activate. Space-separated.")
}
}

override fun isModified(): Boolean {
return autoUpdateEnabled != settings.autoUpdateEnabled
|| useCargoCheckForBuild != settings.useCargoCheckForBuild
|| useCargoCheckAnnotator != settings.useCargoCheckAnnotator
|| compileAllTargets != settings.compileAllTargets
|| cargoFeatures != settings.cargoFeatures
|| cargoFeaturesAdditional != settings.cargoFeaturesAdditional
|| useOfflineForCargoCheck != settings.useOfflineForCargoCheck
}

Expand All @@ -66,6 +88,8 @@ class CargoConfigurable(project: Project) : RsConfigurableBase(project) {
useCargoCheckForBuild = useCargoCheckForBuild,
useCargoCheckAnnotator = useCargoCheckAnnotator,
compileAllTargets = compileAllTargets,
cargoFeatures = cargoFeatures,
cargoFeaturesAdditional = cargoFeaturesAdditional,
useOfflineForCargoCheck = useOfflineForCargoCheck
)
}
Expand All @@ -75,6 +99,37 @@ class CargoConfigurable(project: Project) : RsConfigurableBase(project) {
useCargoCheckForBuild = settings.useCargoCheckForBuild
useCargoCheckAnnotator = settings.useCargoCheckAnnotator
compileAllTargets = settings.compileAllTargets
cargoFeatures = settings.cargoFeatures
cargoFeaturesAdditional = settings.cargoFeaturesAdditional
useOfflineForCargoCheck = settings.useOfflineForCargoCheck
}

private inner class FeaturesRadioButtonDelegate() {
operator fun getValue(thisRef: Any?, property: KProperty<*>): FeaturesSetting {
return when {
cargoFeaturesAllRadio.isSelected -> FeaturesSetting.All
cargoFeaturesDefaultRadio.isSelected -> FeaturesSetting.Default
cargoFeaturesNoDefaultRadio.isSelected -> FeaturesSetting.NoDefault
else -> error("not reachable")
}
}

operator fun setValue(thisRef: Any?, property: KProperty<*>, value: FeaturesSetting) {
when (value) {
FeaturesSetting.All -> cargoFeaturesAllRadio.isSelected = true
FeaturesSetting.Default -> cargoFeaturesDefaultRadio.isSelected = true
FeaturesSetting.NoDefault -> cargoFeaturesNoDefaultRadio.isSelected = true
}
}
}

private inner class FeaturesTextFieldDelegate() {
operator fun getValue(thisRef: Any?, property: KProperty<*>): List<String> {
return cargoFeaturesAdditionalField.text.split(' ').filterNot { it.isEmpty() }
}

operator fun setValue(thisRef: Any?, property: KProperty<*>, value: List<String>) {
cargoFeaturesAdditionalField.text = value.joinToString(separator = " ")
}
}
}
Expand Up @@ -21,11 +21,19 @@ interface RustProjectSettingsService {
val useCargoCheckForBuild: Boolean,
val useCargoCheckAnnotator: Boolean,
val compileAllTargets: Boolean,
val cargoFeatures: FeaturesSetting,
val cargoFeaturesAdditional: List<String>,
val useOfflineForCargoCheck: Boolean,
val expandMacros: Boolean,
val useSkipChildren: Boolean
)

enum class FeaturesSetting {
All,
Default,
NoDefault
}

var data: Data

val toolchain: RustToolchain? get() = data.toolchain
Expand All @@ -38,6 +46,8 @@ interface RustProjectSettingsService {
val useCargoCheckForBuild: Boolean get() = data.useCargoCheckForBuild
val useCargoCheckAnnotator: Boolean get() = data.useCargoCheckAnnotator
val compileAllTargets: Boolean get() = data.compileAllTargets
val cargoFeatures: FeaturesSetting get() = data.cargoFeatures
val cargoFeaturesAdditional: List<String> get() = data.cargoFeaturesAdditional
val useOfflineForCargoCheck: Boolean get() = data.useOfflineForCargoCheck
val expandMacros: Boolean get() = data.expandMacros
val useSkipChildren: Boolean get() = data.useSkipChildren
Expand Down
Expand Up @@ -12,6 +12,7 @@ import com.intellij.openapi.project.Project
import com.intellij.util.io.systemIndependentPath
import org.rust.cargo.project.configurable.RsProjectConfigurable
import org.rust.cargo.project.settings.RustProjectSettingsService
import org.rust.cargo.project.settings.RustProjectSettingsService.FeaturesSetting
import org.rust.cargo.toolchain.RustToolchain
import java.nio.file.Paths

Expand All @@ -29,6 +30,8 @@ class RustProjectSettingsServiceImpl(
var useCargoCheckForBuild: Boolean = true,
var useCargoCheckAnnotator: Boolean = false,
var compileAllTargets: Boolean = true,
var cargoFeatures: FeaturesSetting = FeaturesSetting.Default,
var cargoFeaturesAdditional: List<String> = mutableListOf(),
var useOfflineForCargoCheck: Boolean = false,
var expandMacros: Boolean = true,
var useSkipChildren: Boolean = false
Expand All @@ -54,6 +57,8 @@ class RustProjectSettingsServiceImpl(
useCargoCheckForBuild = state.useCargoCheckForBuild,
useCargoCheckAnnotator = state.useCargoCheckAnnotator,
compileAllTargets = state.compileAllTargets,
cargoFeatures = state.cargoFeatures,
cargoFeaturesAdditional = state.cargoFeaturesAdditional,
useOfflineForCargoCheck = state.useOfflineForCargoCheck,
expandMacros = state.expandMacros,
useSkipChildren = state.useSkipChildren
Expand All @@ -67,6 +72,8 @@ class RustProjectSettingsServiceImpl(
useCargoCheckForBuild = value.useCargoCheckForBuild,
useCargoCheckAnnotator = value.useCargoCheckAnnotator,
compileAllTargets = value.compileAllTargets,
cargoFeatures = value.cargoFeatures,
cargoFeaturesAdditional = value.cargoFeaturesAdditional,
useOfflineForCargoCheck = value.useOfflineForCargoCheck,
expandMacros = value.expandMacros,
useSkipChildren = value.useSkipChildren
Expand Down
24 changes: 23 additions & 1 deletion src/main/kotlin/org/rust/cargo/toolchain/Cargo.kt
Expand Up @@ -18,6 +18,7 @@ import com.intellij.openapi.vfs.VirtualFile
import com.intellij.util.net.HttpConfigurable
import org.jetbrains.annotations.TestOnly
import org.rust.cargo.CargoConstants.RUST_BACTRACE_ENV_VAR
import org.rust.cargo.project.settings.RustProjectSettingsService.FeaturesSetting
import org.rust.cargo.project.settings.rustSettings
import org.rust.cargo.project.workspace.CargoWorkspace
import org.rust.cargo.toolchain.impl.CargoMetadata
Expand Down Expand Up @@ -57,11 +58,32 @@ class Cargo(private val cargoExecutable: Path) {
*/
@Throws(ExecutionException::class)
fun fullProjectDescription(owner: Project, projectDirectory: Path, listener: ProcessListener? = null): CargoWorkspace {
val additionalArgs = mutableListOf("--verbose", "--format-version", "1", "--all-features")
val additionalArgs = mutableListOf("--verbose", "--format-version", "1")
if (owner.rustSettings.useOfflineForCargoCheck) {
additionalArgs += "-Zoffline"
}

val featuresAdditional = owner.rustSettings.cargoFeaturesAdditional
when (owner.rustSettings.cargoFeatures) {
FeaturesSetting.All -> {
additionalArgs += "--all-features"
// Passing --features is not necessary here
}
FeaturesSetting.Default -> {
if (!featuresAdditional.isEmpty()) {
additionalArgs += "--features"
additionalArgs += featuresAdditional.joinToString(separator = " ")
}
}
FeaturesSetting.NoDefault -> {
additionalArgs += "--no-default-features"
if (!featuresAdditional.isEmpty()) {
additionalArgs += "--features"
additionalArgs += featuresAdditional.joinToString(separator = " ")
}
}
}

val json = CargoCommandLine("metadata", projectDirectory, additionalArgs)
.execute(owner, listener)
.stdout
Expand Down
Expand Up @@ -24,6 +24,13 @@ class RustProjectSettingsServiceTest : LightPlatformTestCase() {
val text = """
<State>
<option name="autoUpdateEnabled" value="false" />
<option name="cargoFeatures" value="NoDefault" />
<option name="cargoFeaturesAdditional">
<list>
<option value="foo" />
<option value="bar" />
</list>
</option>
<option name="compileAllTargets" value="false" />
<option name="expandMacros" value="false" />
<option name="explicitPathToStdlib" value="/stdlib" />
Expand All @@ -47,6 +54,8 @@ class RustProjectSettingsServiceTest : LightPlatformTestCase() {
useCargoCheckForBuild = false,
useCargoCheckAnnotator = true,
compileAllTargets = false,
cargoFeatures = RustProjectSettingsService.FeaturesSetting.NoDefault,
cargoFeaturesAdditional = listOf("foo", "bar"),
useOfflineForCargoCheck = true,
expandMacros = false,
useSkipChildren = true
Expand Down
Expand Up @@ -97,7 +97,7 @@ class CargoProjectStructureTest : RustWithToolchainTestBase() {
Features
Package(foo-0.1.0)
Feature(conditional1,enabled)
Feature(conditional2,enabled)
Feature(conditional2,disabled)
Feature(default,enabled)
""","""
Root
Expand Down

0 comments on commit 5841890

Please sign in to comment.