Index: tools/testing/dart/configuration.dart |
diff --git a/tools/testing/dart/configuration.dart b/tools/testing/dart/configuration.dart |
new file mode 100644 |
index 0000000000000000000000000000000000000000..2b41503b042d9955ad9f6503ff84fdc60bc64071 |
--- /dev/null |
+++ b/tools/testing/dart/configuration.dart |
@@ -0,0 +1,741 @@ |
+// 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 'dart:async'; |
+import 'dart:convert'; |
+import 'dart:io'; |
+ |
+import 'compiler_configuration.dart'; |
+import 'http_server.dart'; |
+import 'path.dart'; |
+import 'runtime_configuration.dart'; |
+import 'test_suite.dart'; |
+ |
+/// All of the contextual information to determine how a test suite should be |
+/// run. |
+/// |
+/// Includes the compiler used to compile the code, the runtime the result is |
+/// executed on, etc. |
+class Configuration { |
+ Configuration( |
+ {this.architecture, |
+ this.compiler, |
+ this.mode, |
+ this.progress, |
+ this.runtime, |
+ this.system, |
+ this.selectors, |
+ this.appendLogs, |
+ this.batch, |
+ this.batchDart2JS, |
+ this.copyCoreDumps, |
+ this.hotReload, |
+ this.hotReloadRollback, |
+ this.isChecked, |
+ this.isStrong, |
+ this.isHostChecked, |
+ this.isCsp, |
+ this.isMinified, |
+ this.isVerbose, |
+ this.listTests, |
+ this.printTiming, |
+ this.printReport, |
+ this.reportInJson, |
+ this.resetBrowser, |
+ this.skipCompilation, |
+ this.useBlobs, |
+ this.useSdk, |
+ this.useFastStartup, |
+ this.useDart2JSWithKernel, |
+ this.writeDebugLog, |
+ this.writeTestOutcomeLog, |
+ this.drtPath, |
+ this.dartiumPath, |
+ this.chromePath, |
+ this.safariPath, |
+ this.firefoxPath, |
+ this.dartPath, |
+ this.dartPrecompiledPath, |
+ this.flutterPath, |
+ this.recordingPath, |
+ this.replayPath, |
+ this.taskCount, |
+ int timeout, |
+ this.shardCount, |
+ this.shard, |
+ this.stepName, |
+ this.testServerPort, |
+ this.testServerCrossOriginPort, |
+ this.testDriverErrorPort, |
+ this.localIP, |
+ this.dart2jsOptions, |
+ this.vmOptions, |
+ String packages, |
+ this.packageRoot, |
+ this.suiteDirectory, |
+ this.builderTag, |
+ this.reproducingArguments}) |
+ : _packages = packages, |
+ _timeout = timeout; |
+ |
+ final Architecture architecture; |
+ final Compiler compiler; |
+ final Mode mode; |
+ final Progress progress; |
+ final Runtime runtime; |
+ final System system; |
+ |
+ final Map<String, RegExp> selectors; |
+ |
+ // Boolean flags. |
+ |
+ final bool appendLogs; |
+ final bool batch; |
+ final bool batchDart2JS; |
+ final bool copyCoreDumps; |
+ final bool hotReload; |
+ final bool hotReloadRollback; |
+ final bool isChecked; |
+ final bool isStrong; |
+ final bool isHostChecked; |
+ final bool isCsp; |
+ final bool isMinified; |
+ final bool isVerbose; |
+ final bool listTests; |
+ final bool printTiming; |
+ final bool printReport; |
+ final bool reportInJson; |
+ final bool resetBrowser; |
+ final bool skipCompilation; |
+ final bool useBlobs; |
+ final bool useSdk; |
+ final bool useFastStartup; |
+ final bool useDart2JSWithKernel; |
+ final bool writeDebugLog; |
+ final bool writeTestOutcomeLog; |
+ |
+ // Various file paths. |
+ |
+ final String drtPath; |
+ final String dartiumPath; |
+ final String chromePath; |
+ final String safariPath; |
+ final String firefoxPath; |
+ final String dartPath; |
+ final String dartPrecompiledPath; |
+ final String flutterPath; |
+ final String recordingPath; |
+ final String replayPath; |
+ |
+ final int taskCount; |
+ final int shardCount; |
+ final int shard; |
+ final String stepName; |
+ |
+ final int testServerPort; |
+ final int testServerCrossOriginPort; |
+ final int testDriverErrorPort; |
+ final String localIP; |
+ |
+ /// Extra dart2js options passed to the testing script. |
+ final List<String> dart2jsOptions; |
+ |
+ /// Extra VM options passed to the testing script. |
+ final List<String> vmOptions; |
+ |
+ String _packages; |
+ String get packages { |
+ // If the .packages file path wasn't given, find it. |
+ if (packageRoot == null && _packages == null) { |
+ _packages = TestUtils.dartDirUri.resolve('.packages').toFilePath(); |
+ } |
+ |
+ return _packages; |
+ } |
+ |
+ final String packageRoot; |
+ final String suiteDirectory; |
+ final String builderTag; |
+ final List<String> reproducingArguments; |
+ |
+ TestingServers _servers; |
+ TestingServers get servers { |
+ if (_servers == null) { |
+ throw new StateError("Servers have not been started yet."); |
+ } |
+ return _servers; |
+ } |
+ |
+ /// The base directory named for this configuration, like: |
+ /// |
+ /// none_vm_release_x64 |
+ String _configurationDirectory; |
+ String get configurationDirectory { |
+ // Lazy initialize and cache since it requires hitting the file system. |
+ if (_configurationDirectory == null) { |
+ _configurationDirectory = _calculateDirectory(); |
+ } |
+ |
+ return _configurationDirectory; |
+ } |
+ |
+ /// The build directory path for this configuration, like: |
+ /// |
+ /// build/none_vm_release_x64 |
+ String get buildDirectory => system.outputDirectory + configurationDirectory; |
+ |
+ int _timeout; |
+ int get timeout { |
+ if (_timeout == null) { |
+ var isReload = hotReload || hotReloadRollback; |
+ |
+ var compilerMulitiplier = compilerConfiguration.timeoutMultiplier; |
+ var runtimeMultiplier = runtimeConfiguration.timeoutMultiplier( |
+ mode: mode, |
+ isChecked: isChecked, |
+ isReload: isReload, |
+ arch: architecture); |
+ |
+ _timeout = 60 * compilerMulitiplier * runtimeMultiplier; |
+ } |
+ |
+ return _timeout; |
+ } |
+ |
+ List<String> get standardOptions { |
+ if (compiler != Compiler.dart2js) { |
+ return const ["--ignore-unrecognized-flags"]; |
+ } |
+ |
+ var args = ['--generate-code-with-compile-time-errors', '--test-mode']; |
+ if (isChecked) args.add('--enable-checked-mode'); |
+ |
+ if (!runtime.isBrowser) { |
+ args.add("--allow-mock-compilation"); |
+ args.add("--categories=all"); |
+ } |
+ |
+ if (isMinified) args.add("--minify"); |
+ if (isCsp) args.add("--csp"); |
+ if (useFastStartup) args.add("--fast-startup"); |
+ if (useDart2JSWithKernel) args.add("--use-kernel"); |
+ return args; |
+ } |
+ |
+ String _windowsSdkPath; |
+ String get windowsSdkPath { |
+ if (!Platform.isWindows) { |
+ throw new StateError( |
+ "Should not use windowsSdkPath when not running on Windows."); |
+ } |
+ |
+ if (_windowsSdkPath == null) { |
+ // When running tests on Windows, use cdb from depot_tools to dump |
+ // stack traces of tests timing out. |
+ try { |
+ var path = new Path("build/win_toolchain.json").toNativePath(); |
+ var text = new File(path).readAsStringSync(); |
+ _windowsSdkPath = JSON.decode(text)['win_sdk'] as String; |
+ } on dynamic { |
+ // Ignore errors here. If win_sdk is not found, stack trace dumping |
+ // for timeouts won't work. |
+ } |
+ } |
+ |
+ return _windowsSdkPath; |
+ } |
+ |
+ /// Gets the local file path to the browser executable for this configuration. |
+ String get browserLocation { |
+ // If the user has explicitly configured a browser path, use it. |
+ String location; |
+ switch (runtime) { |
+ case Runtime.chrome: |
+ location = chromePath; |
+ break; |
+ case Runtime.dartium: |
+ location = dartiumPath; |
+ break; |
+ case Runtime.drt: |
+ location = drtPath; |
+ break; |
+ case Runtime.firefox: |
+ location = firefoxPath; |
+ break; |
+ case Runtime.flutter: |
+ location = flutterPath; |
+ break; |
+ case Runtime.safari: |
+ location = safariPath; |
+ break; |
+ } |
+ |
+ if (location != null) return location; |
+ |
+ const locations = const { |
+ Runtime.firefox: const { |
+ System.windows: 'C:\\Program Files (x86)\\Mozilla Firefox\\firefox.exe', |
+ System.linux: 'firefox', |
+ System.macos: '/Applications/Firefox.app/Contents/MacOS/firefox' |
+ }, |
+ Runtime.chrome: const { |
+ System.windows: |
+ 'C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe', |
+ System.macos: |
+ '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome', |
+ System.linux: 'google-chrome' |
+ }, |
+ Runtime.dartium: const { |
+ System.windows: 'client\\tests\\dartium\\chrome.exe', |
+ System.macos: |
+ 'client/tests/dartium/Chromium.app/Contents/MacOS/Chromium', |
+ System.linux: 'client/tests/dartium/chrome' |
+ }, |
+ Runtime.safari: const { |
+ System.macos: '/Applications/Safari.app/Contents/MacOS/Safari' |
+ }, |
+ Runtime.safariMobileSim: const { |
+ System.macos: '/Applications/Xcode.app/Contents/Developer/Platforms/' |
+ 'iPhoneSimulator.platform/Developer/Applications/' |
+ 'iPhone Simulator.app/Contents/MacOS/iPhone Simulator' |
+ }, |
+ Runtime.ie9: const { |
+ System.windows: 'C:\\Program Files\\Internet Explorer\\iexplore.exe' |
+ }, |
+ Runtime.ie10: const { |
+ System.windows: 'C:\\Program Files\\Internet Explorer\\iexplore.exe' |
+ }, |
+ Runtime.ie11: const { |
+ System.windows: 'C:\\Program Files\\Internet Explorer\\iexplore.exe' |
+ } |
+ }; |
+ |
+ location = locations[runtime][System.find(Platform.operatingSystem)]; |
+ |
+ if (location == null) { |
+ throw "${runtime.name} is not supported on ${Platform.operatingSystem}"; |
+ } |
+ |
+ return location; |
+ } |
+ |
+ RuntimeConfiguration _runtimeConfiguration; |
+ RuntimeConfiguration get runtimeConfiguration => |
+ _runtimeConfiguration ??= new RuntimeConfiguration(this); |
+ |
+ CompilerConfiguration _compilerConfiguration; |
+ CompilerConfiguration get compilerConfiguration => |
+ _compilerConfiguration ??= new CompilerConfiguration(this); |
+ |
+ /// Determines if this configuration has a compatible compiler and runtime |
+ /// and other valid fields. |
+ /// |
+ /// Prints a warning if the configuration isn't valid. Returns whether or not |
+ /// it is. |
+ bool validate() { |
+ var isValid = true; |
+ var validRuntimes = compiler.supportedRuntimes; |
+ |
+ if (!validRuntimes.contains(runtime)) { |
+ print("Warning: combination of compiler '${compiler.name}' and " |
+ "runtime '${runtime.name}' is invalid. Skipping this combination."); |
+ isValid = false; |
+ } |
+ |
+ if (runtime.isIE && Platform.operatingSystem != 'windows') { |
+ print("Warning: cannot run Internet Explorer on non-Windows operating" |
+ " system."); |
+ isValid = false; |
+ } |
+ |
+ if (shard < 1 || shard > shardCount) { |
+ print("Error: shard index is $shard out of $shardCount shards"); |
+ isValid = false; |
+ } |
+ |
+ if (runtime == Runtime.flutter && flutterPath == null) { |
+ print("-rflutter requires the flutter engine executable to " |
+ "be specified using --flutter"); |
+ isValid = false; |
+ } |
+ |
+ if (runtime == Runtime.flutter && architecture != Architecture.x64) { |
+ isValid = false; |
+ print("-rflutter is applicable only for --arch=x64"); |
+ } |
+ |
+ return isValid; |
+ } |
+ |
+ /// Starts global HTTP servers that serve the entire dart repo. |
+ /// |
+ /// The HTTP server is available on `window.location.port`, and a second |
+ /// server for cross-domain tests can be found by calling |
+ /// `getCrossOriginPortNumber()`. |
+ Future startServers() { |
+ _servers = new TestingServers( |
+ buildDirectory, isCsp, runtime, null, packageRoot, packages); |
+ var future = servers.startServers(localIP, |
+ port: testServerPort, crossOriginPort: testServerCrossOriginPort); |
+ |
+ if (isVerbose) { |
+ future = future.then((_) { |
+ print('Started HttpServers: ${servers.httpServerCommandLine()}'); |
+ }); |
+ } |
+ |
+ return future; |
+ } |
+ |
+ void stopServers() { |
+ if (_servers != null) _servers.stopServers(); |
+ } |
+ |
+ /// Returns the correct configuration directory (the last component of the |
+ /// output directory path) for regular dart checkouts. |
+ /// |
+ /// Dartium checkouts use the `--build-directory` option to pass in the |
+ /// correct build directory explicitly. We allow our code to have been cross |
+ /// compiled, i.e., that there is an X in front of the arch. We don't allow |
+ /// both a cross compiled and a normal version to be present (except if you |
+ /// specifically pass in the build_directory). |
+ String _calculateDirectory() { |
+ // Capitalize the mode name. |
+ var modeName = |
+ mode.name.substring(0, 1).toUpperCase() + mode.name.substring(1); |
+ |
+ var os = ''; |
+ if (system == System.android) os = "Android"; |
+ |
+ var arch = architecture.name.toUpperCase(); |
+ var normal = '$modeName$os$arch'; |
+ var cross = '$modeName${os}X$arch'; |
+ var outDir = system.outputDirectory; |
+ var normalDir = new Directory(new Path('$outDir$normal').toNativePath()); |
+ var crossDir = new Directory(new Path('$outDir$cross').toNativePath()); |
+ |
+ if (normalDir.existsSync() && crossDir.existsSync()) { |
+ throw "You can't have both $normalDir and $crossDir. We don't know which" |
+ " binary to use."; |
+ } |
+ |
+ if (crossDir.existsSync()) return cross; |
+ |
+ return normal; |
+ } |
+} |
+ |
+class Architecture { |
+ static const ia32 = const Architecture._('ia32'); |
+ static const x64 = const Architecture._('x64'); |
+ static const arm = const Architecture._('arm'); |
+ static const armv6 = const Architecture._('armv6'); |
+ static const armv5te = const Architecture._('armv5te'); |
+ static const arm64 = const Architecture._('arm64'); |
+ static const simarm = const Architecture._('simarm'); |
+ static const simarmv6 = const Architecture._('simarmv6'); |
+ static const simarmv5te = const Architecture._('simarmv5te'); |
+ static const simarm64 = const Architecture._('simarm64'); |
+ static const mips = const Architecture._('mips'); |
+ static const simmips = const Architecture._('simmips'); |
+ static const simdbc = const Architecture._('simdbc'); |
+ static const simdbc64 = const Architecture._('simdbc64'); |
+ |
+ static final List<String> names = _all.keys.toList(); |
+ |
+ static final _all = new Map<String, Architecture>.fromIterable([ |
+ ia32, |
+ x64, |
+ arm, |
+ armv6, |
+ armv5te, |
+ arm64, |
+ simarm, |
+ simarmv6, |
+ simarmv5te, |
+ simarm64, |
+ mips, |
+ simmips, |
+ simdbc, |
+ simdbc64 |
+ ], key: (Architecture architecture) => architecture.name); |
+ |
+ static Architecture find(String name) { |
+ var architecture = _all[name]; |
+ if (architecture != null) return architecture; |
+ |
+ throw new ArgumentError('Unknown architecture "$name".'); |
+ } |
+ |
+ final String name; |
+ |
+ const Architecture._(this.name); |
+ |
+ String toString() => "Architecture($name)"; |
+} |
+ |
+class Compiler { |
+ static const none = const Compiler._('none'); |
+ static const precompiler = const Compiler._('precompiler'); |
+ static const dart2js = const Compiler._('dart2js'); |
+ static const dart2analyzer = const Compiler._('dart2analyzer'); |
+ static const appJit = const Compiler._('app_jit'); |
+ static const dartk = const Compiler._('dartk'); |
+ static const dartkp = const Compiler._('dartkp'); |
+ |
+ static final List<String> names = _all.keys.toList(); |
+ |
+ static final _all = new Map<String, Compiler>.fromIterable( |
+ [none, precompiler, dart2js, dart2analyzer, appJit, dartk, dartkp], |
+ key: (Compiler compiler) => compiler.name); |
+ |
+ static Compiler find(String name) { |
+ var compiler = _all[name]; |
+ if (compiler != null) return compiler; |
+ |
+ throw new ArgumentError('Unknown compiler "$name".'); |
+ } |
+ |
+ final String name; |
+ |
+ const Compiler._(this.name); |
+ |
+ /// Gets the runtimes this compiler can target. |
+ List<Runtime> get supportedRuntimes { |
+ switch (this) { |
+ case Compiler.dart2js: |
+ // Note: by adding 'none' as a configuration, if the user |
+ // runs test.py -c dart2js -r drt,none the dart2js_none and |
+ // dart2js_drt will be duplicating work. If later we don't need 'none' |
+ // with dart2js, we should remove it from here. |
+ return const [ |
+ Runtime.d8, |
+ Runtime.jsshell, |
+ Runtime.drt, |
+ Runtime.none, |
+ Runtime.dartium, |
+ Runtime.firefox, |
+ Runtime.chrome, |
+ Runtime.safari, |
+ Runtime.ie9, |
+ Runtime.ie10, |
+ Runtime.ie11, |
+ Runtime.opera, |
+ Runtime.chromeOnAndroid, |
+ Runtime.safariMobileSim |
+ ]; |
+ |
+ case Compiler.dart2analyzer: |
+ return const [Runtime.none]; |
+ case Compiler.appJit: |
+ case Compiler.dartk: |
+ return const [Runtime.vm, Runtime.selfCheck, Runtime.none]; |
+ case Compiler.precompiler: |
+ case Compiler.dartkp: |
+ return const [Runtime.dartPrecompiled]; |
+ case Compiler.none: |
+ return const [ |
+ Runtime.vm, |
+ Runtime.flutter, |
+ Runtime.drt, |
+ Runtime.dartium, |
+ Runtime.contentShellOnAndroid, |
+ Runtime.dartiumOnAndroid |
+ ]; |
+ } |
+ |
+ throw "unreachable"; |
+ } |
+ |
+ String toString() => "Compiler($name)"; |
+} |
+ |
+class Mode { |
+ static const debug = const Mode._('debug'); |
+ static const product = const Mode._('product'); |
+ static const release = const Mode._('release'); |
+ |
+ static final List<String> names = _all.keys.toList(); |
+ |
+ static final _all = new Map<String, Mode>.fromIterable( |
+ [debug, product, release], |
+ key: (Mode mode) => mode.name); |
+ |
+ static Mode find(String name) { |
+ var mode = _all[name]; |
+ if (mode != null) return mode; |
+ |
+ throw new ArgumentError('Unknown mode "$name".'); |
+ } |
+ |
+ final String name; |
+ |
+ const Mode._(this.name); |
+ |
+ bool get isDebug => this == debug; |
+ |
+ String toString() => "Mode($name)"; |
+} |
+ |
+class Progress { |
+ static const compact = const Progress._('compact'); |
+ static const color = const Progress._('color'); |
+ static const line = const Progress._('line'); |
+ static const verbose = const Progress._('verbose'); |
+ static const silent = const Progress._('silent'); |
+ static const status = const Progress._('status'); |
+ static const buildbot = const Progress._('buildbot'); |
+ static const diff = const Progress._('diff'); |
+ |
+ static final List<String> names = _all.keys.toList(); |
+ |
+ static final _all = new Map<String, Progress>.fromIterable( |
+ [compact, color, line, verbose, silent, status, buildbot, diff], |
+ key: (Progress progress) => progress.name); |
+ |
+ static Progress find(String name) { |
+ var progress = _all[name]; |
+ if (progress != null) return progress; |
+ |
+ throw new ArgumentError('Unknown progress type "$name".'); |
+ } |
+ |
+ final String name; |
+ |
+ const Progress._(this.name); |
+ |
+ String toString() => "Progress($name)"; |
+} |
+ |
+class Runtime { |
+ static const vm = const Runtime._('vm'); |
+ static const flutter = const Runtime._('flutter'); |
+ static const dartPrecompiled = const Runtime._('dart_precompiled'); |
+ static const d8 = const Runtime._('d8'); |
+ static const jsshell = const Runtime._('jsshell'); |
+ static const drt = const Runtime._('drt'); |
+ static const dartium = const Runtime._('dartium'); |
+ static const firefox = const Runtime._('firefox'); |
+ static const chrome = const Runtime._('chrome'); |
+ static const safari = const Runtime._('safari'); |
+ static const ie9 = const Runtime._('ie9'); |
+ static const ie10 = const Runtime._('ie10'); |
+ static const ie11 = const Runtime._('ie11'); |
+ static const opera = const Runtime._('opera'); |
+ static const chromeOnAndroid = const Runtime._('chromeOnAndroid'); |
+ static const safariMobileSim = const Runtime._('safarimobilesim'); |
+ static const contentShellOnAndroid = const Runtime._('ContentShellOnAndroid'); |
+ static const dartiumOnAndroid = const Runtime._('DartiumOnAndroid'); |
+ static const selfCheck = const Runtime._('self_check'); |
+ static const none = const Runtime._('none'); |
+ |
+ static final List<String> names = _all.keys.toList()..add("ff"); |
+ |
+ static final _all = new Map<String, Runtime>.fromIterable([ |
+ vm, |
+ flutter, |
+ dartPrecompiled, |
+ d8, |
+ jsshell, |
+ drt, |
+ dartium, |
+ firefox, |
+ chrome, |
+ safari, |
+ ie9, |
+ ie10, |
+ ie11, |
+ opera, |
+ chromeOnAndroid, |
+ safariMobileSim, |
+ contentShellOnAndroid, |
+ dartiumOnAndroid, |
+ selfCheck, |
+ none |
+ ], key: (Runtime runtime) => runtime.name); |
+ |
+ static Runtime find(String name) { |
+ // Allow "ff" as a synonym for Firefox. |
+ if (name == "ff") return firefox; |
+ |
+ var runtime = _all[name]; |
+ if (runtime != null) return runtime; |
+ |
+ throw new ArgumentError('Unknown runtime "$name".'); |
+ } |
+ |
+ final String name; |
+ |
+ const Runtime._(this.name); |
+ |
+ bool get isBrowser => const [ |
+ drt, |
+ dartium, |
+ ie9, |
+ ie10, |
+ ie11, |
+ safari, |
+ opera, |
+ chrome, |
+ firefox, |
+ chromeOnAndroid, |
+ safariMobileSim, |
+ contentShellOnAndroid, |
+ dartiumOnAndroid, |
+ ].contains(this); |
+ |
+ bool get isIE => name.startsWith("ie"); |
+ bool get isSafari => name.startsWith("safari"); |
+ |
+ /// Whether this runtime is a command-line JavaScript environment. |
+ bool get isJSCommandLine => const [d8, jsshell].contains(this); |
+ |
+ /// If the runtime doesn't support `Window.open`, we use iframes instead. |
+ bool get requiresIFrame => !const [ie11, ie10].contains(this); |
+ |
+ String toString() => "Runtime($name)"; |
+} |
+ |
+class System { |
+ static const android = const System._('android'); |
+ static const fuchsia = const System._('fuchsia'); |
+ static const linux = const System._('linux'); |
+ static const macos = const System._('macos'); |
+ static const windows = const System._('windows'); |
+ |
+ static final List<String> names = _all.keys.toList(); |
+ |
+ static final _all = new Map<String, System>.fromIterable( |
+ [android, fuchsia, linux, macos, windows], |
+ key: (System system) => system.name); |
+ |
+ static System find(String name) { |
+ var system = _all[name]; |
+ if (system != null) return system; |
+ |
+ throw new ArgumentError('Unknown operating system "$name".'); |
+ } |
+ |
+ final String name; |
+ |
+ const System._(this.name); |
+ |
+ /// The root directory name for build outputs on this system. |
+ String get outputDirectory { |
+ switch (this) { |
+ case android: |
+ case fuchsia: |
+ case linux: |
+ case windows: |
+ return 'out/'; |
+ |
+ case macos: |
+ return 'xcodebuild/'; |
+ } |
+ |
+ throw "unreachable"; |
+ } |
+ |
+ String toString() => "System($name)"; |
+} |