160 lines
6.5 KiB
Dart
160 lines
6.5 KiB
Dart
// 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.
|
|
|
|
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);
|
|
late final String platformDillName;
|
|
|
|
// TODO(zanderso): to support autodetect this would need to partition the source code into
|
|
// a sound and unsound set and perform separate compilations
|
|
final List<String> extraFrontEndOptions = List<String>.of(buildInfo.extraFrontEndOptions);
|
|
if (buildInfo.nullSafetyMode == NullSafetyMode.unsound || buildInfo.nullSafetyMode == NullSafetyMode.autodetect) {
|
|
platformDillName = 'ddc_outline.dill';
|
|
if (!extraFrontEndOptions.contains('--no-sound-null-safety')) {
|
|
extraFrontEndOptions.add('--no-sound-null-safety');
|
|
}
|
|
} else if (buildInfo.nullSafetyMode == NullSafetyMode.sound) {
|
|
languageVersion = currentLanguageVersion(_fileSystem, Cache.flutterRoot!);
|
|
platformDillName = 'ddc_outline_sound.dill';
|
|
if (!extraFrontEndOptions.contains('--sound-null-safety')) {
|
|
extraFrontEndOptions.add('--sound-null-safety');
|
|
}
|
|
}
|
|
|
|
final String platformDillPath = _fileSystem.path.join(
|
|
getWebPlatformBinariesDirectory(_artifacts, buildInfo.webRenderer).path,
|
|
platformDillName
|
|
);
|
|
|
|
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: _fileSystem.file(platformDillPath).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 == null || 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);
|
|
}
|
|
}
|