os.dart 4.05 KB
Newer Older
1 2 3 4
// 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.

5
import 'dart:async';
6 7
import 'dart:io';

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

11
import 'context.dart';
12
import 'process.dart';
13 14

/// Returns [OperatingSystemUtils] active in the current app context (i.e. zone).
15 16 17
OperatingSystemUtils get os {
  return context[OperatingSystemUtils] ?? (context[OperatingSystemUtils] = new OperatingSystemUtils._());
}
18 19 20 21 22 23

abstract class OperatingSystemUtils {
  factory OperatingSystemUtils._() {
    if (Platform.isWindows) {
      return new _WindowsUtils();
    } else {
Adam Barth's avatar
Adam Barth committed
24
      return new _PosixUtils();
25 26 27
    }
  }

28 29 30 31 32 33 34 35
  OperatingSystemUtils._private();

  String get operatingSystem => Platform.operatingSystem;

  bool get isMacOS => operatingSystem == 'macos';
  bool get isWindows => operatingSystem == 'windows';
  bool get isLinux => operatingSystem == 'linux';

36 37
  /// Make the given file executable. This may be a no-op on some platforms.
  ProcessResult makeExecutable(File file);
38 39 40 41

  /// Return the path (with symlinks resolved) to the given executable, or `null`
  /// if `which` was not able to locate the binary.
  File which(String execName);
42 43

  void unzip(File file, Directory targetDirectory);
44 45
}

46 47 48
class _PosixUtils extends OperatingSystemUtils {
  _PosixUtils() : super._private();

49
  @override
50
  ProcessResult makeExecutable(File file) {
51
    return Process.runSync('chmod', <String>['a+x', file.path]);
52
  }
53 54 55

  /// Return the path (with symlinks resolved) to the given executable, or `null`
  /// if `which` was not able to locate the binary.
56
  @override
57 58 59 60 61 62 63
  File which(String execName) {
    ProcessResult result = Process.runSync('which', <String>[execName]);
    if (result.exitCode != 0)
      return null;
    String path = result.stdout.trim().split('\n').first.trim();
    return new File(new File(path).resolveSymbolicLinksSync());
  }
64 65 66 67 68 69

  // unzip -o -q zipfile -d dest
  @override
  void unzip(File file, Directory targetDirectory) {
    runSync(<String>['unzip', '-o', '-q', file.path, '-d', targetDirectory.path]);
  }
70 71
}

72 73 74
class _WindowsUtils extends OperatingSystemUtils {
  _WindowsUtils() : super._private();

75
  // This is a no-op.
76
  @override
77 78 79
  ProcessResult makeExecutable(File file) {
    return new ProcessResult(0, 0, null, null);
  }
80

81
  @override
82
  File which(String execName) {
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
    ProcessResult result = Process.runSync('where', <String>[execName]);
    if (result.exitCode != 0)
      return null;
    return new File(result.stdout.trim().split('\n').first.trim());
  }

  @override
  void unzip(File file, Directory targetDirectory) {
    Archive archive = new ZipDecoder().decodeBytes(file.readAsBytesSync());

    for (ArchiveFile archiveFile in archive.files) {
      // The archive package doesn't correctly set isFile.
      if (!archiveFile.isFile || archiveFile.name.endsWith('/'))
        continue;

      File destFile = new File(path.join(targetDirectory.path, archiveFile.name));
      if (!destFile.parent.existsSync())
        destFile.parent.createSync(recursive: true);
      destFile.writeAsBytesSync(archiveFile.content);
    }
103
  }
104
}
105 106 107 108 109 110 111

Future<int> findAvailablePort() async {
  ServerSocket socket = await ServerSocket.bind(InternetAddress.LOOPBACK_IP_V4, 0);
  int port = socket.port;
  await socket.close();
  return port;
}
Devon Carew's avatar
Devon Carew committed
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138

const int _kMaxSearchIterations = 5;

/// This method will attempt to return a port close to or the same as
/// [defaultPort]. Failing that, it will return any available port.
Future<int> findPreferredPort(int defaultPort, { int searchStep: 2 }) async {
  int iterationCount = 0;

  while (iterationCount < _kMaxSearchIterations) {
    int port = defaultPort + iterationCount * searchStep;
    if (await _isPortAvailable(port))
      return port;
    iterationCount++;
  }

  return findAvailablePort();
}

Future<bool> _isPortAvailable(int port) async {
  try {
    ServerSocket socket = await ServerSocket.bind(InternetAddress.LOOPBACK_IP_V4, port);
    await socket.close();
    return true;
  } catch (error) {
    return false;
  }
}