| // Copyright (c) 2019, 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:convert'; | 
 | import 'dart:io'; | 
 |  | 
 | import 'package:expect/config.dart'; | 
 | import 'package:expect/expect.dart'; | 
 | import 'package:path/path.dart' as path; | 
 |  | 
 | final isAOTRuntime = isVmAotConfiguration; | 
 | final buildDir = path.dirname(Platform.executable); | 
 | final sdkDir = path.dirname(path.dirname(buildDir)); | 
 | late final platformDill = () { | 
 |   final possiblePaths = [ | 
 |     // No cross compilation. | 
 |     path.join(buildDir, 'vm_platform_strong.dill'), | 
 |     // ${MODE}SIMARM_X64 for X64->SIMARM cross compilation. | 
 |     path.join('${buildDir}_X64', 'vm_platform_strong.dill'), | 
 |   ]; | 
 |   for (final path in possiblePaths) { | 
 |     if (File(path).existsSync()) { | 
 |       return path; | 
 |     } | 
 |   } | 
 |   throw 'Could not find vm_platform_strong.dill for build directory $buildDir'; | 
 | }(); | 
 | final genKernel = path.join(sdkDir, 'pkg', 'vm', 'tool', | 
 |     'gen_kernel' + (Platform.isWindows ? '.bat' : '')); | 
 | final genKernelDart = path.join('pkg', 'vm', 'bin', 'gen_kernel.dart'); | 
 | final _genSnapshotBase = 'gen_snapshot' + (Platform.isWindows ? '.exe' : ''); | 
 | // Lazily initialize `genSnapshot` so that tests that don't use it on platforms | 
 | // that don't have a `gen_snapshot` don't fail. | 
 | late final genSnapshot = () { | 
 |   final possiblePaths = [ | 
 |     // No cross compilation. | 
 |     path.join(buildDir, _genSnapshotBase), | 
 |     // ${MODE}SIMARM_X64 for X64->SIMARM cross compilation. | 
 |     path.join('${buildDir}_X64', _genSnapshotBase), | 
 |     // ${MODE}XARM64/clang_x64 for X64->ARM64 cross compilation. | 
 |     path.join(buildDir, 'clang_x64', _genSnapshotBase), | 
 |   ]; | 
 |   for (final path in possiblePaths) { | 
 |     if (File(path).existsSync()) { | 
 |       return path; | 
 |     } | 
 |   } | 
 |   throw 'Could not find gen_snapshot for build directory $buildDir'; | 
 | }(); | 
 | final dart = path.join(buildDir, 'dart' + (Platform.isWindows ? '.exe' : '')); | 
 | final dartPrecompiledRuntime = path.join( | 
 |     buildDir, 'dart_precompiled_runtime' + (Platform.isWindows ? '.exe' : '')); | 
 | final checkedInDartVM = path.join('tools', 'sdks', 'dart-sdk', 'bin', | 
 |     'dart' + (Platform.isWindows ? '.exe' : '')); | 
 | // Lazily initialize 'lipo' so that tests that don't use it on platforms | 
 | // that don't have it don't fail. | 
 | late final lipo = () { | 
 |   final path = "/usr/bin/lipo"; | 
 |   if (File(path).existsSync()) { | 
 |     return path; | 
 |   } | 
 |   throw 'Could not find lipo binary at $path'; | 
 | }(); | 
 |  | 
 | final isSimulator = path.basename(buildDir).contains('SIM'); | 
 |  | 
 | String? get clangBuildToolsDir { | 
 |   String archDir; | 
 |   if (Platform.isLinux) { | 
 |     archDir = 'linux-x64'; | 
 |   } else if (Platform.isMacOS) { | 
 |     archDir = 'mac-x64'; | 
 |   } else { | 
 |     return null; | 
 |   } | 
 |   var clangDir = path.join(sdkDir, 'buildtools', archDir, 'clang', 'bin'); | 
 |   return Directory(clangDir).existsSync() ? clangDir : null; | 
 | } | 
 |  | 
 | Future<void> assembleSnapshot(String assemblyPath, String snapshotPath, | 
 |     {bool debug = false}) async { | 
 |   if (!Platform.isLinux && !Platform.isMacOS) { | 
 |     throw "Unsupported platform ${Platform.operatingSystem} for assembling"; | 
 |   } | 
 |  | 
 |   final ccFlags = <String>[]; | 
 |   final ldFlags = <String>[]; | 
 |   String cc = 'gcc'; | 
 |   String shared = '-shared'; | 
 |  | 
 |   if (buildDir.endsWith('SIMRISCV64')) { | 
 |     cc = 'riscv64-linux-gnu-gcc'; | 
 |   } else if (isSimulator) { | 
 |     // For other simulators, depend on buildtools existing. | 
 |     final clangBuildTools = clangBuildToolsDir; | 
 |     if (clangBuildTools != null) { | 
 |       cc = path.join(clangBuildTools, 'clang'); | 
 |     } else { | 
 |       throw 'Cannot assemble for ${path.basename(buildDir)} ' | 
 |           'without //buildtools on ${Platform.operatingSystem}'; | 
 |     } | 
 |   } else if (Platform.isMacOS) { | 
 |     cc = 'clang'; | 
 |   } | 
 |  | 
 |   if (buildDir.endsWith('SIMARM')) { | 
 |     ccFlags.add('--target=armv7-linux-gnueabihf'); | 
 |   } else if (buildDir.endsWith('SIMARM64')) { | 
 |     ccFlags.add('--target=aarch64-linux-gnu'); | 
 |   } else if (buildDir.endsWith('SIMRISCV64')) { | 
 |     ccFlags.add('--target=riscv64-linux-gnu'); | 
 |   } else if (Platform.isMacOS) { | 
 |     shared = '-dynamiclib'; | 
 |     // Tell Mac linker to give up generating eh_frame from dwarf. | 
 |     ldFlags.add('-Wl,-no_compact_unwind'); | 
 |   } | 
 |  | 
 |   if (buildDir.endsWith('X64') || buildDir.endsWith('SIMARM64')) { | 
 |     ccFlags.add('-m64'); | 
 |   } | 
 |   if (debug) { | 
 |     ccFlags.add('-g'); | 
 |   } | 
 |  | 
 |   await run(cc, <String>[ | 
 |     ...ccFlags, | 
 |     ...ldFlags, | 
 |     shared, | 
 |     if (!Platform.isMacOS) '-nostdlib', | 
 |     '-o', | 
 |     snapshotPath, | 
 |     assemblyPath, | 
 |   ]); | 
 | } | 
 |  | 
 | Future<void> stripSnapshot(String snapshotPath, String strippedPath, | 
 |     {bool forceElf = false}) async { | 
 |   if (!Platform.isLinux && !Platform.isMacOS) { | 
 |     throw "Unsupported platform ${Platform.operatingSystem} for stripping"; | 
 |   } | 
 |  | 
 |   var strip = 'strip'; | 
 |  | 
 |   if (isSimulator || (Platform.isMacOS && forceElf)) { | 
 |     final clangBuildTools = clangBuildToolsDir; | 
 |     if (clangBuildTools != null) { | 
 |       strip = path.join(clangBuildTools, 'llvm-strip'); | 
 |     } else { | 
 |       throw 'Cannot strip ELF files for ${path.basename(buildDir)} ' | 
 |           'without //buildtools on ${Platform.operatingSystem}'; | 
 |     } | 
 |   } | 
 |  | 
 |   await run(strip, <String>[ | 
 |     '-o', | 
 |     strippedPath, | 
 |     snapshotPath, | 
 |   ]); | 
 | } | 
 |  | 
 | Future<ProcessResult> runHelper(String executable, List<String> args) async { | 
 |   print('Running $executable ${args.join(' ')}'); | 
 |  | 
 |   final result = await Process.run(executable, args); | 
 |   print('Subcommand terminated with exit code ${result.exitCode}.'); | 
 |   if (result.stdout.isNotEmpty) { | 
 |     print('Subcommand stdout:'); | 
 |     print(result.stdout); | 
 |   } | 
 |   if (result.exitCode != 0) { | 
 |     if (result.stderr.isNotEmpty) { | 
 |       print('Subcommand stderr:'); | 
 |       print(result.stderr); | 
 |     } | 
 |   } | 
 |  | 
 |   return result; | 
 | } | 
 |  | 
 | Future<bool> testExecutable(String executable) async { | 
 |   try { | 
 |     final result = await runHelper(executable, <String>['--version']); | 
 |     return result.exitCode == 0; | 
 |   } on ProcessException catch (e) { | 
 |     print('Got process exception: $e'); | 
 |     return false; | 
 |   } | 
 | } | 
 |  | 
 | Future<void> run(String executable, List<String> args) async { | 
 |   final result = await runHelper(executable, args); | 
 |  | 
 |   if (result.exitCode != 0) { | 
 |     throw 'Command failed with unexpected exit code (was ${result.exitCode})'; | 
 |   } | 
 | } | 
 |  | 
 | Future<List<String>> runOutput(String executable, List<String> args) async { | 
 |   final result = await runHelper(executable, args); | 
 |  | 
 |   if (result.exitCode != 0) { | 
 |     throw 'Command failed with unexpected exit code (was ${result.exitCode})'; | 
 |   } | 
 |   Expect.isTrue(result.stdout.isNotEmpty); | 
 |   Expect.isTrue(result.stderr.isEmpty); | 
 |  | 
 |   return LineSplitter.split(result.stdout).toList(growable: false); | 
 | } | 
 |  | 
 | Future<List<String>> runError(String executable, List<String> args) async { | 
 |   final result = await runHelper(executable, args); | 
 |  | 
 |   if (result.exitCode == 0) { | 
 |     throw 'Command did not fail with non-zero exit code'; | 
 |   } | 
 |   Expect.isTrue(result.stdout.isEmpty); | 
 |   Expect.isTrue(result.stderr.isNotEmpty); | 
 |  | 
 |   return LineSplitter.split(result.stderr).toList(growable: false); | 
 | } | 
 |  | 
 | const keepTempKey = 'KEEP_TEMPORARY_DIRECTORIES'; | 
 |  | 
 | Future<void> withTempDir(String name, Future<void> fun(String dir)) async { | 
 |   final tempDir = Directory.systemTemp.createTempSync(name); | 
 |   try { | 
 |     await fun(tempDir.path); | 
 |   } finally { | 
 |     if (!Platform.environment.containsKey(keepTempKey) || | 
 |         Platform.environment[keepTempKey]!.isEmpty) { | 
 |       tempDir.deleteSync(recursive: true); | 
 |     } | 
 |   } | 
 | } |