process.dart 4.58 KB
Newer Older
1 2 3 4 5 6 7 8
// Copyright 2015 The Chromium Authors. 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:async';
import 'dart:convert';
import 'dart:io';

9 10 11
import 'package:path/path.dart' as path;

import '../dart/sdk.dart';
12
import '../globals.dart';
13

Devon Carew's avatar
Devon Carew committed
14 15
typedef String StringConverter(String string);

16 17
/// This runs the command in the background from the specified working
/// directory. Completes when the process has been started.
18
Future<Process> runCommand(List<String> cmd, { String workingDirectory }) async {
19 20 21 22 23 24 25 26 27 28 29
  printTrace(cmd.join(' '));
  String executable = cmd[0];
  List<String> arguments = cmd.length > 1 ? cmd.sublist(1) : [];
  Process process = await Process.start(
    executable,
    arguments,
    workingDirectory: workingDirectory
  );
  return process;
}

30
/// This runs the command and streams stdout/stderr from the child process to
31
/// this process' stdout/stderr. Completes with the process's exit code.
32
Future<int> runCommandAndStreamOutput(List<String> cmd, {
Devon Carew's avatar
Devon Carew committed
33
  String workingDirectory,
34 35
  String prefix: '',
  RegExp filter,
Devon Carew's avatar
Devon Carew committed
36
  StringConverter mapFunction
37
}) async {
38 39
  Process process = await runCommand(cmd,
                                     workingDirectory: workingDirectory);
40 41 42 43 44
  process.stdout
    .transform(UTF8.decoder)
    .transform(const LineSplitter())
    .where((String line) => filter == null ? true : filter.hasMatch(line))
    .listen((String line) {
Devon Carew's avatar
Devon Carew committed
45 46 47 48
      if (mapFunction != null)
        line = mapFunction(line);
      if (line != null)
        printStatus('$prefix$line');
49 50 51 52 53 54
    });
  process.stderr
    .transform(UTF8.decoder)
    .transform(const LineSplitter())
    .where((String line) => filter == null ? true : filter.hasMatch(line))
    .listen((String line) {
Devon Carew's avatar
Devon Carew committed
55 56 57 58
      if (mapFunction != null)
        line = mapFunction(line);
      if (line != null)
        printError('$prefix$line');
59
    });
Hixie's avatar
Hixie committed
60
  return await process.exitCode;
61
}
62

Ian Hickson's avatar
Ian Hickson committed
63
Future<Null> runAndKill(List<String> cmd, Duration timeout) {
64
  Future<Process> proc = runDetached(cmd);
Ian Hickson's avatar
Ian Hickson committed
65
  return new Future<Null>.delayed(timeout, () async {
66
    printTrace('Intentionally killing ${cmd[0]}');
67 68 69 70
    Process.killPid((await proc).pid);
  });
}

Hixie's avatar
Hixie committed
71
Future<Process> runDetached(List<String> cmd) {
72
  printTrace(cmd.join(' '));
73 74 75 76 77 78
  Future<Process> proc = Process.start(
      cmd[0], cmd.getRange(1, cmd.length).toList(),
      mode: ProcessStartMode.DETACHED);
  return proc;
}

79 80
/// Run cmd and return stdout.
/// Throws an error if cmd exits with a non-zero value.
Devon Carew's avatar
Devon Carew committed
81 82 83 84 85 86
String runCheckedSync(List<String> cmd, {
  String workingDirectory, bool truncateCommand: false
}) {
  return _runWithLoggingSync(
    cmd, workingDirectory: workingDirectory, checked: true, noisyErrors: true, truncateCommand: truncateCommand
  );
87
}
88 89

/// Run cmd and return stdout.
90 91 92
String runSync(List<String> cmd, { String workingDirectory }) {
  return _runWithLoggingSync(cmd, workingDirectory: workingDirectory);
}
93

Devon Carew's avatar
Devon Carew committed
94
/// Return the platform specific name for the given Dart SDK binary. So, `pub`
95 96
/// ==> `pub.bat`.  The default SDK location can be overridden with a specified
/// [sdkLocation].
pq's avatar
pq committed
97
String sdkBinaryName(String name, { String sdkLocation }) {
98
  return path.absolute(path.join(sdkLocation ?? dartSdkPath, 'bin', Platform.isWindows ? '$name.bat' : name));
Devon Carew's avatar
Devon Carew committed
99 100
}

101
bool exitsHappy(List<String> cli) {
Devon Carew's avatar
Devon Carew committed
102 103
  printTrace(cli.join(' '));

104 105 106 107 108 109 110
  try {
    return Process.runSync(cli.first, cli.sublist(1)).exitCode == 0;
  } catch (error) {
    return false;
  }
}

111 112
String _runWithLoggingSync(List<String> cmd, {
  bool checked: false,
Devon Carew's avatar
Devon Carew committed
113
  bool noisyErrors: false,
Devon Carew's avatar
Devon Carew committed
114 115
  String workingDirectory,
  bool truncateCommand: false
116
}) {
Devon Carew's avatar
Devon Carew committed
117 118 119 120
  String cmdText = cmd.join(' ');
  if (truncateCommand && cmdText.length > 160)
    cmdText = cmdText.substring(0, 160) + '…';
  printTrace(cmdText);
121
  ProcessResult results =
122
      Process.runSync(cmd[0], cmd.getRange(1, cmd.length).toList(), workingDirectory: workingDirectory);
Devon Carew's avatar
Devon Carew committed
123 124 125 126 127 128 129 130 131 132

  printTrace('Exit code ${results.exitCode} from: ${cmd.join(' ')}');

  if (results.stdout.isNotEmpty) {
    if (results.exitCode != 0 && noisyErrors)
      printStatus(results.stdout.trim());
    else
      printTrace(results.stdout.trim());
  }

133
  if (results.exitCode != 0) {
Devon Carew's avatar
Devon Carew committed
134 135
    if (results.stderr.isNotEmpty) {
      if (noisyErrors)
Devon Carew's avatar
Devon Carew committed
136
        printError(results.stderr.trim());
Devon Carew's avatar
Devon Carew committed
137 138
      else
        printTrace(results.stderr.trim());
Devon Carew's avatar
Devon Carew committed
139
    }
Devon Carew's avatar
Devon Carew committed
140

141
    if (checked)
Devon Carew's avatar
Devon Carew committed
142
      throw 'Exit code ${results.exitCode} from: ${cmd.join(' ')}';
143
  }
Devon Carew's avatar
Devon Carew committed
144

145
  return results.stdout.trim();
146
}
147 148 149

class ProcessExit implements Exception {
  ProcessExit(this.exitCode);
150 151 152

  final int exitCode;

Hixie's avatar
Hixie committed
153
  String get message => 'ProcessExit: $exitCode';
154 155

  @override
156 157
  String toString() => message;
}