Skip to content

Commit 2dec487

Browse files
committed
Add ability to configure cargo build features.
1 parent ec4571e commit 2dec487

File tree

7 files changed

+171
-27
lines changed

7 files changed

+171
-27
lines changed

RustEnhanced.sublime-commands

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,8 @@
3131
"caption": "Rust: Set Cargo Toolchain",
3232
"command": "cargo_set_toolchain"
3333
},
34+
{
35+
"caption": "Rust: Set Cargo Features",
36+
"command": "cargo_set_features"
37+
},
3438
]

docs/build.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ Command | Description
5959
Rust: Set Cargo Target | Set the Cargo target (`--lib`, `--example foo`, etc.) for each build variant. The "Automatic Detection" option will attempt to determine which target to use based on the current active view in Sublime (a test file will use `--test` or a binary will use `--bin`, etc.).
6060
Rust: Set Cargo Build Profile | Set whether or not to use the `--release` flag.
6161
Rust: Set Cargo Target Triple | Set the target triple (such as `x86_64-apple-darwin`).
62+
Rust: Set Cargo Features | Set the Cargo build features to use.
6263
Rust: Set Cargo Toolchain | Set the Rust toolchain to use (`nightly`, `beta`, etc.). Use the Targets > "All Targets" to set globally.
6364

6465
Caution: If you have not created a `sublime-project` file, then any changes
@@ -117,6 +118,8 @@ Setting Name | Description
117118
`target_triple` | If set, uses the `--target` flag with the given value.
118119
`toolchain` | The Rust toolchain to use (such as `nightly` or `beta`).
119120
`target` | The Cargo target (such as `"--bin myprog"`). Applies to `variants` only. Can be `"auto"` (see "Set Cargo Target" above).
121+
`no_default_features` | If True, sets the `--no-default-features` flag.
122+
`features` | A string with a space separated list of features to pass to the `--features` flag. Set to "ALL" to pass the `--all-features` flag.
120123
`extra_cargo_args` | Extra arguments passed to Cargo (before the `--` flags separator).
121124
`extra_run_args` | Extra arguments passed to Cargo (after the `--` flags separator).
122125

rust/cargo_config.py

Lines changed: 87 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ def show_next_question(self):
8383
item_info = getattr(self, 'items_' + q)()
8484
if not isinstance(item_info, dict):
8585
item_info = {'items': item_info}
86-
items = item_info['items']
86+
8787
f_selected = getattr(self, 'selected_' + q, None)
8888

8989
def make_choice(value):
@@ -94,34 +94,45 @@ def make_choice(value):
9494
self._sequence.extend(next)
9595
self.show_next_question()
9696

97-
def wrapper(index):
98-
if index != -1:
99-
chosen = items[index][1]
100-
RECENT_CHOICES[q] = chosen
101-
make_choice(chosen)
102-
10397
if q in self.input:
10498
make_choice(self.input[q])
105-
elif item_info.get('skip_if_one', False) and len(items) == 1:
106-
wrapper(0)
10799
else:
108-
# If the user manually edits the config and enters custom values
109-
# then it won't show up in the list (because it is not an exact
110-
# match). Add it so that it is a valid choice (assuming the user
111-
# entered a valid value).
112-
if 'default' in item_info:
113-
default_index = index_with(items,
114-
lambda x: x[1] == item_info['default'])
115-
if default_index == -1:
116-
items.append((item_info['default'], item_info['default']))
117-
# Determine the default selection.
118-
# Use the default provided by the items_ method, else
119-
# use the most recently used value.
120-
default = index_with(items,
121-
lambda x: x[1] == item_info.get('default',
122-
RECENT_CHOICES.get(q, '_NO_DEFAULT_SENTINEL_')))
123-
display_items = [x[0] for x in items]
124-
self.window.show_quick_panel(display_items, wrapper, 0, default)
100+
if 'items' in item_info:
101+
def wrapper(index):
102+
if index != -1:
103+
chosen = item_info['items'][index][1]
104+
RECENT_CHOICES[q] = chosen
105+
make_choice(chosen)
106+
107+
items = item_info['items']
108+
if item_info.get('skip_if_one', False) and len(items) == 1:
109+
wrapper(0)
110+
else:
111+
# If the user manually edits the config and enters custom
112+
# values then it won't show up in the list (because it is
113+
# not an exact match). Add it so that it is a valid
114+
# choice (assuming the user entered a valid value).
115+
if 'default' in item_info:
116+
default_index = index_with(items,
117+
lambda x: x[1] == item_info['default'])
118+
if default_index == -1:
119+
items.append((item_info['default'],
120+
item_info['default']))
121+
# Determine the default selection.
122+
# Use the default provided by the items_ method, else
123+
# use the most recently used value.
124+
default = index_with(items,
125+
lambda x: x[1] == item_info.get('default',
126+
RECENT_CHOICES.get(q, '_NO_DEFAULT_SENTINEL_')))
127+
display_items = [x[0] for x in items]
128+
self.window.show_quick_panel(display_items, wrapper, 0,
129+
default)
130+
elif 'caption' in item_info:
131+
self.window.show_input_panel(item_info['caption'],
132+
item_info.get('default', ''),
133+
make_choice, None, None)
134+
else:
135+
raise ValueError(item_info)
125136

