| // 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:convert'; | |
| import 'dart:io'; | |
| import 'package:code_assets/code_assets.dart'; | |
| import 'package:hooks/hooks.dart'; | |
| import 'package:native_test_helpers/native_test_helpers.dart'; | |
| import 'package:test/test.dart'; | |
| import '../hook/build.dart' as hook; | |
| void main() { | |
| test('build hook decodes percent-encoded package root paths', () async { | |
| final tempDir = await Directory.systemTemp.createTemp( | |
| 'objective_c_hook_path', | |
| ); | |
| addTearDown(() => tempDir.delete(recursive: true)); | |
| final realPackageRoot = findPackageRoot('objective_c'); | |
| const rootDirName = 'pkg%47root'; | |
| final rootDir = Directory('${tempDir.path}/$rootDirName'); | |
| await rootDir.create(); | |
| final symlinkPath = '${rootDir.path}/objective_c'; | |
| await Link(symlinkPath).create(realPackageRoot.toFilePath()); | |
| final symlinkDir = Directory(symlinkPath); | |
| final encodedPath = symlinkPath.replaceAll('%', '%25'); | |
| final encodedPackageRoot = Uri.parse( | |
| 'file:///${encodedPath.substring(1)}/', | |
| ); | |
| expect(encodedPackageRoot.toString(), contains('%2547')); | |
| final compilerLog = tempDir.uri.resolve('compiler_args.txt').toFilePath(); | |
| File(compilerLog).writeAsStringSync(''); | |
| final compilerScript = tempDir.uri.resolve('clang').toFilePath(); | |
| File(compilerScript).writeAsStringSync('''#!/bin/sh | |
| if [ "\$1" = "--version" ]; then | |
| echo "Apple clang version 15.0.0" | |
| exit 0 | |
| fi | |
| log="$compilerLog" | |
| out="" | |
| prev="" | |
| for arg in "\$@"; do | |
| if [ "\$prev" = "-o" ]; then | |
| out="\$arg" | |
| fi | |
| prev="\$arg" | |
| printf '%s\\n' "\$arg" >> "\$log" | |
| done | |
| if [ -n "\$out" ]; then | |
| mkdir -p "\$(dirname "\$out")" | |
| : > "\$out" | |
| fi | |
| exit 0 | |
| '''); | |
| Process.runSync('chmod', ['+x', compilerScript]); | |
| final outputDirectoryShared = tempDir.uri.resolve('output_shared/'); | |
| await Directory.fromUri(outputDirectoryShared).create(); | |
| final outputFile = tempDir.uri.resolve('output.json'); | |
| final inputBuilder = BuildInputBuilder() | |
| ..setupShared( | |
| packageRoot: encodedPackageRoot, | |
| packageName: 'objective_c', | |
| outputFile: outputFile, | |
| outputDirectoryShared: outputDirectoryShared, | |
| ) | |
| ..setupBuildInput() | |
| ..config.setupBuild(linkingEnabled: false) | |
| ..addExtension( | |
| CodeAssetExtension( | |
| targetArchitecture: Architecture.current, | |
| targetOS: OS.macOS, | |
| linkModePreference: LinkModePreference.dynamic, | |
| macOS: MacOSCodeConfig(targetVersion: 13), | |
| cCompiler: CCompilerConfig( | |
| compiler: Uri.file(compilerScript), | |
| linker: Uri.file(compilerScript), | |
| archiver: Uri.file(compilerScript), | |
| ), | |
| ), | |
| ); | |
| final input = inputBuilder.build(); | |
| final inputFile = tempDir.uri.resolve('input.json'); | |
| File.fromUri(inputFile).writeAsStringSync(json.encode(input.json)); | |
| final originalCwd = Directory.current; | |
| Directory.current = symlinkDir; | |
| try { | |
| await (hook.main as dynamic)(['--config=${inputFile.toFilePath()}']); | |
| } finally { | |
| Directory.current = originalCwd; | |
| } | |
| final logLines = File(compilerLog).readAsLinesSync(); | |
| final decodedSrc = '$symlinkPath/src/objective_c.c'; | |
| expect(logLines, contains(decodedSrc)); | |
| expect(logLines.any((line) => line.contains('%2547')), isFalse); | |
| final decodedUtil = '$symlinkPath/test/util.c'; | |
| expect(logLines, isNot(contains(decodedUtil))); | |
| }, skip: !Platform.isMacOS ? 'Requires macOS' : null); | |
| } |