Skip to content
Snippets Groups Projects
Commit 44166965 authored by Natalie Weizenbaum's avatar Natalie Weizenbaum
Browse files

Support features with "pub global activate". (#1661)

Support features with "pub global activate".

Partially addresses #1593
parent aa5918b1
No related branches found
No related tags found
No related merge requests found
......@@ -7,6 +7,7 @@ import 'dart:async';
import 'package:pub_semver/pub_semver.dart';
import '../command.dart';
import '../package_name.dart';
import '../utils.dart';
/// Handles the `global activate` pub command.
......@@ -22,6 +23,12 @@ class GlobalActivateCommand extends PubCommand {
allowed: ["git", "hosted", "path"],
defaultsTo: "hosted");
argParser.addOption("features",
abbr: "f", help: "Feature(s) to enable.", allowMultiple: true);
argParser.addOption("omit-features",
abbr: "F", help: "Feature(s) to disable.", allowMultiple: true);
argParser.addFlag("no-executables",
negatable: false, help: "Do not put executables on PATH.");
......@@ -49,6 +56,18 @@ class GlobalActivateCommand extends PubCommand {
executables = [];
}
var features = <String, FeatureDependency>{};
for (var feature in argResults["features"] ?? []) {
features[feature] = FeatureDependency.required;
}
for (var feature in argResults["omit-features"] ?? []) {
if (features.containsKey(feature)) {
usageException("Cannot both enable and disable $feature.");
}
features[feature] = FeatureDependency.unused;
}
var overwrite = argResults["overwrite"];
var args = argResults.rest;
......@@ -72,7 +91,7 @@ class GlobalActivateCommand extends PubCommand {
// TODO(rnystrom): Allow passing in a Git ref too.
validateNoExtraArgs();
return globals.activateGit(repo, executables,
overwriteBinStubs: overwrite);
features: features, overwriteBinStubs: overwrite);
case "hosted":
var package = readArg("No package to activate given.");
......@@ -89,9 +108,16 @@ class GlobalActivateCommand extends PubCommand {
validateNoExtraArgs();
return globals.activateHosted(package, constraint, executables,
overwriteBinStubs: overwrite);
features: features, overwriteBinStubs: overwrite);
case "path":
if (features.isNotEmpty) {
// Globally-activated path packages just use the existing lockfile, so
// we can't change the feature selection.
usageException("--features and --omit-features may not be used with "
"the path source.");
}
var path = readArg("No package to activate given.");
validateNoExtraArgs();
return globals.activatePath(path, executables,
......
......@@ -79,11 +79,13 @@ class GlobalPackages {
/// If `null`, all executables in the package will get binstubs. If empty, no
/// binstubs will be created.
///
/// if [overwriteBinStubs] is `true`, any binstubs that collide with
/// The [features] map controls which features of the package to activate.
///
/// If [overwriteBinStubs] is `true`, any binstubs that collide with
/// existing binstubs in other packages will be overwritten by this one's.
/// Otherwise, the previous ones will be preserved.
Future activateGit(String repo, List<String> executables,
{bool overwriteBinStubs}) async {
{Map<String, FeatureDependency> features, bool overwriteBinStubs}) async {
var name = await cache.git.getPackageNameFromRepo(repo);
// Call this just to log what the current active package is, if any.
_describeActive(name);
......@@ -95,7 +97,8 @@ class GlobalPackages {
await _installInCache(
cache.git.source
.refFor(name, repo)
.withConstraint(VersionConstraint.any),
.withConstraint(VersionConstraint.any)
.withFeatures(features ?? const {}),
executables,
overwriteBinStubs: overwriteBinStubs);
}
......@@ -103,6 +106,8 @@ class GlobalPackages {
/// Finds the latest version of the hosted package with [name] that matches
/// [constraint] and makes it the active global version.
///
/// The [features] map controls which features of the package to activate.
///
/// [executables] is the names of the executables that should have binstubs.
/// If `null`, all executables in the package will get binstubs. If empty, no
/// binstubs will be created.
......@@ -112,10 +117,13 @@ class GlobalPackages {
/// Otherwise, the previous ones will be preserved.
Future activateHosted(
String name, VersionConstraint constraint, List<String> executables,
{bool overwriteBinStubs}) async {
{Map<String, FeatureDependency> features, bool overwriteBinStubs}) async {
_describeActive(name);
await _installInCache(
cache.hosted.source.refFor(name).withConstraint(constraint),
cache.hosted.source
.refFor(name)
.withConstraint(constraint)
.withFeatures(features ?? const {}),
executables,
overwriteBinStubs: overwriteBinStubs);
}
......
// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'package:test/test.dart';
import '../../descriptor.dart' as d;
import '../../test_pub.dart';
main() {
test('enables default-on features by default', () async {
await servePackages((builder) {
builder.serve("foo", "1.0.0", pubspec: {
"features": {
"stuff": {
"dependencies": {"bar": "1.0.0"}
},
"things": {
"default": false,
"dependencies": {"baz": "1.0.0"}
}
}
});
builder.serve("bar", "1.0.0");
builder.serve("baz", "1.0.0");
});
await runPub(args: ["global", "activate", "foo"], output: contains("""
Resolving dependencies...
+ bar 1.0.0
+ foo 1.0.0
Downloading"""));
});
test('can enable default-off features', () async {
await servePackages((builder) {
builder.serve("foo", "1.0.0", pubspec: {
"features": {
"stuff": {
"dependencies": {"bar": "1.0.0"}
},
"things": {
"default": false,
"dependencies": {"baz": "1.0.0"}
}
}
});
builder.serve("bar", "1.0.0");
builder.serve("baz", "1.0.0");
});
await runPub(
args: ["global", "activate", "foo", "--features", "things"],
output: contains("""
Resolving dependencies...
+ bar 1.0.0
+ baz 1.0.0
+ foo 1.0.0
Downloading"""));
});
test('can disable default-on features', () async {
await servePackages((builder) {
builder.serve("foo", "1.0.0", pubspec: {
"features": {
"stuff": {
"dependencies": {"bar": "1.0.0"}
},
"things": {
"default": false,
"dependencies": {"baz": "1.0.0"}
}
}
});
builder.serve("bar", "1.0.0");
builder.serve("baz", "1.0.0");
});
await runPub(
args: ["global", "activate", "foo", "--omit-features", "stuff"],
output: contains("""
Resolving dependencies...
+ foo 1.0.0
Downloading"""));
});
test('supports multiple arguments', () async {
await servePackages((builder) {
builder.serve("foo", "1.0.0", pubspec: {
"features": {
"stuff": {
"default": false,
"dependencies": {"bar": "1.0.0"}
},
"things": {
"default": false,
"dependencies": {"baz": "1.0.0"}
}
}
});
builder.serve("bar", "1.0.0");
builder.serve("baz", "1.0.0");
});
await runPub(
args: ["global", "activate", "foo", "--features", "things,stuff"],
output: contains("""
Resolving dependencies...
+ bar 1.0.0
+ baz 1.0.0
+ foo 1.0.0
Downloading"""));
});
test('can both enable and disable', () async {
await servePackages((builder) {
builder.serve("foo", "1.0.0", pubspec: {
"features": {
"stuff": {
"dependencies": {"bar": "1.0.0"}
},
"things": {
"default": false,
"dependencies": {"baz": "1.0.0"}
}
}
});
builder.serve("bar", "1.0.0");
builder.serve("baz", "1.0.0");
});
await runPub(args: [
"global",
"activate",
"foo",
"--features",
"things",
"--omit-features",
"stuff"
], output: contains("""
Resolving dependencies...
+ baz 1.0.0
+ foo 1.0.0
Downloading"""));
});
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment