From 40b7fee86f556ba4f7a00bcf5e34f98cdb5c6df0 Mon Sep 17 00:00:00 2001 From: Gold87 <91761103+Gold872@users.noreply.github.com> Date: Mon, 2 Feb 2026 16:26:20 -0500 Subject: [PATCH 1/3] Pass environment through cmake executable resolve --- lib/src/builder/builder.dart | 2 +- lib/src/builder/run_builder.dart | 30 +++++--- lib/src/native_toolchain/android_ndk.dart | 11 ++- lib/src/native_toolchain/cmake.dart | 10 ++- lib/src/native_toolchain/msvc.dart | 14 +++- lib/src/native_toolchain/ninja.dart | 10 ++- lib/src/native_toolchain/recognizer.dart | 18 ++++- lib/src/native_toolchain/xcode.dart | 20 ++++- lib/src/tool/tool_resolver.dart | 94 ++++++++++++++++++----- 9 files changed, 164 insertions(+), 45 deletions(-) diff --git a/lib/src/builder/builder.dart b/lib/src/builder/builder.dart index a84a7f3..5a52c35 100644 --- a/lib/src/builder/builder.dart +++ b/lib/src/builder/builder.dart @@ -349,7 +349,7 @@ class CMakeBuilder implements Builder { final tools = await vcvars.defaultResolver!.resolve(logger: logger); if (tools.isNotEmpty) { final _vars = await environmentFromBatchFile(tools.first.uri); - logger?.info('Environment variables from $vcvars: $vars'); + logger?.info('Environment variables from $vcvars: $_vars'); vars.addAll(_vars); return vars; } diff --git a/lib/src/builder/run_builder.dart b/lib/src/builder/run_builder.dart index 4bcddb7..297df0e 100644 --- a/lib/src/builder/run_builder.dart +++ b/lib/src/builder/run_builder.dart @@ -83,15 +83,23 @@ class RunCMakeBuilder { }) : outDir = outputDir ?? input.outputDirectory, userConfig = userConfig ?? UserConfig(targetOS: codeConfig.targetOS); - Future cmakePath() async { - final cmakeTools = await cmake.defaultResolver?.resolve(logger: logger, userConfig: userConfig); + Future cmakePath({Map? environment}) async { + final cmakeTools = await cmake.defaultResolver?.resolve( + logger: logger, + userConfig: userConfig, + environment: environment, + ); final path = cmakeTools?.first.uri; assert(path != null); return Future.value(path); } - Future ninjaPath() async { - final ninjaTools = await ninja.defaultResolver?.resolve(logger: logger, userConfig: userConfig); + Future ninjaPath({Map? environment}) async { + final ninjaTools = await ninja.defaultResolver?.resolve( + logger: logger, + userConfig: userConfig, + environment: environment, + ); final path = ninjaTools?.first.uri; assert(path != null); return Future.value(path); @@ -101,8 +109,12 @@ class RunCMakeBuilder { Future iosToolchainCmake() async => (await currentPackageRoot()).resolve('cmake/ios.toolchain.cmake'); - Future androidToolchainCmake() async { - final tool = await androidNdk.defaultResolver?.resolve(logger: logger, userConfig: userConfig); + Future androidToolchainCmake({Map? environment}) async { + final tool = await androidNdk.defaultResolver?.resolve( + logger: logger, + userConfig: userConfig, + environment: environment, + ); final toolUri = tool?.first.uri.resolve('build/cmake/android.toolchain.cmake'); assert(toolUri != null); return Future.value(toolUri); @@ -185,14 +197,14 @@ class RunCMakeBuilder { defines.forEach((k, v) => _defines.add('-D$k=${v ?? "1"}')); if (generator == Generator.ninja) { - final ninjaBinUri = await ninjaPath(); + final ninjaBinUri = await ninjaPath(environment: environment); final ninjaBinDir = File.fromUri(ninjaBinUri).parent.path; _defines.add('-DCMAKE_PROGRAM_PATH=$ninjaBinDir'); } final _generator = generator.toArgs(); final results = await runProcess( - executable: await cmakePath(), + executable: await cmakePath(environment: environment), arguments: [ '--log-level=${logLevel.name}', '-S', @@ -225,7 +237,7 @@ class RunCMakeBuilder { Future _build({Map? environment}) async { return runProcess( - executable: await cmakePath(), + executable: await cmakePath(environment: environment), arguments: [ '--build', outDir.normalizePath().toFilePath(), diff --git a/lib/src/native_toolchain/android_ndk.dart b/lib/src/native_toolchain/android_ndk.dart index 92bc452..6a3e987 100644 --- a/lib/src/native_toolchain/android_ndk.dart +++ b/lib/src/native_toolchain/android_ndk.dart @@ -31,7 +31,11 @@ final androidNdkLld = Tool(name: lld.name, defaultResolver: _AndroidNdkResolver( class _AndroidNdkResolver implements ToolResolver { @override - Future> resolve({required Logger? logger, UserConfig? userConfig}) async { + Future> resolve({ + required Logger? logger, + UserConfig? userConfig, + Map? environment, + }) async { final installLocationResolver = PathVersionResolver( wrappedResolver: ToolResolvers([ RelativeToolResolver( @@ -57,7 +61,10 @@ class _AndroidNdkResolver implements ToolResolver { ]), ); - final ndkInstances = await installLocationResolver.resolve(logger: logger); + final ndkInstances = await installLocationResolver.resolve( + logger: logger, + environment: environment, + ); // sort latest version first ndkInstances.sort( (a, b) => switch ((a.version, b.version)) { diff --git a/lib/src/native_toolchain/cmake.dart b/lib/src/native_toolchain/cmake.dart index d0f52f4..9c53c3e 100644 --- a/lib/src/native_toolchain/cmake.dart +++ b/lib/src/native_toolchain/cmake.dart @@ -46,14 +46,18 @@ class _CmakeResolver implements ToolResolver { } @override - Future> resolve({required Logger? logger, UserConfig? userConfig}) async { + Future> resolve({ + required Logger? logger, + UserConfig? userConfig, + Map? environment, + }) async { // here, we always try to find android cmake first and filter out unsatisfied versions final androidResolver = _getAndroidResolver(userConfig: userConfig); - final androidCmakeInstances = await androidResolver.resolve(logger: logger); + final androidCmakeInstances = await androidResolver.resolve(logger: logger, environment: environment); logger?.info('Found Android CMake: ${androidCmakeInstances.map((e) => e.toString()).join(', ')}'); final systemResolver = _getSystemResolver(); - final systemCmakeInstances = await systemResolver.resolve(logger: logger); + final systemCmakeInstances = await systemResolver.resolve(logger: logger, environment: environment); logger?.info('Found System CMake: ${systemCmakeInstances.map((e) => e.toString()).join(', ')}'); final combinedCmakeInstances = []; diff --git a/lib/src/native_toolchain/msvc.dart b/lib/src/native_toolchain/msvc.dart index cb2fe76..cbe4082 100644 --- a/lib/src/native_toolchain/msvc.dart +++ b/lib/src/native_toolchain/msvc.dart @@ -250,8 +250,16 @@ Tool _msvcTool({ class VisualStudioResolver implements ToolResolver { @override - Future> resolve({required Logger? logger, UserConfig? userConfig}) async { - final vswhereInstances = await vswhere.defaultResolver!.resolve(logger: logger, userConfig: userConfig); + Future> resolve({ + required Logger? logger, + UserConfig? userConfig, + Map? environment, + }) async { + final vswhereInstances = await vswhere.defaultResolver!.resolve( + logger: logger, + userConfig: userConfig, + environment: environment, + ); final result = []; for (final vswhereInstance in vswhereInstances.take(1)) { @@ -260,6 +268,7 @@ class VisualStudioResolver implements ToolResolver { executable: vswhereInstance.uri, arguments: arguments, logger: logger, + environment: environment, ); var toolInfos = json.decode(vswhereResult.stdout) as List; // Try again including prerelease versions if no stable versions found. @@ -268,6 +277,7 @@ class VisualStudioResolver implements ToolResolver { executable: vswhereInstance.uri, arguments: [...arguments, '-prerelease'], logger: logger, + environment: environment, ); toolInfos = json.decode(vswhereResult.stdout) as List; } diff --git a/lib/src/native_toolchain/ninja.dart b/lib/src/native_toolchain/ninja.dart index d6fc01c..6e914e7 100644 --- a/lib/src/native_toolchain/ninja.dart +++ b/lib/src/native_toolchain/ninja.dart @@ -45,13 +45,17 @@ class _NinjaResolver implements ToolResolver { } @override - Future> resolve({required Logger? logger, UserConfig? userConfig}) async { + Future> resolve({ + required Logger? logger, + UserConfig? userConfig, + Map? environment, + }) async { final androidResolver = _getAndroidResolver(userConfig: userConfig); - final androidNinjaInstances = await androidResolver.resolve(logger: logger); + final androidNinjaInstances = await androidResolver.resolve(logger: logger, environment: environment); logger?.info('Found Android Ninja: ${androidNinjaInstances.map((e) => e.toString()).join(', ')}'); final systemResolver = _getSystemResolver(); - final systemNinjaInstances = await systemResolver.resolve(logger: logger); + final systemNinjaInstances = await systemResolver.resolve(logger: logger, environment: environment); logger?.info('Found System Ninja: ${systemNinjaInstances.map((e) => e.toString()).join(', ')}'); final combinedNinjaInstances = []; diff --git a/lib/src/native_toolchain/recognizer.dart b/lib/src/native_toolchain/recognizer.dart index 322e186..0ceb450 100644 --- a/lib/src/native_toolchain/recognizer.dart +++ b/lib/src/native_toolchain/recognizer.dart @@ -20,7 +20,11 @@ class CompilerRecognizer implements ToolResolver { CompilerRecognizer(this.uri); @override - Future> resolve({required Logger? logger, UserConfig? userConfig}) async { + Future> resolve({ + required Logger? logger, + UserConfig? userConfig, + Map? environment, + }) async { final os = OS.current; logger?.finer('Trying to recognize $uri.'); final filePath = uri.toFilePath(); @@ -61,7 +65,11 @@ class LinkerRecognizer implements ToolResolver { LinkerRecognizer(this.uri); @override - Future> resolve({required Logger? logger, UserConfig? userConfig}) async { + Future> resolve({ + required Logger? logger, + UserConfig? userConfig, + Map? environment, + }) async { final os = OS.current; logger?.finer('Trying to recognize $uri.'); final filePath = uri.toFilePath(); @@ -106,7 +114,11 @@ class ArchiverRecognizer implements ToolResolver { ArchiverRecognizer(this.uri); @override - Future> resolve({required Logger? logger, UserConfig? userConfig}) async { + Future> resolve({ + required Logger? logger, + UserConfig? userConfig, + Map? environment, + }) async { logger?.finer('Trying to recognize $uri.'); final os = OS.current; final filePath = uri.toFilePath(); diff --git a/lib/src/native_toolchain/xcode.dart b/lib/src/native_toolchain/xcode.dart index 4321015..8e32b57 100644 --- a/lib/src/native_toolchain/xcode.dart +++ b/lib/src/native_toolchain/xcode.dart @@ -33,23 +33,35 @@ final Tool iPhoneSimulatorSdk = Tool(name: 'iPhoneSimulator SDK', defaultResolve class XCodeSdkResolver implements ToolResolver { @override - Future> resolve({required Logger? logger, UserConfig? userConfig}) async { - final xcrunInstances = await xcrun.defaultResolver!.resolve(logger: logger); + Future> resolve({ + required Logger? logger, + UserConfig? userConfig, + Map? environment, + }) async { + final xcrunInstances = await xcrun.defaultResolver!.resolve(logger: logger, environment: environment); return [ for (final xcrunInstance in xcrunInstances) ...[ - ...await tryResolveSdk(xcrunInstance: xcrunInstance, sdk: 'macosx', tool: macosxSdk, logger: logger), + ...await tryResolveSdk( + xcrunInstance: xcrunInstance, + sdk: 'macosx', + tool: macosxSdk, + logger: logger, + environment: environment, + ), ...await tryResolveSdk( xcrunInstance: xcrunInstance, sdk: 'iphoneos', tool: iPhoneOSSdk, logger: logger, + environment: environment, ), ...await tryResolveSdk( xcrunInstance: xcrunInstance, sdk: 'iphonesimulator', tool: iPhoneSimulatorSdk, logger: logger, + environment: environment, ), ], // xcrun --sdk macosx --show-sdk-path) @@ -61,11 +73,13 @@ class XCodeSdkResolver implements ToolResolver { required String sdk, required Tool tool, required Logger? logger, + Map? environment, }) async { final result = await runProcess( executable: xcrunInstance.uri, arguments: ['--sdk', sdk, '--show-sdk-path'], logger: logger, + environment: environment, ); if (result.exitCode == 1) { assert(result.stderr.contains('cannot be located')); diff --git a/lib/src/tool/tool_resolver.dart b/lib/src/tool/tool_resolver.dart index 2c87ad4..0a9f8ba 100644 --- a/lib/src/tool/tool_resolver.dart +++ b/lib/src/tool/tool_resolver.dart @@ -21,7 +21,11 @@ import 'tool_instance.dart'; abstract class ToolResolver { /// Resolves tools on the host system. - Future> resolve({required Logger? logger, UserConfig? userConfig}); + Future> resolve({ + required Logger? logger, + UserConfig? userConfig, + Map? environment, + }); } /// Tries to resolve a tool on the `PATH`. @@ -39,11 +43,15 @@ class PathToolResolver extends ToolResolver { /// Resolves [toolName] on the `PATH`. /// [userConfig] is ignored. @override - Future> resolve({required Logger? logger, UserConfig? userConfig}) async { + Future> resolve({ + required Logger? logger, + UserConfig? userConfig, + Map? environment, + }) async { logger?.finer('Looking for $toolName on PATH.'); - final uri = await runWhich(logger: logger); + final uri = await runWhich(logger: logger, environment: environment); if (uri == null) { - logger?.fine('Did not find $toolName on PATH.'); + logger?.fine('Did not find $toolName on PATH.'); return []; } final toolInstances = [ @@ -58,8 +66,8 @@ class PathToolResolver extends ToolResolver { static Uri get which => Uri.file(Platform.isWindows ? 'where' : 'which'); - Future runWhich({required Logger? logger}) async { - final process = await runProcess(executable: which, arguments: [executableName], logger: logger); + Future runWhich({required Logger? logger, Map? environment}) async { + final process = await runProcess(executable: which, arguments: [executableName], logger: logger, environment: environment); if (process.exitCode == 0) { final file = File(LineSplitter.split(process.stdout).first); final uri = File(await file.resolveSymbolicLinks()).uri; @@ -87,8 +95,16 @@ class CliVersionResolver implements ToolResolver { }); @override - Future> resolve({required Logger? logger, UserConfig? userConfig}) async { - final toolInstances = await wrappedResolver.resolve(logger: logger, userConfig: userConfig); + Future> resolve({ + required Logger? logger, + UserConfig? userConfig, + Map? environment, + }) async { + final toolInstances = await wrappedResolver.resolve( + logger: logger, + userConfig: userConfig, + environment: environment, + ); return [ for (final toolInstance in toolInstances) await lookupVersion( @@ -115,7 +131,7 @@ class CliVersionResolver implements ToolResolver { logger: logger, ); final result = toolInstance.copyWith(version: version); - logger?.fine('Found version for $result.'); + logger?.fine('Found version for $result. $version'); return result; } @@ -125,7 +141,11 @@ class CliVersionResolver implements ToolResolver { List arguments = const ['--version'], int expectedExitCode = 0, }) async { - final process = await runProcess(executable: executable, arguments: arguments, logger: logger); + final process = await runProcess( + executable: executable, + arguments: arguments, + logger: logger, + ); if (process.exitCode != expectedExitCode) { final executablePath = executable.toFilePath(); throw ToolError( @@ -143,8 +163,16 @@ class PathVersionResolver implements ToolResolver { PathVersionResolver({required this.wrappedResolver}); @override - Future> resolve({required Logger? logger, UserConfig? userConfig}) async { - final toolInstances = await wrappedResolver.resolve(logger: logger, userConfig: userConfig); + Future> resolve({ + required Logger? logger, + UserConfig? userConfig, + Map? environment, + }) async { + final toolInstances = await wrappedResolver.resolve( + logger: logger, + userConfig: userConfig, + environment: environment, + ); return [for (final toolInstance in toolInstances) lookupVersion(toolInstance)]; } @@ -170,8 +198,16 @@ class ToolResolvers implements ToolResolver { ToolResolvers(this.resolvers); @override - Future> resolve({required Logger? logger, UserConfig? userConfig}) async => [ - for (final resolver in resolvers) ...await resolver.resolve(logger: logger, userConfig: userConfig), + Future> resolve({ + required Logger? logger, + UserConfig? userConfig, + Map? environment, + }) async => [ + for (final resolver in resolvers) ...await resolver.resolve( + logger: logger, + userConfig: userConfig, + environment: environment, + ), ]; } @@ -188,7 +224,11 @@ class InstallLocationResolver implements ToolResolver { /// [userConfig] is ignored. @override - Future> resolve({required Logger? logger, UserConfig? userConfig}) async { + Future> resolve({ + required Logger? logger, + UserConfig? userConfig, + Map? environment, + }) async { logger?.finer('Looking for $toolName in $paths.'); final resolvedPaths = [for (final path in paths) ...await tryResolvePath(path)]; final toolInstances = [ @@ -248,8 +288,16 @@ class RelativeToolResolver implements ToolResolver { RelativeToolResolver({required this.toolName, required this.wrappedResolver, required this.relativePath}); @override - Future> resolve({required Logger? logger, UserConfig? userConfig}) async { - final otherToolInstances = await wrappedResolver.resolve(logger: logger, userConfig: userConfig); + Future> resolve({ + required Logger? logger, + UserConfig? userConfig, + Map? environment, + }) async { + final otherToolInstances = await wrappedResolver.resolve( + logger: logger, + userConfig: userConfig, + environment: environment, + ); logger?.finer( 'Looking for $toolName relative to $otherToolInstances ' @@ -291,8 +339,16 @@ class CliFilter implements ToolResolver { CliFilter({required this.wrappedResolver, required this.cliArguments, required this.keepIf}); @override - Future> resolve({required Logger? logger, UserConfig? userConfig}) async { - final toolInstances = await wrappedResolver.resolve(logger: logger, userConfig: userConfig); + Future> resolve({ + required Logger? logger, + UserConfig? userConfig, + Map? environment, + }) async { + final toolInstances = await wrappedResolver.resolve( + logger: logger, + userConfig: userConfig, + environment: environment, + ); return [ for (final toolInstance in toolInstances) await filter(toolInstance, logger: logger), ].whereType().toList(); From 96d00d55f1fc42539682408defbd9658b6385987 Mon Sep 17 00:00:00 2001 From: Gold87 <91761103+Gold872@users.noreply.github.com> Date: Mon, 2 Feb 2026 17:37:59 -0500 Subject: [PATCH 2/3] Don't run commands in shell --- lib/src/utils/run_process.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/src/utils/run_process.dart b/lib/src/utils/run_process.dart index af8dc1a..3b156b9 100644 --- a/lib/src/utils/run_process.dart +++ b/lib/src/utils/run_process.dart @@ -39,7 +39,7 @@ Future runProcess({ arguments, workingDirectory: workingDirectory?.toFilePath(), environment: environment, - runInShell: Platform.isWindows && workingDirectory != null, + runInShell: false, ); final stdoutSub = process.stdout.listen( @@ -121,7 +121,7 @@ RunProcessResult runProcessSync({ arguments, workingDirectory: workingDirectory?.toFilePath(), environment: environment, - runInShell: Platform.isWindows && workingDirectory != null, + runInShell: false, ); final runResult = RunProcessResult( pid: result.pid, From 3c74dba6ef5d7429e2ec94384c345b68a36c581b Mon Sep 17 00:00:00 2001 From: rainy liu Date: Tue, 3 Feb 2026 11:28:20 +0800 Subject: [PATCH 3/3] bump version to 0.2.3, update changelog --- CHANGELOG.md | 6 ++++++ pubspec.yaml | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e72fe42..0379757 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # native_toolchain_cmake +## 0.2.3 + +- new: support skipping generate if cached +- feat: check for prelrease VS version if no stable version is found +- fix: pass environment variables from vcvars.bat to resolvers to allow finding cmake not in PATH + ## 0.2.2 - find all available visual studio versions diff --git a/pubspec.yaml b/pubspec.yaml index 95f1e5f..27e9012 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: native_toolchain_cmake description: >- A library to invoke and build CMake projects for Dart Native Assets. -version: 0.2.2 +version: 0.2.3 repository: https://github.com/rainyl/native_toolchain_cmake topics: