Cleanup getExecutablePath() to better respect the platform (#24)
diff --git a/.gitignore b/.gitignore
index 5a472d6..fda1750 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,7 @@
### Dart template
# Don’t commit the following directories created by pub.
.buildlog
+.dart_tool
.pub/
build/
packages
diff --git a/.travis.yml b/.travis.yml
index 83a837c..5722c5b 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,7 +1,6 @@
language: dart
sudo: false
dart:
- - stable
- dev
install:
- gem install coveralls-lcov
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 53b1990..8973425 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,7 @@
+#### 3.0.0
+
+* Cleanup getExecutablePath() to better respect the platform
+
#### 2.0.9
* Bumped `package:file` dependency
diff --git a/lib/src/interface/common.dart b/lib/src/interface/common.dart
index c0e80b1..c115332 100644
--- a/lib/src/interface/common.dart
+++ b/lib/src/interface/common.dart
@@ -2,58 +2,89 @@
// 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 'dart:io' show File, Directory;
-
-import 'package:path/path.dart' as p;
+import 'package:file/file.dart';
+import 'package:file/local.dart';
+import 'package:path/path.dart' show Context, Style;
import 'package:platform/platform.dart';
-/// Searches the `PATH` for the actual executable that [commandName] is supposed
-/// to launch.
+const Map<String, String> _osToPathStyle = const <String, String>{
+ 'linux': 'posix',
+ 'macos': 'posix',
+ 'android': 'posix',
+ 'ios': 'posix',
+ 'fuchsia': 'posix',
+ 'windows': 'windows',
+};
+
+/// Searches the `PATH` for the executable that [command] is supposed to launch.
///
-/// Return `null` if the executable cannot be found.
-String getExecutablePath(String commandName, String workingDirectory,
- {Platform platform}) {
- platform ??= new LocalPlatform();
- workingDirectory ??= Directory.current.path;
+/// This first builds a list of candidate paths where the executable may reside.
+/// If [command] is already an absolute path, then the `PATH` environment
+/// variable will not be consulted, and the specified absolute path will be the
+/// only candidate that is considered.
+///
+/// Once the list of candidate paths has been constructed, this will pick the
+/// first such path that represents an existent file.
+///
+/// Return `null` if there were no viable candidates, meaning the executable
+/// could not be found.
+///
+/// If [platform] is not specified, it will default to the current platform.
+String getExecutablePath(
+ String command,
+ String workingDirectory, {
+ Platform platform: const LocalPlatform(),
+ FileSystem fs: const LocalFileSystem(),
+}) {
+ assert(_osToPathStyle[platform.operatingSystem] == fs.path.style.name);
+
+ workingDirectory ??= fs.currentDirectory.path;
+ Context context =
+ new Context(style: fs.path.style, current: workingDirectory);
+
// TODO(goderbauer): refactor when github.com/google/platform.dart/issues/2
// is available.
String pathSeparator = platform.isWindows ? ';' : ':';
List<String> extensions = <String>[];
- if (platform.isWindows && p.extension(commandName).isEmpty) {
+ if (platform.isWindows && context.extension(command).isEmpty) {
extensions = platform.environment['PATHEXT'].split(pathSeparator);
}
List<String> candidates = <String>[];
- if (commandName.contains(p.separator)) {
- candidates =
- _getCandidatePaths(commandName, <String>[workingDirectory], extensions);
+ if (command.contains(context.separator)) {
+ candidates = _getCandidatePaths(
+ command, <String>[workingDirectory], extensions, context);
} else {
List<String> searchPath = platform.environment['PATH'].split(pathSeparator);
- candidates = _getCandidatePaths(commandName, searchPath, extensions);
+ candidates = _getCandidatePaths(command, searchPath, extensions, context);
}
- return candidates.firstWhere((String path) => new File(path).existsSync(),
+ return candidates.firstWhere((String path) => fs.file(path).existsSync(),
orElse: () => null);
}
-/// Returns all possible combinations of `$searchPath\$commandName.$ext` for
+/// Returns all possible combinations of `$searchPath\$command.$ext` for
/// `searchPath` in [searchPaths] and `ext` in [extensions].
///
/// If [extensions] is empty, it will just enumerate all
-/// `$searchPath\$commandName`.
-/// If [commandName] is an absolute path, it will just enumerate
-/// `$commandName.$ext`.
+/// `$searchPath\$command`.
+/// If [command] is an absolute path, it will just enumerate
+/// `$command.$ext`.
Iterable<String> _getCandidatePaths(
- String commandName, List<String> searchPaths, List<String> extensions) {
+ String command,
+ List<String> searchPaths,
+ List<String> extensions,
+ Context context,
+) {
List<String> withExtensions = extensions.isNotEmpty
- ? extensions.map((String ext) => '$commandName$ext').toList()
- : <String>[commandName];
- if (p.isAbsolute(commandName)) {
+ ? extensions.map((String ext) => '$command$ext').toList()
+ : <String>[command];
+ if (context.isAbsolute(command)) {
return withExtensions;
}
return searchPaths
.map((String path) =>
- withExtensions.map((String command) => p.join(path, command)))
+ withExtensions.map((String command) => context.join(path, command)))
.expand((Iterable<String> e) => e)
.toList();
}
diff --git a/pubspec.yaml b/pubspec.yaml
index 07637b8..a70deb8 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,5 +1,5 @@
name: process
-version: 2.0.9
+version: 3.0.0
authors:
- Todd Volkert <tvolkert@google.com>
- Michael Goderbauer <goderbauer@google.com>
@@ -7,14 +7,14 @@
homepage: https://github.com/google/process.dart
dependencies:
- file: '>=2.0.1 <4.0.0'
+ file: '>=2.0.1'
intl: '>=0.14.0 <0.16.0'
- meta: ^1.0.4
- path: ^1.4.0
- platform: '>=1.0.1 <3.0.0'
+ meta: ^1.1.2
+ path: ^1.5.1
+ platform: '>=1.0.1'
dev_dependencies:
- test: ^0.12.10
+ test: ^0.12.33
environment:
- sdk: '>=1.21.0 <2.0.0'
+ sdk: '>=2.0.0-dev.28.0 <2.0.0'
diff --git a/test/src/interface/common_test.dart b/test/src/interface/common_test.dart
index 1c82252..ba2bc5d 100644
--- a/test/src/interface/common_test.dart
+++ b/test/src/interface/common_test.dart
@@ -3,19 +3,19 @@
// BSD-style license that can be found in the LICENSE file.
import 'package:file/file.dart';
-import 'package:file/local.dart';
+import 'package:file/memory.dart';
import 'package:path/path.dart' as p;
import 'package:platform/platform.dart';
import 'package:process/src/interface/common.dart';
import 'package:test/test.dart';
void main() {
- FileSystem fs = new LocalFileSystem();
-
group('getExecutablePath', () {
+ FileSystem fs;
Directory workingDir, dir1, dir2, dir3;
setUp(() {
+ fs = new MemoryFileSystem();
workingDir = fs.systemTempDirectory.createTempSync('work_dir_');
dir1 = fs.systemTempDirectory.createTempSync('dir1_');
dir2 = fs.systemTempDirectory.createTempSync('dir2_');
@@ -32,11 +32,12 @@
setUp(() {
platform = new FakePlatform(
- operatingSystem: 'windows',
- environment: <String, String>{
- 'PATH': '${dir1.path};${dir2.path}',
- 'PATHEXT': '.exe;.bat'
- });
+ operatingSystem: 'windows',
+ environment: <String, String>{
+ 'PATH': '${dir1.path};${dir2.path}',
+ 'PATHEXT': '.exe;.bat'
+ },
+ );
});
test('absolute', () {
@@ -44,13 +45,21 @@
String expectedPath = command;
fs.file(command).createSync();
- String executablePath =
- getExecutablePath(command, workingDir.path, platform: platform);
+ String executablePath = getExecutablePath(
+ command,
+ workingDir.path,
+ platform: platform,
+ fs: fs,
+ );
_expectSamePath(executablePath, expectedPath);
command = p.withoutExtension(command);
- executablePath =
- getExecutablePath(command, workingDir.path, platform: platform);
+ executablePath = getExecutablePath(
+ command,
+ workingDir.path,
+ platform: platform,
+ fs: fs,
+ );
_expectSamePath(executablePath, expectedPath);
});
@@ -59,13 +68,21 @@
String expectedPath = p.join(dir2.path, command);
fs.file(expectedPath).createSync();
- String executablePath =
- getExecutablePath(command, workingDir.path, platform: platform);
+ String executablePath = getExecutablePath(
+ command,
+ workingDir.path,
+ platform: platform,
+ fs: fs,
+ );
_expectSamePath(executablePath, expectedPath);
command = p.withoutExtension(command);
- executablePath =
- getExecutablePath(command, workingDir.path, platform: platform);
+ executablePath = getExecutablePath(
+ command,
+ workingDir.path,
+ platform: platform,
+ fs: fs,
+ );
_expectSamePath(executablePath, expectedPath);
});
@@ -76,13 +93,21 @@
fs.file(expectedPath).createSync();
fs.file(wrongPath).createSync();
- String executablePath =
- getExecutablePath(command, workingDir.path, platform: platform);
+ String executablePath = getExecutablePath(
+ command,
+ workingDir.path,
+ platform: platform,
+ fs: fs,
+ );
_expectSamePath(executablePath, expectedPath);
command = p.withoutExtension(command);
- executablePath =
- getExecutablePath(command, workingDir.path, platform: platform);
+ executablePath = getExecutablePath(
+ command,
+ workingDir.path,
+ platform: platform,
+ fs: fs,
+ );
_expectSamePath(executablePath, expectedPath);
});
@@ -91,13 +116,21 @@
String expectedPath = p.join(workingDir.path, command);
fs.file(expectedPath).createSync(recursive: true);
- String executablePath =
- getExecutablePath(command, workingDir.path, platform: platform);
+ String executablePath = getExecutablePath(
+ command,
+ workingDir.path,
+ platform: platform,
+ fs: fs,
+ );
_expectSamePath(executablePath, expectedPath);
command = p.withoutExtension(command);
- executablePath =
- getExecutablePath(command, workingDir.path, platform: platform);
+ executablePath = getExecutablePath(
+ command,
+ workingDir.path,
+ platform: platform,
+ fs: fs,
+ );
_expectSamePath(executablePath, expectedPath);
});
@@ -108,13 +141,21 @@
fs.file(expectedPath).createSync();
fs.file(wrongPath).createSync();
- String executablePath =
- getExecutablePath(command, workingDir.path, platform: platform);
+ String executablePath = getExecutablePath(
+ command,
+ workingDir.path,
+ platform: platform,
+ fs: fs,
+ );
_expectSamePath(executablePath, expectedPath);
command = p.withoutExtension(command);
- executablePath =
- getExecutablePath(command, workingDir.path, platform: platform);
+ executablePath = getExecutablePath(
+ command,
+ workingDir.path,
+ platform: platform,
+ fs: fs,
+ );
_expectSamePath(executablePath, expectedPath);
});
@@ -127,19 +168,27 @@
fs.file(wrongPath1).createSync();
fs.file(wrongPath2).createSync();
- String executablePath =
- getExecutablePath(command, workingDir.path, platform: platform);
+ String executablePath = getExecutablePath(
+ command,
+ workingDir.path,
+ platform: platform,
+ fs: fs,
+ );
_expectSamePath(executablePath, expectedPath);
});
test('not found', () {
String command = 'foo.exe';
- String executablePath =
- getExecutablePath(command, workingDir.path, platform: platform);
+ String executablePath = getExecutablePath(
+ command,
+ workingDir.path,
+ platform: platform,
+ fs: fs,
+ );
expect(executablePath, isNull);
});
- });
+ }, skip: 'https://github.com/google/file.dart/issues/68');
group('on Linux', () {
Platform platform;
@@ -157,8 +206,12 @@
fs.file(command).createSync();
fs.file(wrongPath).createSync();
- String executablePath =
- getExecutablePath(command, workingDir.path, platform: platform);
+ String executablePath = getExecutablePath(
+ command,
+ workingDir.path,
+ platform: platform,
+ fs: fs,
+ );
_expectSamePath(executablePath, expectedPath);
});
@@ -169,16 +222,24 @@
fs.file(expectedPath).createSync();
fs.file(wrongPath).createSync();
- String executablePath =
- getExecutablePath(command, workingDir.path, platform: platform);
+ String executablePath = getExecutablePath(
+ command,
+ workingDir.path,
+ platform: platform,
+ fs: fs,
+ );
_expectSamePath(executablePath, expectedPath);
});
test('not found', () {
String command = 'foo';
- String executablePath =
- getExecutablePath(command, workingDir.path, platform: platform);
+ String executablePath = getExecutablePath(
+ command,
+ workingDir.path,
+ platform: platform,
+ fs: fs,
+ );
expect(executablePath, isNull);
});
});