-
Notifications
You must be signed in to change notification settings - Fork 1.6k
/
Copy pathconfiguration.dart
564 lines (484 loc) · 18.2 KB
/
configuration.dart
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
// 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:ffi';
import 'dart:io';
import 'package:native_assets_cli/native_assets_cli.dart' show CCompilerConfig;
import 'package:smith/configuration.dart';
import 'package:smith/smith.dart';
import 'compiler_configuration.dart';
import 'feature.dart';
import 'path.dart';
import 'repository.dart';
import 'runtime_configuration.dart';
import 'testing_servers.dart';
export 'package:smith/smith.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 TestConfiguration {
TestConfiguration(
{required this.configuration,
this.progress = Progress.compact,
this.selectors = const {},
this.build = false,
this.testList = const [],
this.repeat = 1,
this.batch = false,
this.copyCoreDumps = false,
this.rr = false,
this.isVerbose = false,
this.listTests = false,
this.listStatusFiles = false,
this.cleanExit = false,
this.silentFailures = false,
this.printTiming = false,
this.printReport = false,
this.reportFailures = false,
this.reportInJson = false,
this.resetBrowser = false,
this.writeDebugLog = false,
this.writeResults = false,
this.writeLogs = false,
this.drtPath,
this.chromePath,
this.safariPath,
this.firefoxPath,
this.dartPath,
this.dartPrecompiledPath,
this.genSnapshotPath,
this.taskCount = 1,
this.shardCount = 1,
this.shard = 1,
this.stepName,
this.testServerPort = 0,
this.testServerCrossOriginPort = 0,
this.testDriverErrorPort = 0,
this.localIP = '0.0.0.0',
this.keepGeneratedFiles = false,
this.sharedOptions = const [],
String? packages,
this.serviceResponseSizesDirectory,
this.suiteDirectory,
required this.outputDirectory,
required this.reproducingArguments,
this.fastTestsOnly = false,
this.printPassingStdout = false})
: packages = packages ??
Repository.uri
.resolve('.dart_tool/package_config.json')
.toFilePath();
final Map<String, RegExp?> selectors;
final Progress progress;
// The test configuration read from the -n option and the test matrix
// or else computed from the test options.
final Configuration configuration;
// Boolean flags.
final bool batch;
final bool build;
final bool copyCoreDumps;
final bool rr;
final bool fastTestsOnly;
final bool isVerbose;
final bool listTests;
final bool listStatusFiles;
final bool cleanExit;
final bool silentFailures;
final bool printTiming;
final bool printReport;
final bool reportFailures;
final bool reportInJson;
final bool resetBrowser;
final bool writeDebugLog;
final bool writeResults;
final bool writeLogs;
final bool printPassingStdout;
Architecture get architecture => configuration.architecture;
Compiler get compiler => configuration.compiler;
Mode get mode => configuration.mode;
Runtime get runtime => configuration.runtime;
System get system => configuration.system;
NnbdMode get nnbdMode => configuration.nnbdMode;
Sanitizer get sanitizer => configuration.sanitizer;
// Boolean getters
bool get hotReload => configuration.useHotReload;
bool get hotReloadRollback => configuration.useHotReloadRollback;
bool get isChecked => configuration.isChecked;
bool get isHostChecked => configuration.isHostChecked;
bool get isCsp => configuration.isCsp;
bool get isMinified => configuration.isMinified;
bool get isSimulator => architecture.isSimulator;
bool get useAnalyzerCfe => configuration.useAnalyzerCfe;
bool get useAnalyzerFastaParser => configuration.useAnalyzerFastaParser;
bool get useElf => configuration.useElf;
bool get useSdk => configuration.useSdk;
bool get enableAsserts => configuration.enableAsserts;
bool get useQemu => configuration.useQemu;
// Various file paths.
final String? drtPath;
final String? chromePath;
final String? safariPath;
final String? firefoxPath;
final String? dartPath;
final String? dartPrecompiledPath;
final String? genSnapshotPath;
final List<String>? testList;
final int taskCount;
final int shardCount;
final int shard;
final int repeat;
final String? stepName;
final int testServerPort;
final int testServerCrossOriginPort;
final int testDriverErrorPort;
final String localIP;
final bool keepGeneratedFiles;
/// Extra dart2js options passed to the testing script.
List<String> get dart2jsOptions => configuration.dart2jsOptions;
/// Extra dart2wasm options passed to the testing script.
List<String> get dart2wasmOptions => configuration.dart2wasmOptions;
/// Extra ddc options passed to the testing script.
List<String> get ddcOptions => configuration.ddcOptions;
/// Extra gen_kernel options passed to the testing script.
List<String> get genKernelOptions => configuration.genKernelOptions;
/// Extra VM options passed to the testing script.
List<String> get vmOptions => configuration.vmOptions;
/// The names of the experiments to enable while running tests.
///
/// A test may *require* an experiment to always be enabled by containing a
/// comment like:
///
/// // SharedOptions=--enable-experiment=extension-methods
///
/// Enabling an experiment here in the configuration allows running the same
/// test both with an experiment on and off.
List<String> get experiments => configuration.experiments;
/// Extra general options passed to the testing script.
final List<String> sharedOptions;
final String packages;
final String? serviceResponseSizesDirectory;
final String outputDirectory;
final String? suiteDirectory;
String get babel => configuration.babel;
String get builderTag => configuration.builderTag;
final List<String> reproducingArguments;
TestingServers? _servers;
TestingServers get servers {
return _servers ?? (throw StateError("Servers have not been started yet."));
}
/// Returns true if this configuration uses the new front end (fasta)
/// as the first stage of compilation.
bool get usesFasta {
var fastaCompilers = const [
Compiler.appJitk,
Compiler.ddc,
Compiler.dartk,
Compiler.dartkp,
Compiler.fasta,
Compiler.dart2js,
Compiler.dart2wasm,
];
return fastaCompilers.contains(compiler);
}
/// The base directory named for this configuration, like:
///
/// ReleaseX64
String? _configurationDirectory;
String get configurationDirectory {
// Lazy initialize and cache since it requires hitting the file system.
return _configurationDirectory ??= _calculateDirectory();
}
/// The build directory path for this configuration, like:
///
/// build/ReleaseX64
String get buildDirectory => system.outputDirectory + configurationDirectory;
int? _timeout;
// TODO(whesse): Put non-default timeouts explicitly in configs, not this.
/// Calculates a default timeout based on the compiler and runtime used,
/// and the mode, architecture, etc.
int get timeout {
if (_timeout == null) {
if (configuration.timeout > 0) {
_timeout = configuration.timeout;
} else {
var isReload = hotReload || hotReloadRollback;
var compilerMultiplier = compilerConfiguration.timeoutMultiplier;
var runtimeMultiplier = runtimeConfiguration.timeoutMultiplier(
mode: mode,
isChecked: isChecked,
isReload: isReload,
arch: architecture,
system: system);
_timeout = 60 * compilerMultiplier * runtimeMultiplier;
}
}
return _timeout!;
}
List<String> get standardOptions {
if (compiler != Compiler.dart2js) {
return const ["--ignore-unrecognized-flags"];
}
var args = ['--test-mode'];
if (isMinified) args.add("--minify");
if (isCsp) args.add("--csp");
if (enableAsserts) args.add("--enable-asserts");
return args;
}
late final String? windowsSdkPath = () {
if (!Platform.isWindows) {
throw StateError(
"Should not use windowsSdkPath when not running on Windows.");
}
// When running tests on Windows, use cdb from depot_tools to dump
// stack traces of tests timing out.
try {
var path = Path("build/win_toolchain.json").toNativePath();
var text = File(path).readAsStringSync();
return jsonDecode(text)['win_sdk'] as String;
} catch (_) {
// Ignore errors here. If win_sdk is not found, stack trace dumping
// for timeouts won't work.
}
}();
late final Map<String, String> nativeCompilerEnvironmentVariables = () {
String unparseKey(String key) => key.replaceAll('.', '__').toUpperCase();
final arKey = unparseKey(CCompilerConfig.arConfigKeyFull);
final ccKey = unparseKey(CCompilerConfig.ccConfigKeyFull);
final ldKey = unparseKey(CCompilerConfig.ldConfigKeyFull);
final envScriptKey = unparseKey(CCompilerConfig.envScriptConfigKeyFull);
final envScriptArgsKey =
unparseKey(CCompilerConfig.envScriptArgsConfigKeyFull);
if (Platform.isWindows) {
// Use MSVC from Depot Tools instead. When using clang from DEPS, we still
// need to pass the right INCLUDE / LIB environment variables. So we might
// as well use the MSVC instead.
final windowsSdkPath_ = windowsSdkPath;
if (windowsSdkPath_ == null) {
return <String, String>{};
}
final windowsSdk = Uri.directory(windowsSdkPath_);
final vsPath = windowsSdk.resolve('../../');
final msvcPaths = vsPath.resolve('VC/Tools/MSVC/');
final msvcPath = Directory.fromUri(msvcPaths)
.listSync()
.firstWhere((element) => element.path != '.' && element.path != '..')
.uri;
const targetFolderName = {
Abi.windowsX64: 'x64',
Abi.windowsIA32: 'ia32',
Abi.windowsArm64: 'arm64',
};
const envScriptArgument = {
Abi.windowsX64: '/x64',
Abi.windowsIA32: '/x86',
Abi.windowsArm64: '/arm64',
};
final binDir =
msvcPath.resolve('bin/Hostx64/${targetFolderName[Abi.current()]!}/');
final toolchainEnvScript = windowsSdk.resolve('bin/SetEnv.cmd');
return {
arKey: binDir.resolve('lib.exe').toFilePath(),
ccKey: binDir.resolve('cl.exe').toFilePath(),
ldKey: binDir.resolve('link.exe').toFilePath(),
envScriptKey: toolchainEnvScript.toFilePath(),
envScriptArgsKey: envScriptArgument[Abi.current()]!,
};
}
if (Platform.isMacOS) {
// Use XCode instead, it has the right sysroot by default.
return <String, String>{};
}
assert(Platform.isLinux);
// Keep consistent with DEPS.
const clangHostFolderName = {
Abi.linuxArm64: 'linux-arm64',
Abi.linuxX64: 'linux-x64',
};
final hostFolderName = clangHostFolderName[Abi.current()]!;
final clangBin =
Directory.current.uri.resolve('buildtools/$hostFolderName/clang/bin/');
return {
arKey: clangBin.resolve('llvm-ar').toFilePath(),
ccKey: clangBin.resolve('clang').toFilePath(),
ldKey: clangBin.resolve('ld.lld').toFilePath(),
};
}();
/// Gets the local file path to the browser executable for this configuration.
late final String browserLocation = () {
// If the user has explicitly configured a browser path, use it.
String? location;
switch (runtime) {
case Runtime.chrome:
location = chromePath;
break;
case Runtime.firefox:
location = firefoxPath;
break;
}
if (location != null) return location;
const locations = {
Runtime.firefox: {
System.win: 'C:\\Program Files (x86)\\Mozilla Firefox\\firefox.exe',
System.linux: '/usr/bin/firefox',
System.mac: '/Applications/Firefox.app/Contents/MacOS/firefox'
},
Runtime.chrome: {
System.win:
'C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe',
System.mac:
'/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
System.linux: '/usr/bin/google-chrome'
},
Runtime.ie9: {
System.win: 'C:\\Program Files\\Internet Explorer\\iexplore.exe'
},
Runtime.ie10: {
System.win: 'C:\\Program Files\\Internet Explorer\\iexplore.exe'
},
Runtime.ie11: {
System.win: '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 ??= RuntimeConfiguration(this);
CompilerConfiguration? _compilerConfiguration;
CompilerConfiguration get compilerConfiguration =>
_compilerConfiguration ??= CompilerConfiguration(this);
/// The set of [Feature]s supported by this configuration.
late final Set<Feature> supportedFeatures = compiler == Compiler.dart2analyzer
// The analyzer should parse all tests.
? {...Feature.all}
: {
// TODO(rnystrom): Define more features for things like "dart:io", separate
// int/double representation, etc.
if (NnbdMode.legacy == configuration.nnbdMode)
Feature.nnbdLegacy
else
Feature.nnbd,
if (NnbdMode.weak == configuration.nnbdMode) Feature.nnbdWeak,
if (NnbdMode.strong == configuration.nnbdMode) Feature.nnbdStrong,
};
/// 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' &&
!listStatusFiles &&
!listTests) {
print("Warning: cannot run Internet Explorer on non-Windows operating"
" system.");
isValid = false;
}
if (architecture == Architecture.ia32 && compiler == Compiler.dartkp) {
print("Warning: IA32 does not support AOT mode.");
isValid = false;
}
if (system == System.android &&
!(architecture == Architecture.ia32 ||
architecture == Architecture.x64 ||
architecture == Architecture.arm ||
architecture == Architecture.arm_x64 ||
architecture == Architecture.arm64 ||
architecture == Architecture.arm64c ||
architecture == Architecture.riscv64)) {
print("Warning: Android only supports the following architectures: "
"ia32/x64/x64c/arm/arm64/arm64c/arm_x64/riscv64.");
isValid = false;
}
if (shard < 1 || shard > shardCount) {
print("Error: shard index is $shard out of $shardCount shards");
isValid = false;
}
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 = TestingServers(buildDirectory, isCsp, runtime, null, packages);
var future = servers.startServers(localIP,
port: testServerPort, crossOriginPort: testServerCrossOriginPort);
if (isVerbose) {
future = future.then((_) {
print('Started HttpServers: ${servers.commandLine}');
});
}
return future;
}
void stopServers() {
_servers?.stopServers();
}
/// Returns the correct configuration directory (the last component of the
/// output directory path) for regular dart checkouts.
///
/// 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 result =
mode.name.substring(0, 1).toUpperCase() + mode.name.substring(1);
if (system == System.android) result += "Android";
if (system == System.fuchsia) result += "Fuchsia";
if (sanitizer != Sanitizer.none) {
result += sanitizer.name.toUpperCase();
}
var arch = architecture.name.toUpperCase();
var normal = '$result$arch';
var cross = '${result}X$arch';
var outDir = system.outputDirectory;
var normalDir = Directory(Path('$outDir$normal').toNativePath());
var crossDir = Directory(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.";
}
return crossDir.existsSync() ? cross : normal;
}
}
class Progress {
static const compact = Progress._('compact');
static const color = Progress._('color');
static const line = Progress._('line');
static const verbose = Progress._('verbose');
static const silent = Progress._('silent');
static const status = Progress._('status');
static const buildbot = Progress._('buildbot');
static final List<String> names = _all.keys.toList();
static final _all = Map<String, Progress>.fromIterable(
[compact, color, line, verbose, silent, status, buildbot],
key: (progress) => (progress as Progress).name);
static Progress find(String name) {
var progress = _all[name];
if (progress != null) return progress;
throw ArgumentError('Unknown progress type "$name".');
}
final String name;
const Progress._(this.name);
@override
String toString() => "Progress($name)";
}