126137
def items_package(self):
127138
# path/to/package: package_info
@@ -365,3 +376,53 @@ def done(self):
365376
self.choices['toolchain'])
366377
else:
367378
raise AssertionError(self.choices['which'])
379+
380+
381+
class CargoSetFeatures(CargoConfigBase):
382+
383+
sequence = ['package', 'target', 'no_default_features', 'features']
384+
385+
def items_no_default_features(self):
386+
current = self.settings.get_with_target(self.choices['package'],
387+
self.choices['target'],
388+
'no_default_features', False)
389+
items = [
390+
('Include default features.', False),
391+
('Do not include default features.', True)
392+
]
393+
return {
394+
'items': items,
395+
'default': current,
396+
}
397+
398+
def items_features(self):
399+
features = self.settings.get_with_target(self.choices['package'],
400+
self.choices['target'],
401+
'features', None)
402+
if features is None:
403+
package_path = self.choices['package']
404+
available_features = self.packages[package_path].get('features', {})
405+
items = list(available_features.keys())
406+
# Remove the "default" entry.
407+
if 'default' in items:
408+
del items[items.index('default')]
409+
if not self.choices['no_default_features']:
410+
# Don't show default features, (they are already included).
411+
for ft in available_features['default']:
412+
if ft in items:
413+
del items[items.index(ft)]
414+
features = ' '.join(items)
415+
return {
416+
'caption': 'Choose features (space separated, use "ALL" to use all features)',
417+
'default': features,
418+
}
419+
420+
def done(self):
421+
self.settings.set_with_target(self.choices['package'],
422+
self.choices['target'],
423+
'no_default_features',
424+
self.choices['no_default_features'])
425+
self.settings.set_with_target(self.choices['package'],
426+
self.choices['target'],
427+
'features',
428+
self.choices['features'])

rust/cargo_settings.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,19 @@ def get(key, default=None):
321321
if cmd_info.get('allows_json'):
322322
result.append('--message-format=json')
323323

324+
# features
325+
if cmd_info.get('allows_features', False):
326+
v = get('no_default_features', False)
327+
if v:
328+
result.append('--no-default-features')
329+
v = get('features', None)
330+
if v:
331+
if v.upper() == 'ALL':
332+
result.append('--all-features')
333+
else:
334+
result.append('--features')
335+
result.append(v)
336+
324337
# Add path from current active view (mainly for "cargo script").
325338
if cmd_info.get('wants_view_path', False):
326339
if not self._active_view_is_rust():

tests/multi-targets/Cargo.toml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ build = "build.rs"
77

88
[features]
99
nightly = []
10+
default = ["feat1"]
11+
feat1 = []
12+
feat2 = []
13+
feat3 = []
1014

1115
[[bin]]
1216
name = "otherbin"
@@ -27,6 +31,10 @@ path = "src/bin/bin1.rs"
2731
name = "bin2"
2832
path = "src/bin/bin2.rs"
2933

34+
[[bin]]
35+
name = "feats"
36+
path = "src/bin/feats.rs"
37+
3038
[[example]]
3139
name = "ex1"
3240
path = "examples/ex1.rs"

tests/multi-targets/src/bin/feats.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
fn main() {
2+
print!("feats: ");
3+
let mut v = Vec::new();
4+
if cfg!(feature="feat1") {
5+
v.push("feat1");
6+
}
7+
if cfg!(feature="feat2") {
8+
v.push("feat2")
9+
}
10+
if cfg!(feature="feat3") {
11+
v.push("feat3")
12+
}
13+
println!("{}", v.join(" "));
14+
}

tests/test_cargo_build.py

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ def _test_build_with_target(self, view):
6363
'libmulti_targets.rlib']),
6464
# I'm actually uncertain why Cargo builds all bins here.
6565
('--test test1', [exe('bin1'), exe('bin2'), exe('multi-targets'),
66-
exe('otherbin'),
66+
exe('otherbin'), exe('feats'),
6767
'libmulti_targets.rlib', 'test1-*']),
6868
# bench requires nightly
6969
]
@@ -307,3 +307,44 @@ def _test_script(self, view):
307307
self._run_build_wait('script')
308308
output = self._get_build_output(window)
309309
self.assertRegex(output, '(?m)^Hello Mystery$')
310+
311+
def test_features(self):
312+
"""Test feature selection."""
313+
self._with_open_file('tests/multi-targets/src/bin/feats.rs',
314+
self._test_features)
315+
316+
def _test_features(self, view):
317+
window = view.window()
318+
window.run_command('cargo_set_target', {'variant': 'run',
319+
'target': '--bin feats'})
320+
self._run_build_wait('run')
321+
output = self._get_build_output(window)
322+
self.assertRegex(output, '(?m)^feats: feat1$')
323+
324+
window.run_command('cargo_set_features', {'target': None,
325+
'no_default_features': True,
326+
'features': ''})
327+
self._run_build_wait('run')
328+
output = self._get_build_output(window)
329+
self.assertRegex(output, '(?m)^feats: $')
330+
331+
window.run_command('cargo_set_features', {'target': None,
332+
'no_default_features': False,
333+
'features': 'feat3'})
334+
self._run_build_wait('run')
335+
output = self._get_build_output(window)
336+
self.assertRegex(output, '(?m)^feats: feat1 feat3$')
337+
338+
window.run_command('cargo_set_features', {'target': None,
339+
'no_default_features': True,
340+
'features': 'feat2 feat3'})
341+
self._run_build_wait('run')
342+
output = self._get_build_output(window)
343+
self.assertRegex(output, '(?m)^feats: feat2 feat3$')
344+
345+
window.run_command('cargo_set_features', {'target': None,
346+
'no_default_features': True,
347+
'features': 'ALL'})
348+
self._run_build_wait('run')
349+
output = self._get_build_output(window)
350+
self.assertRegex(output, '(?m)^feats: feat1 feat2 feat3$')

0 commit comments

Comments
 (0)