blob: d1570e71097c2836d068dc1a0973d306c498a89b [file] [log] [blame] [edit]
// Copyright (c) 2025, 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:io';
import 'package:code_assets/code_assets.dart';
import 'package:dartdev/src/commands/compile.dart'
show CompileNativeCommand, crossCompileErrorExitCode;
import 'package:hooks_runner/hooks_runner.dart';
import 'package:path/path.dart' as path;
import 'package:test/test.dart';
import '../utils.dart';
import 'compile_test.dart' show targetingHostOSMessage, usingTargetOSMessage;
void main() {
ensureRunFromSdkBinDart();
// Cross compilation is not available on 32-bit architectures.
final hostArch = Target.current.architecture;
final bool isRunningOn32Bit = hostArch == Architecture.ia32 ||
hostArch == Architecture.arm ||
hostArch == Architecture.riscv32;
group('cross compile -', defineCrossCompileTests,
timeout: longTimeout, skip: isRunningOn32Bit);
}
String unsupportedTargetMessage(Target target) =>
'Unsupported target platform $target';
typedef TestFunction = Future<void> Function();
void defineCrossCompileTests() {
final subcommands = [
CompileNativeCommand.aotSnapshotCmdName,
CompileNativeCommand.exeCmdName,
];
final crossCompileTargets = CompileNativeCommand.supportedTargetPlatforms;
String mainMessage(Target target) => 'I love ${target.os}';
Future<(ProcessResult, String)> crossCompile(
String subcommand, Target target) async {
final p =
project(mainSrc: 'void main() {print("${mainMessage(target)}");}');
final inFile = path.canonicalize(path.join(p.dirPath, p.relativeFilePath));
final filename = subcommand == CompileNativeCommand.exeCmdName
? 'myexe'
: subcommand == CompileNativeCommand.aotSnapshotCmdName
? 'out.so'
: throw ArgumentError(
'Unexpected subcommand $subcommand', 'subcommand');
final outFile = path.canonicalize(path.join(p.dirPath, filename));
final result = await p.run(
[
'compile',
subcommand,
'-v',
'--target-os',
target.os.name,
'--target-arch',
target.architecture.name,
'-o',
outFile,
inFile,
],
);
print('Subcommand terminated with exit code ${result.exitCode}.');
if (result.stdout.isNotEmpty) {
print('Subcommand stdout:');
print(result.stdout);
}
if (result.stderr.isNotEmpty) {
print('Subcommand stderr:');
print(result.stderr);
}
return (result, outFile);
}
TestFunction crossCompileTest(String subcommand, Target target) => () async {
expect(subcommand, isIn(subcommands));
expect(target, isIn(crossCompileTargets));
var (result, outFile) = await crossCompile(subcommand, target);
expect(result.stdout, contains(usingTargetOSMessage(target.os)));
expect(
result.stderr, isNot(contains(unsupportedTargetMessage(target))));
expect(result.exitCode, 0);
expect(File(outFile).existsSync(), true,
reason: 'File not found: $outFile');
if (target != Target.current) return;
if (subcommand == CompileNativeCommand.exeCmdName) {
result = Process.runSync(outFile, const []);
} else {
expect(subcommand, CompileNativeCommand.aotSnapshotCmdName);
final Directory binDir = File(Platform.resolvedExecutable).parent;
result = Process.runSync(
path.join(binDir.path, 'dartaotruntime'),
[outFile],
);
}
expect(result.stdout, contains(mainMessage(target)));
expect(result.stderr, isEmpty);
expect(result.exitCode, 0);
};
TestFunction crossCompileFailureTest(String subcommand, Target target) =>
() async {
expect(subcommand, isIn(subcommands));
expect(target, isNot(Target.current));
expect(target, isNot(isIn(crossCompileTargets)));
final (result, outFile) = await crossCompile(subcommand, target);
expect(result.stdout, isNot(contains(targetingHostOSMessage)));
expect(result.stdout, isNot(contains(usingTargetOSMessage(target.os))));
expect(result.stderr, contains(unsupportedTargetMessage(target)));
expect(result.exitCode, crossCompileErrorExitCode);
expect(File(outFile).existsSync(), false,
reason: 'File created despite failure: $outFile');
};
for (final subcommand in subcommands) {
for (final target in crossCompileTargets) {
test('Compile $subcommand can cross compile to $target',
crossCompileTest(subcommand, target));
}
var targetOS = Platform.isWindows ? OS.macOS : OS.windows;
var targetArch = Architecture.arm64;
var target = Target.fromArchitectureAndOS(targetArch, targetOS);
test('Compile $subcommand fails on invalid target OS',
crossCompileFailureTest(subcommand, target));
targetOS = OS.linux;
targetArch = Architecture.riscv32;
target = Target.fromArchitectureAndOS(targetArch, targetOS);
test('Compile $subcommand fails on invalid target architecture',
crossCompileFailureTest(subcommand, target));
}
}