c66f91be23
Change-Id: I7cdfb0000996a711bdee9b1618c20a21bbdda814 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/427620 Commit-Queue: Nate Biggs <natebiggs@google.com> Reviewed-by: Nate Biggs <natebiggs@google.com> Auto-Submit: Mayank Patke <fishythefish@google.com>
216 lines
7.1 KiB
Dart
216 lines
7.1 KiB
Dart
// Copyright (c) 2024, 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.
|
|
|
|
/// Implementation of the [TargetExecutor] for DDC.
|
|
library;
|
|
|
|
import 'dart:io';
|
|
import '../common/testing.dart' as helper;
|
|
|
|
import 'model.dart';
|
|
import 'util.dart';
|
|
import 'target.dart';
|
|
|
|
/// Logic to build and execute dynamic modules in DDC.
|
|
///
|
|
/// In particular:
|
|
/// * The initial app is built as a regular DDC target, except that
|
|
/// a dynamic interface is used to validate that the public API matches
|
|
/// real declarations.
|
|
/// * For dynamic modules, DDC validates that the module only accesses what's
|
|
/// allowed by the dynamic interface.
|
|
/// * For dynamic modules, DDC also produces a slighly different output
|
|
/// to implement library isolation.
|
|
/// * Tests are executed in d8 using a custom bootstrapping logic. Eventually
|
|
/// this logic needs to be centralized inside the compiler.
|
|
class DdcExecutor implements TargetExecutor {
|
|
static const rootScheme = 'dev-dart-app';
|
|
late final bool _shouldCleanup;
|
|
late final Directory _tmp;
|
|
final Logger _logger;
|
|
|
|
DdcExecutor(this._logger) {
|
|
/// Allow using an environment variable to run tests on a fixed directory.
|
|
/// This prevents the directory from getting deleted too.
|
|
var path = Platform.environment['TMP_DIR'] ?? '';
|
|
if (path.isEmpty) {
|
|
_tmp = Directory.systemTemp.createTempSync('_dynamic_module-');
|
|
_shouldCleanup = true;
|
|
} else {
|
|
_tmp = Directory(path);
|
|
if (!_tmp.existsSync()) _tmp.createSync();
|
|
_shouldCleanup = false;
|
|
}
|
|
}
|
|
|
|
@override
|
|
Future<void> suiteComplete() async {
|
|
if (!_shouldCleanup) return;
|
|
try {
|
|
_tmp.delete(recursive: true);
|
|
} on FileSystemException {
|
|
// Windows bots sometimes fail to delete folders, and can make tests
|
|
// flaky. It is OK in those cases to leak some files in the tmp folder,
|
|
// these will eventually be cleared when a new bot instance is created.
|
|
_logger.warning('Error trying to delete $_tmp');
|
|
}
|
|
}
|
|
|
|
// TODO(sigmund): add support to run also in the ddc-canary mode.
|
|
Future _compile(
|
|
String testName,
|
|
String source,
|
|
Uri sourceDir,
|
|
bool isMain,
|
|
) async {
|
|
var testDir = _tmp.uri.resolve(testName).toFilePath();
|
|
var args = [
|
|
'--packages=${repoRoot.toFilePath()}/.dart_tool/package_config.json',
|
|
ddcAotSnapshot.toFilePath(),
|
|
'--modules=ddc',
|
|
'--no-summarize',
|
|
'--no-source-map',
|
|
'--multi-root',
|
|
'${sourceDir.resolve('../../')}',
|
|
'--multi-root-scheme',
|
|
rootScheme,
|
|
'$rootScheme:/data/$testName/$source',
|
|
'--dart-sdk-summary',
|
|
'$ddcSdkOutline',
|
|
// Note: this needs to change if we ever intend to support packages within
|
|
// the dynamic loading tests themselves
|
|
'--packages=$repoRoot/.dart_tool/package_config.json',
|
|
if (!isMain) ...[
|
|
// TODO(sigmund): consider specifying the module name directly
|
|
'--dynamic-module',
|
|
'--summary=main.dart.dill=main.dart',
|
|
],
|
|
'-o',
|
|
'$source.js',
|
|
];
|
|
await runProcess(
|
|
dartAotBin.toFilePath(),
|
|
args,
|
|
testDir,
|
|
_logger,
|
|
'compile $testName/$source',
|
|
);
|
|
}
|
|
|
|
Future _buildKernelOutline(
|
|
String testName,
|
|
String source,
|
|
Uri sourceDir,
|
|
) async {
|
|
assert(source == 'main.dart');
|
|
var testDir = _tmp.uri.resolve(testName).toFilePath();
|
|
var args = [
|
|
'--packages=${repoRoot.toFilePath()}/.dart_tool/package_config.json',
|
|
kernelWorkerAotSnapshot.toFilePath(),
|
|
'--summary-only',
|
|
'--target',
|
|
'ddc',
|
|
'--multi-root',
|
|
'${sourceDir.resolve('../../')}',
|
|
'--multi-root-scheme',
|
|
rootScheme,
|
|
'--packages-file=$repoRoot/.dart_tool/package_config.json',
|
|
'--dart-sdk-summary',
|
|
'$ddcSdkOutline',
|
|
'--source',
|
|
'$rootScheme:/data/$testName/$source',
|
|
'--output',
|
|
'$source.dill',
|
|
];
|
|
|
|
await runProcess(
|
|
dartAotBin.toFilePath(),
|
|
args,
|
|
testDir,
|
|
_logger,
|
|
'sumarize $testName/$source',
|
|
);
|
|
}
|
|
|
|
@override
|
|
Future compileApplication(DynamicModuleTest test) async {
|
|
_ensureDirectory(test.name);
|
|
_logger.info('Compile ${test.name} app');
|
|
await _buildKernelOutline(test.name, test.main, test.folder);
|
|
await _compile(test.name, test.main, test.folder, true);
|
|
}
|
|
|
|
@override
|
|
Future compileDynamicModule(DynamicModuleTest test, String name) async {
|
|
_logger.info('Compile module ${test.name}.$name');
|
|
_ensureDirectory(test.name);
|
|
await _compile(test.name, test.dynamicModules[name]!, test.folder, false);
|
|
}
|
|
|
|
@override
|
|
Future executeApplication(DynamicModuleTest test) async {
|
|
_logger.info('Execute ${test.name}');
|
|
_ensureDirectory(test.name);
|
|
|
|
// We generate a self contained script that loads necessary preambles,
|
|
// ddc module loader, the necessary modules (the SDK and the main module),
|
|
// and finally launches the app.
|
|
var testDir = _tmp.uri.resolve('${test.name}/');
|
|
var bootstrapUri = testDir.resolve('bootstrap.js');
|
|
// TODO(sigmund): remove hardwired entrypoint name
|
|
String toPath(Uri uri) => uri.toFilePath().replaceAll('\\', '\\\\');
|
|
File.fromUri(bootstrapUri).writeAsStringSync('''
|
|
load('${toPath(ddcPreamblesJs)}'); // preambles/d8.js
|
|
load('${toPath(ddcSealNativeObjectJs)}'); // seal_native_object.js
|
|
load('${toPath(ddcModuleLoaderJs)}'); // ddc_module_loader.js
|
|
load('${toPath(ddcSdkJs)}'); // dart_sdk.js
|
|
load('main.dart.js'); // compiled test module
|
|
|
|
self.dartMainRunner(function () {
|
|
dart_library.configure(
|
|
"_dynamic_module_test", // app name
|
|
{
|
|
dynamicModuleLoader: (uri, onLoad) => {
|
|
let name = uri; // our test framework simply
|
|
// provides the module name as
|
|
// the uri.
|
|
load(`modules/\${name}.js`);
|
|
onLoad(name);
|
|
},
|
|
});
|
|
dart_library.start(
|
|
"_dynamic_module_test", // app name
|
|
'00000000-0000-0000-0000-000000000000', // uuid
|
|
"main.dart", // module
|
|
"data__${test.name}__main", // library containing main
|
|
false
|
|
);
|
|
});
|
|
''');
|
|
var result = await runProcess(
|
|
d8Uri.toFilePath(),
|
|
[bootstrapUri.toFilePath()],
|
|
testDir.toFilePath(),
|
|
_logger,
|
|
'd8 ${test.name}/bootstrap.js',
|
|
);
|
|
var stdout = result.stdout as String;
|
|
if (!stdout.contains(helper.successToken)) {
|
|
_logger.error(
|
|
'Error: test didn\'t complete as expected.\n'
|
|
'Make sure the test finishes and calls `helper.done()`.\n'
|
|
'Test output:\n$stdout',
|
|
);
|
|
throw Exception('missing helper.done');
|
|
}
|
|
}
|
|
|
|
void _ensureDirectory(String name) {
|
|
var dir = Directory.fromUri(_tmp.uri.resolve(name));
|
|
if (!dir.existsSync()) {
|
|
dir.createSync();
|
|
}
|
|
}
|
|
}
|