diff --git a/pkg/dart2native/analysis_options.yaml b/pkg/dart2native/analysis_options.yaml index d1e7e419769..8b452172bb4 100644 --- a/pkg/dart2native/analysis_options.yaml +++ b/pkg/dart2native/analysis_options.yaml @@ -8,3 +8,7 @@ linter: rules: - prefer_final_in_for_each - prefer_final_locals + +formatter: + trailing_commas: preserve + diff --git a/pkg/dart2native/lib/sdk.dart b/pkg/dart2native/lib/sdk.dart index 73c510445be..fd478ced18b 100644 --- a/pkg/dart2native/lib/sdk.dart +++ b/pkg/dart2native/lib/sdk.dart @@ -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', diff --git a/pkg/dartdev/lib/src/commands/build.dart b/pkg/dartdev/lib/src/commands/build.dart index e1cdd89656b..7abf7867521 100644 --- a/pkg/dartdev/lib/src/commands/build.dart +++ b/pkg/dartdev/lib/src/commands/build.dart @@ -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 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 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) { diff --git a/pkg/dartdev/lib/src/commands/compile.dart b/pkg/dartdev/lib/src/commands/compile.dart index 89462588473..0414b08e854 100644 --- a/pkg/dartdev/lib/src/commands/compile.dart +++ b/pkg/dartdev/lib/src/commands/compile.dart @@ -70,6 +70,16 @@ enum Sanitizer { } return null; } + + static List 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 diff --git a/pkg/dartdev/test/native_assets/build_test.dart b/pkg/dartdev/test/native_assets/build_test.dart index 6a81aeda89e..0e002644cfb 100644 --- a/pkg/dartdev/test/native_assets/build_test.dart +++ b/pkg/dartdev/test/native_assets/build_test.dart @@ -350,6 +350,39 @@ void main(List 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 _withTempDir(Future Function(Uri tempUri) fun) async {