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 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.
  • Loading branch information
kumbayo committed Dec 17, 2018
1 parent 971c9d2 commit 28c56ea
Show file tree
Hide file tree
Showing 5 changed files with 117 additions and 2 deletions.
Expand Up @@ -6,10 +6,16 @@
package org.rust.cargo.project.configurable

import com.intellij.openapi.project.Project
import com.intellij.openapi.ui.ComboBox
import com.intellij.ui.components.JBCheckBox
import com.intellij.ui.components.JBTextField
import com.intellij.ui.components.Label
import org.rust.cargo.project.settings.RustProjectSettingsService.FeaturesSetting
import org.rust.ide.ui.layout
import org.rust.openapiext.CheckboxDelegate
import javax.swing.JComponent
import java.awt.Component
import javax.swing.*
import kotlin.reflect.KProperty

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

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

private val cargoFeaturesComboBox = ComboBox<FeaturesSetting>().apply {
FeaturesSetting.values().forEach { addItem(it) }
setRenderer(object : DefaultListCellRenderer() {
override fun getListCellRendererComponent(list: JList<*>?, value: Any?, index: Int, isSelected: Boolean, cellHasFocus: Boolean): Component {
val value2 = when (value as FeaturesSetting) {
FeaturesSetting.All -> "All"
FeaturesSetting.Default -> "Default"
FeaturesSetting.NoDefault -> "No default"
}
return super.getListCellRendererComponent(list, value2, index, isSelected, cellHasFocus)
}
})
}
private var cargoFeatures: FeaturesSetting by FeaturesComboDelegate()

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

override fun getDisplayName(): String = "Cargo"

override fun createComponent(): JComponent = layout {
Expand All @@ -49,13 +73,30 @@ class CargoConfigurable(project: Project) : RsConfigurableBase(project) {
Pass `-Z offline` option to cargo not to perform network requests.
Used only for nightly toolchain.
""")

cargoFeaturesAdditionalField.toolTipText = "Additional features to activate. Space-separated."
cargoFeaturesAdditionalField.emptyText.text = "Additional features to activate. Space-separated."

val p = JPanel().apply {
layout = BoxLayout(this, BoxLayout.X_AXIS)
add(cargoFeaturesComboBox)
add(Label("+"))
add(cargoFeaturesAdditionalField)
}

row("Cargo metadata features:", p, """
Cargo features to enable.
Pass --all-features, --no-default-features and --features to `cargo metadata`.
""")
}

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 +107,8 @@ class CargoConfigurable(project: Project) : RsConfigurableBase(project) {
useCargoCheckForBuild = useCargoCheckForBuild,
useCargoCheckAnnotator = useCargoCheckAnnotator,
compileAllTargets = compileAllTargets,
cargoFeatures = cargoFeatures,
cargoFeaturesAdditional = cargoFeaturesAdditional,
useOfflineForCargoCheck = useOfflineForCargoCheck
)
}
Expand All @@ -75,6 +118,28 @@ 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 FeaturesComboDelegate {
operator fun getValue(thisRef: Any?, property: KProperty<*>): FeaturesSetting {
return cargoFeaturesComboBox.model.selectedItem as FeaturesSetting
}

operator fun setValue(thisRef: Any?, property: KProperty<*>, value: FeaturesSetting) {
cargoFeaturesComboBox.selectedItem = value
}
}

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,12 +21,21 @@ 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 showTestToolWindow: Boolean,
val useSkipChildren: Boolean
)

// We cannot add a toString() method, as the enum serialization/deserialization in XmlSerializerImpl uses toString()
enum class FeaturesSetting {
All,
Default,
NoDefault,
}

var data: Data

val toolchain: RustToolchain? get() = data.toolchain
Expand All @@ -39,6 +48,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 showTestToolWindow: Boolean get() = data.showTestToolWindow
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 showTestToolWindow: Boolean = true,
Expand All @@ -55,6 +58,8 @@ class RustProjectSettingsServiceImpl(
useCargoCheckForBuild = state.useCargoCheckForBuild,
useCargoCheckAnnotator = state.useCargoCheckAnnotator,
compileAllTargets = state.compileAllTargets,
cargoFeatures = state.cargoFeatures,
cargoFeaturesAdditional = state.cargoFeaturesAdditional,
useOfflineForCargoCheck = state.useOfflineForCargoCheck,
expandMacros = state.expandMacros,
showTestToolWindow = state.showTestToolWindow,
Expand All @@ -69,6 +74,8 @@ class RustProjectSettingsServiceImpl(
useCargoCheckForBuild = value.useCargoCheckForBuild,
useCargoCheckAnnotator = value.useCargoCheckAnnotator,
compileAllTargets = value.compileAllTargets,
cargoFeatures = value.cargoFeatures,
cargoFeaturesAdditional = value.cargoFeaturesAdditional,
useOfflineForCargoCheck = value.useOfflineForCargoCheck,
expandMacros = value.expandMacros,
showTestToolWindow = value.showTestToolWindow,
Expand Down
25 changes: 24 additions & 1 deletion src/main/kotlin/org/rust/cargo/toolchain/Cargo.kt
Expand Up @@ -19,6 +19,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 @@ -58,11 +59,33 @@ 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 in this case
// cargo will just ignore them anyway
}
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 @@ -48,6 +55,8 @@ class RustProjectSettingsServiceTest : LightPlatformTestCase() {
useCargoCheckForBuild = false,
useCargoCheckAnnotator = true,
compileAllTargets = false,
cargoFeatures = RustProjectSettingsService.FeaturesSetting.NoDefault,
cargoFeaturesAdditional = listOf("foo", "bar"),
useOfflineForCargoCheck = true,
expandMacros = false,
showTestToolWindow = false,
Expand Down

0 comments on commit 28c56ea

Please sign in to comment.