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
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// @dart = 2.8
import 'package:meta/meta.dart';
import 'package:package_config/package_config.dart';
import 'package:process/process.dart';
import '../artifacts.dart';
import '../base/common.dart';
import '../base/config.dart';
import '../base/file_system.dart';
import '../base/logger.dart';
import '../base/platform.dart';
import '../build_info.dart';
import '../bundle.dart';
import '../cache.dart';
import '../compile.dart';
import '../dart/language_version.dart';
import '../web/bootstrap.dart';
import '../web/memory_fs.dart';
import 'test_config.dart';
/// A web compiler for the test runner.
class WebTestCompiler {
WebTestCompiler({
@required FileSystem fileSystem,
@required Logger logger,
@required Artifacts artifacts,
@required Platform platform,
@required ProcessManager processManager,
@required Config config,
}) : _logger = logger,
_fileSystem = fileSystem,
_artifacts = artifacts,
_platform = platform,
_processManager = processManager,
_config = config;
final Logger _logger;
final FileSystem _fileSystem;
final Artifacts _artifacts;
final Platform _platform;
final ProcessManager _processManager;
final Config _config;
Future<WebMemoryFS> initialize({
@required Directory projectDirectory,
@required String testOutputDir,
@required List<String> testFiles,
@required BuildInfo buildInfo,
}) async {
LanguageVersion languageVersion = LanguageVersion(2, 8);
HostArtifact platformDillArtifact;
// TODO(jonahwilliams): to support autodetect this would need to partition the source code into a
// a sound and unsound set and perform separate compilations.
final List<String> extraFrontEndOptions = List<String>.of(buildInfo.extraFrontEndOptions ?? <String>[]);
if (buildInfo.nullSafetyMode == NullSafetyMode.unsound || buildInfo.nullSafetyMode == NullSafetyMode.autodetect) {
platformDillArtifact = HostArtifact.webPlatformKernelDill;
if (!extraFrontEndOptions.contains('--no-sound-null-safety')) {
extraFrontEndOptions.add('--no-sound-null-safety');
}
} else if (buildInfo.nullSafetyMode == NullSafetyMode.sound) {
platformDillArtifact = HostArtifact.webPlatformSoundKernelDill;
languageVersion = currentLanguageVersion(_fileSystem, Cache.flutterRoot);
if (!extraFrontEndOptions.contains('--sound-null-safety')) {
extraFrontEndOptions.add('--sound-null-safety');
}
}
final Directory outputDirectory = _fileSystem.directory(testOutputDir)
..createSync(recursive: true);
final List<File> generatedFiles = <File>[];
for (final String testFilePath in testFiles) {
final List<String> relativeTestSegments = _fileSystem.path.split(
_fileSystem.path.relative(testFilePath, from: projectDirectory.childDirectory('test').path));
final File generatedFile = _fileSystem.file(
_fileSystem.path.join(outputDirectory.path, '${relativeTestSegments.join('_')}.test.dart'));
generatedFile
..createSync(recursive: true)
..writeAsStringSync(generateTestEntrypoint(
relativeTestPath: relativeTestSegments.join('/'),
absolutePath: testFilePath,
testConfigPath: findTestConfigFile(_fileSystem.file(testFilePath), _logger)?.path,
languageVersion: languageVersion,
));
generatedFiles.add(generatedFile);
}
// Generate a fake main file that imports all tests to be executed. This will force
// each of them to be compiled.
final StringBuffer buffer = StringBuffer('// @dart=${languageVersion.major}.${languageVersion.minor}\n');
for (final File generatedFile in generatedFiles) {
buffer.writeln('import "${_fileSystem.path.basename(generatedFile.path)}";');
}
buffer.writeln('void main() {}');
_fileSystem.file(_fileSystem.path.join(outputDirectory.path, 'main.dart'))
..createSync()
..writeAsStringSync(buffer.toString());
final String cachedKernelPath = getDefaultCachedKernelPath(
trackWidgetCreation: buildInfo.trackWidgetCreation,
dartDefines: buildInfo.dartDefines,
extraFrontEndOptions: extraFrontEndOptions,
fileSystem: _fileSystem,
config: _config,
);
final ResidentCompiler residentCompiler = ResidentCompiler(
_artifacts.getHostArtifact(HostArtifact.flutterWebSdk).path,
buildMode: buildInfo.mode,
trackWidgetCreation: buildInfo.trackWidgetCreation,
fileSystemRoots: <String>[
projectDirectory.childDirectory('test').path,
testOutputDir,
],
// Override the filesystem scheme so that the frontend_server can find
// the generated entrypoint code.
fileSystemScheme: 'org-dartlang-app',
initializeFromDill: cachedKernelPath,
targetModel: TargetModel.dartdevc,
extraFrontEndOptions: extraFrontEndOptions,
platformDill: _artifacts
.getHostArtifact(platformDillArtifact)
.absolute.uri.toString(),
dartDefines: buildInfo.dartDefines,
librariesSpec: _artifacts.getHostArtifact(HostArtifact.flutterWebLibrariesJson).uri.toString(),
packagesPath: buildInfo.packagesPath,
artifacts: _artifacts,
processManager: _processManager,
logger: _logger,
platform: _platform,
fileSystem: _fileSystem,
);
final CompilerOutput output = await residentCompiler.recompile(
Uri.parse('org-dartlang-app:///main.dart'),
<Uri>[],
outputPath: outputDirectory.childFile('out').path,
packageConfig: buildInfo.packageConfig,
fs: _fileSystem,
projectRootPath: projectDirectory.absolute.path,
);
if (output.errorCount > 0) {
throwToolExit('Failed to compile');
}
// Cache the output kernel file to speed up subsequent compiles.
_fileSystem.file(cachedKernelPath).parent.createSync(recursive: true);
_fileSystem.file(output.outputFilename).copySync(cachedKernelPath);
final File codeFile = outputDirectory.childFile('${output.outputFilename}.sources');
final File manifestFile = outputDirectory.childFile('${output.outputFilename}.json');
final File sourcemapFile = outputDirectory.childFile('${output.outputFilename}.map');
final File metadataFile = outputDirectory.childFile('${output.outputFilename}.metadata');
return WebMemoryFS()
..write(codeFile, manifestFile, sourcemapFile, metadataFile);
}
}