[dartdev] dart build cli add --target-sanitizer support

Closes: https://github.com/dart-lang/sdk/issues/63374

Does not yet add sanitizer support for build hooks:
https://github.com/dart-lang/native/issues/2497

Change-Id: I53763b94d03a9b759f226a2b45f723706a1e5934
Cq-Include-Trybots: luci.dart.try:pkg-linux-debug-try,pkg-linux-release-arm64-try,pkg-linux-release-try,pkg-mac-release-try,pkg-win-release-try
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/504880
Commit-Queue: Daco Harkes <dacoharkes@google.com>
Reviewed-by: Ryan Macnak <rmacnak@google.com>
Reviewed-by: Ben Konyi <bkonyi@google.com>
This commit is contained in:
Daco Harkes
2026-05-20 13:45:54 -07:00
committed by dart-scoped@luci-project-accounts.iam.gserviceaccount.com
parent bb220724dc
commit 6f7f338328
5 changed files with 84 additions and 16 deletions
+4
View File
@@ -8,3 +8,7 @@ linter:
rules:
- prefer_final_in_for_each
- prefer_final_locals
formatter:
trailing_commas: preserve
+13 -4
View File
@@ -38,10 +38,19 @@ class Sdk {
'dartvm',
);
String get dartAotRuntime => _executablePathFor(
'dartaotruntime',
forceProductInBuildRoot: true,
);
String dartAotRuntimeFor({
String? sanitizer,
}) {
final name = sanitizer != null && sanitizer != 'none'
? 'dartaotruntime_$sanitizer'
: 'dartaotruntime';
return _executablePathFor(
name,
forceProductInBuildRoot: true,
);
}
String get dartAotRuntime => dartAotRuntimeFor();
String get genSnapshot => _executablePathFor(
'gen_snapshot',
+23 -4
View File
@@ -139,14 +139,25 @@ then that is used instead.''',
valueHelp: 'path',
help: 'Path to output Ninja depfile',
)
..addOption(
'target-sanitizer',
help: 'Build with a specific target sanitizer.',
allowed: Sanitizer.available().map((s) => s.name).toList(),
defaultsTo: 'none',
)
..addExperimentalFlags(verbose: verbose);
}
@override
Future<int> run() async {
final args = argResults!;
final sanitizer = Sanitizer.fromString(args.option('target-sanitizer'))!;
final targetDartAotRuntime = sdk.dartAotRuntimeFor(
sanitizer: sanitizer.name,
);
if (!checkArtifactExists(sdk.genKernelSnapshot) ||
!checkArtifactExists(sdk.genSnapshot) ||
!checkArtifactExists(sdk.dartAotRuntime) ||
!checkArtifactExists(targetDartAotRuntime) ||
!checkArtifactExists(sdk.dart)) {
return 255;
}
@@ -156,7 +167,6 @@ then that is used instead.''',
stderr.writeln("'dart build' is not supported on x86 architectures.");
return 64;
}
final args = argResults!;
var target = args.option('target');
if (target == null) {
@@ -205,6 +215,7 @@ then that is used instead.''',
verbose: verbose,
verbosity: verbosity,
depFile: depFile,
sanitizer: sanitizer,
);
}
@@ -218,6 +229,7 @@ then that is used instead.''',
required List<String> enabledExperiments,
required bool verbose,
required String verbosity,
Sanitizer sanitizer = Sanitizer.none,
bool progressUpdatesOnStderr = false,
String? depFile,
}) async {
@@ -273,6 +285,8 @@ then that is used instead.''',
pubspecUri ??= await DartNativeAssetsBuilder.findWorkspacePubspec(
packageConfigUri,
);
// TODO(https://github.com/dart-lang/native/issues/2497): Pass the sanitizer
// to the native assets builder.
final builder = DartNativeAssetsBuilder(
pubspecUri: pubspecUri,
packageConfigUri: packageConfigUri,
@@ -315,13 +329,15 @@ then that is used instead.''',
);
final generator = KernelGenerator(
genSnapshot: sdk.genSnapshot,
targetDartAotRuntime: sdk.dartAotRuntime,
targetDartAotRuntime: sdk.dartAotRuntimeFor(
sanitizer: sanitizer.name,
),
kind: Kind.exe,
sourceFile: e.sourceEntryPoint.toFilePath(),
outputFile: outputExeUri.toFilePath(),
verbose: verbose,
verbosity: verbosity,
defines: [],
defines: [...sanitizer.defines],
packages: packageConfigUri.toFilePath(),
targetOS: targetOS,
enableExperiment: enabledExperiments.join(','),
@@ -395,6 +411,9 @@ Use linkMode as dynamic library instead.""",
await snapshotGenerator.generate(
nativeAssets: nativeAssetsYamlUri?.toFilePath(),
extraOptions: [
...sanitizer.genSnapshotFlags,
],
);
if (targetOS == OS.macOS) {
+11 -8
View File
@@ -70,6 +70,16 @@ enum Sanitizer {
}
return null;
}
static List<Sanitizer> available() {
final v = Platform.version;
if (v.contains('"linux_x64"') || v.contains('"linux_arm64"')) {
return [none, asan, msan, tsan];
} else if (v.contains('"linux_riscv64"')) {
return [none, asan, tsan];
}
return [none];
}
}
bool checkFile(String sourcePath) {
@@ -608,14 +618,7 @@ Remove debugging information from the output and save it separately to the speci
if (commandName != aotSnapshotCmdName) {
return ['none'];
}
final v = Platform.version;
if (v.contains('"linux_x64"') || v.contains('"linux_arm64"')) {
return ['none', 'asan', 'msan', 'tsan'];
} else if (v.contains('"linux_riscv64"')) {
return ['none', 'asan', 'tsan'];
}
return ['none'];
return Sanitizer.available().map((s) => s.name).toList();
}
@override
@@ -350,6 +350,39 @@ void main(List<String> args) {
},
);
}
for (var sanitizer in ['asan', 'msan', 'tsan']) {
test('dart build cli --target-sanitizer $sanitizer', timeout: longTimeout,
() async {
await nativeAssetsTest('dart_app', (dartAppUri) async {
final result = await runDart(
arguments: [
'build',
'cli',
'--target-sanitizer',
sanitizer,
],
workingDirectory: dartAppUri,
logger: logger,
expectExitCodeZero: false,
);
final Directory binDir = File(Platform.resolvedExecutable).parent;
final sanitizedRuntime =
File.fromUri(binDir.uri.resolve('dartaotruntime_$sanitizer'));
if (sanitizedRuntime.existsSync()) {
expect(result.exitCode, 0);
final relativeExeUri = relativeBundleUri
.resolve('bin/')
.resolve(OS.current.executableFileName('dart_app'));
final absoluteExeUri = dartAppUri.resolveUri(relativeExeUri);
expect(await File.fromUri(absoluteExeUri).exists(), true);
} else {
expect(result.stderr, contains('dartaotruntime_$sanitizer'));
expect(result.exitCode, 255);
}
});
}, skip: !Platform.isLinux);
}
}
Future<void> _withTempDir(Future<void> Function(Uri tempUri) fun) async {