Skip to content

Commit 63a9d41

Browse files
committed
use hierarchical user_defines configuration, add targetOS to UserConfig, optimize cmake and ninja filtering, more tests
1 parent 92f0a37 commit 63a9d41

File tree

16 files changed

+252
-158
lines changed

16 files changed

+252
-158
lines changed

example/add/pubspec.yaml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,15 @@ dev_dependencies:
1919
lints: ^5.0.0
2020
test: ^1.24.0
2121
ffigen: ^18.1.0
22+
23+
hooks:
24+
user_defines:
25+
add:
26+
env_file: null # ".env"
27+
cmake_version: null # "3.22.1"
28+
ninja_version: null # "1.10.2"
29+
prefer_android_cmake: null # true # defaults to true for android
30+
prefer_android_ninja: null # true # defaults to true for android
31+
android:
32+
android_home: null # "C:\\Android\\Sdk" # can be set in .env file
33+
ndk_version: null # "28.2.13676358"

example/flutter/pubspec.yaml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,3 +89,15 @@ flutter:
8989
#
9090
# For details regarding fonts from package dependencies,
9191
# see https://flutter.dev/to/font-from-package
92+
93+
hooks:
94+
user_defines:
95+
add:
96+
env_file: null # ".env"
97+
cmake_version: null # "3.22.1"
98+
ninja_version: null # "1.10.2"
99+
prefer_android_cmake: null # true # defaults to true for android
100+
prefer_android_ninja: null # true # defaults to true for android
101+
android:
102+
android_home: null # "C:\\Android\\Sdk" # can be set in .env file
103+
ndk_version: null # "28.2.13676358"

lib/src/builder/builder.dart

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -216,19 +216,32 @@ class CMakeBuilder implements Builder {
216216
}
217217
await Directory.fromUri(outDir ?? input.outputDirectory).create(recursive: true);
218218

219+
// hooks:
220+
// user_defines:
221+
// <package_name_that_use_native_toolchain_cmake>:
222+
// env_file: ".env"
223+
// cmake_version: "3.22.1"
224+
// ninja_version: "1.10.2"
225+
// prefer_android_cmake: false # defaults to true for android
226+
// prefer_android_ninja: false # defaults to true for android
227+
// android:
228+
// android_home: "C:\\Android\\Sdk" # can be set in .env file
229+
// ndk_version: "28.2.13676358"
230+
final androidConfig = input.userDefines["android"] as Map<String, dynamic>?;
231+
219232
var userConfig = UserConfig(
220-
androidHome: input.userDefines["androidHome"] as String?,
221-
cmakeVersion: input.userDefines["cmakeVersion"] as String?,
222-
ninjaVersion: input.userDefines["ninjaVersion"] as String?,
223-
ndkVersion: input.userDefines["ndkVersion"] as String?,
224-
preferAndroidNinja: input.userDefines["preferAndroidNinja"] as bool? ?? false,
225-
preferAndroidCmake: input.userDefines["preferAndroidCmake"] as bool? ?? false,
226-
androidTargetCmakeVersion: input.userDefines["androidTargetCmakeVersion"] as String?,
227-
androidTargetNinjaVersion: input.userDefines["androidTargetNinjaVersion"] as String?,
233+
targetOS: input.config.code.targetOS,
234+
cmakeVersion: input.userDefines["cmake_version"] as String?,
235+
ninjaVersion: input.userDefines["ninja_version"] as String?,
236+
ndkVersion: androidConfig?["ndk_version"] as String?,
237+
androidHome: androidConfig?["android_home"] as String?,
238+
preferAndroidNinja: input.userDefines["prefer_android_ninja"] as bool?,
239+
preferAndroidCmake: input.userDefines["prefer_android_cmake"] as bool?,
228240
);
229241

230242
// optional host specific build config
231-
final envFile = input.userDefines["envFile"] as String?;
243+
final envFile = input.userDefines["env_file"] as String?;
244+
232245
if (envFile != null) {
233246
final userEnvConfig = await getUserEnvConfig(input: input, envFile: envFile);
234247
final androidHome = userEnvConfig['ANDROID_HOME'];
@@ -256,8 +269,8 @@ class CMakeBuilder implements Builder {
256269
targets: targets,
257270
appleArgs: appleArgs,
258271
androidArgs: androidArgs,
259-
userConfig: userConfig,
260272
logLevel: logLevel,
273+
userConfig: userConfig,
261274
);
262275

263276
// Do not remove this line for potential extra variables in the future

lib/src/builder/run_builder.dart

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,6 @@ class RunCMakeBuilder {
6666
required this.input,
6767
required this.codeConfig,
6868
required this.sourceDir,
69-
Uri? outputDir,
7069
this.logger,
7170
this.defines = const {},
7271
this.generator = Generator.defaultGenerator,
@@ -75,19 +74,21 @@ class RunCMakeBuilder {
7574
this.targets,
7675
this.androidArgs = const AndroidBuilderArgs(),
7776
this.appleArgs = const AppleBuilderArgs(),
78-
this.userConfig = const UserConfig(),
7977
this.logLevel = LogLevel.STATUS,
80-
}) : outDir = outputDir ?? input.outputDirectory;
78+
Uri? outputDir,
79+
UserConfig? userConfig,
80+
}) : outDir = outputDir ?? input.outputDirectory,
81+
userConfig = userConfig ?? UserConfig(targetOS: codeConfig.targetOS);
8182

8283
Future<Uri> cmakePath() async {
83-
final cmakeTools = await cmake.defaultResolver?.resolve(logger: logger, userConfig: userConfig, codeConfig: codeConfig);
84+
final cmakeTools = await cmake.defaultResolver?.resolve(logger: logger, userConfig: userConfig);
8485
final path = cmakeTools?.first.uri;
8586
assert(path != null);
8687
return Future.value(path);
8788
}
8889

8990
Future<Uri> ninjaPath() async {
90-
final ninjaTools = await ninja.defaultResolver?.resolve(logger: logger, userConfig: userConfig, codeConfig: codeConfig);
91+
final ninjaTools = await ninja.defaultResolver?.resolve(logger: logger, userConfig: userConfig);
9192
final path = ninjaTools?.first.uri;
9293
assert(path != null);
9394
return Future.value(path);

lib/src/builder/user_config.dart

Lines changed: 37 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,64 @@
11
// Copyright (c) 2025, jasaw and rainyl. All rights reserved. Use of this source code is governed by a
22
// Apache-2.0 license that can be found in the LICENSE file.
33

4+
import 'dart:io';
5+
6+
import 'package:code_assets/code_assets.dart';
7+
48
class UserConfig {
9+
final OS targetOS;
10+
11+
/// for [OS.android], i.e., ANDROID_HOME, will try to load from environment variable if not specified.
512
final String? androidHome;
13+
14+
/// for [OS.android], if not specified, use the latest one
15+
final String? ndkVersion;
16+
17+
/// for [OS.current], if not specified, use the latest one
18+
/// for [OS.android], [preferAndroidCmake] is assumed to be true by default, will try to use
19+
/// the android cmake if available, explicitly set it to false if you want to use the system cmake.
620
final String? cmakeVersion;
21+
22+
/// whether to prefer android cmake if available, for [OS.android], it is assumed to be true by default.
23+
/// explicitly set it to true for other platforms if you still want to use android cmake.
724
final bool preferAndroidCmake;
8-
final String? androidTargetCmakeVersion;
25+
26+
/// for [OS.current], if not specified, use the latest one
27+
/// for [OS.android], [preferAndroidNinja] is assumed to be true by default, will try to use
28+
/// the android ninja if available, explicitly set it to false if you want to use the system ninja.
929
final String? ninjaVersion;
30+
31+
/// whether to prefer android ninja if available, for [OS.android], it is assumed to be true by default.
32+
/// explicitly set it to true for other platforms if you still want to use android ninja.
1033
final bool preferAndroidNinja;
11-
final String? androidTargetNinjaVersion;
12-
final String? ndkVersion;
1334

14-
const UserConfig({
15-
this.androidHome,
35+
UserConfig({
36+
required this.targetOS,
1637
this.cmakeVersion,
17-
this.preferAndroidCmake = false,
18-
this.androidTargetCmakeVersion,
19-
this.preferAndroidNinja = false,
2038
this.ninjaVersion,
21-
this.androidTargetNinjaVersion,
2239
this.ndkVersion,
23-
});
40+
String? androidHome,
41+
bool? preferAndroidCmake,
42+
bool? preferAndroidNinja,
43+
}) : preferAndroidCmake = preferAndroidCmake ?? targetOS == OS.android,
44+
preferAndroidNinja = preferAndroidNinja ?? targetOS == OS.android,
45+
androidHome = androidHome ?? Platform.environment['ANDROID_HOME'];
2446

2547
UserConfig copyWith({
26-
String? androidHome,
48+
OS? targetOS,
2749
String? cmakeVersion,
28-
bool preferAndroidCmake = false,
29-
String? androidTargetCmakeVersion,
3050
String? ninjaVersion,
31-
bool preferAndroidNinja = false,
32-
String? androidTargetNinjaVersion,
3351
String? ndkVersion,
52+
String? androidHome,
53+
bool preferAndroidCmake = false,
54+
bool preferAndroidNinja = false,
3455
}) => UserConfig(
56+
targetOS: targetOS ?? this.targetOS,
3557
androidHome: androidHome ?? this.androidHome,
3658
cmakeVersion: cmakeVersion ?? this.cmakeVersion,
3759
preferAndroidCmake: preferAndroidCmake,
38-
androidTargetCmakeVersion: androidTargetCmakeVersion ?? this.androidTargetCmakeVersion,
3960
ninjaVersion: ninjaVersion ?? this.ninjaVersion,
4061
preferAndroidNinja: preferAndroidNinja,
41-
androidTargetNinjaVersion: androidTargetNinjaVersion ?? this.androidTargetNinjaVersion,
4262
ndkVersion: ndkVersion ?? this.ndkVersion,
4363
);
4464
}

lib/src/native_toolchain/android_ndk.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ final androidNdkLld = Tool(name: lld.name, defaultResolver: _AndroidNdkResolver(
3131

3232
class _AndroidNdkResolver implements ToolResolver {
3333
@override
34-
Future<List<ToolInstance>> resolve({required Logger? logger, UserConfig? userConfig, CodeConfig? codeConfig}) async {
34+
Future<List<ToolInstance>> resolve({required Logger? logger, UserConfig? userConfig}) async {
3535
final installLocationResolver = PathVersionResolver(
3636
wrappedResolver: ToolResolvers([
3737
RelativeToolResolver(

lib/src/native_toolchain/cmake.dart

Lines changed: 26 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -18,19 +18,19 @@ class _CmakeResolver implements ToolResolver {
1818
final executableName = OS.current.executableFileName('cmake');
1919

2020
@override
21-
Future<List<ToolInstance>> resolve({required Logger? logger, UserConfig? userConfig, CodeConfig? codeConfig}) async {
21+
Future<List<ToolInstance>> resolve({required Logger? logger, UserConfig? userConfig}) async {
22+
// here, we always try to find android cmake first and filter out unsatisfied versions
2223
final androidResolver = CliVersionResolver(
2324
wrappedResolver: ToolResolvers([
24-
if ((userConfig?.preferAndroidCmake ?? false) || (codeConfig?.targetOS == OS.android))
25-
InstallLocationResolver(
26-
toolName: 'CMake',
27-
paths: [
28-
if (userConfig?.androidHome != null) '${userConfig?.androidHome}/cmake/*/bin/$executableName',
29-
if (Platform.isLinux) r'$HOME/Android/Sdk/cmake/*/bin/' + executableName,
30-
if (Platform.isMacOS) r'$HOME/Library/Android/sdk/cmake/*/bin/' + executableName,
31-
if (Platform.isWindows) r'$HOME/AppData/Local/Android/Sdk/cmake/*/bin/' + executableName,
32-
],
33-
),
25+
InstallLocationResolver(
26+
toolName: 'CMake',
27+
paths: [
28+
if (userConfig?.androidHome != null) '${userConfig?.androidHome}/cmake/*/bin/$executableName',
29+
if (Platform.isLinux) r'$HOME/Android/Sdk/cmake/*/bin/' + executableName,
30+
if (Platform.isMacOS) r'$HOME/Library/Android/sdk/cmake/*/bin/' + executableName,
31+
if (Platform.isWindows) r'$HOME/AppData/Local/Android/Sdk/cmake/*/bin/' + executableName,
32+
],
33+
),
3434
]),
3535
);
3636
final androidCmakeInstances = await androidResolver.resolve(logger: logger);
@@ -45,7 +45,7 @@ class _CmakeResolver implements ToolResolver {
4545
// sort latest version first
4646
androidCmakeInstances.sort((a, b) => a.version! > b.version! ? -1 : 1);
4747
final combinedCmakeInstances = <ToolInstance>[];
48-
if ((userConfig?.preferAndroidCmake ?? false) || (codeConfig?.targetOS == OS.android)) {
48+
if (userConfig?.preferAndroidCmake ?? userConfig?.targetOS == OS.android) {
4949
combinedCmakeInstances.addAll(androidCmakeInstances);
5050
}
5151
combinedCmakeInstances.addAll(systemCmakeInstances);
@@ -55,29 +55,25 @@ class _CmakeResolver implements ToolResolver {
5555
}
5656
}
5757

58-
String? specificCmakeVersion;
59-
if (codeConfig?.targetOS == OS.android && userConfig?.androidTargetCmakeVersion != null) {
60-
specificCmakeVersion = userConfig?.androidTargetCmakeVersion;
61-
} else {
62-
specificCmakeVersion = userConfig?.cmakeVersion;
63-
}
64-
58+
final specificCmakeVersion = userConfig?.cmakeVersion;
6559
if (specificCmakeVersion != null) {
6660
final cmakeVer = Version.parse(specificCmakeVersion);
6761
logger?.info('Filtering CMake version: $cmakeVer');
68-
logger?.info('Found CMake: ${combinedCmakeInstances.map((e) => e.toString()).join(', ')}');
6962
// cmake version of android are likely to be the format of `3.22.1-g37088a8-dirty`
7063
// so here we just check the major, minor and patch version
71-
combinedCmakeInstances.removeWhere((instance) {
72-
return instance.version == null ||
73-
(instance.version!.major != cmakeVer.major &&
74-
instance.version!.minor != cmakeVer.minor &&
75-
instance.version!.patch != cmakeVer.patch);
76-
});
77-
if (combinedCmakeInstances.isEmpty) {
78-
logger?.severe('Failed to find cmake version: $specificCmakeVersion');
79-
throw Exception('Failed to find cmake version: $specificCmakeVersion');
80-
}
64+
combinedCmakeInstances.removeWhere(
65+
(instance) =>
66+
instance.version == null ||
67+
instance.version?.major != cmakeVer.major ||
68+
instance.version?.minor != cmakeVer.minor ||
69+
instance.version?.patch != cmakeVer.patch,
70+
);
71+
}
72+
73+
logger?.info('Found CMake: ${combinedCmakeInstances.map((e) => e.toString()).join(', ')}');
74+
if (combinedCmakeInstances.isEmpty) {
75+
logger?.severe('Failed to find cmake with version=${specificCmakeVersion ?? 'latest'}');
76+
throw Exception('Failed to find cmake version: ${specificCmakeVersion ?? 'latest'}');
8177
}
8278

8379
return combinedCmakeInstances;

lib/src/native_toolchain/msvc.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -250,8 +250,8 @@ Tool _msvcTool({
250250

251251
class VisualStudioResolver implements ToolResolver {
252252
@override
253-
Future<List<ToolInstance>> resolve({required Logger? logger, UserConfig? userConfig, CodeConfig? codeConfig}) async {
254-
final vswhereInstances = await vswhere.defaultResolver!.resolve(logger: logger, userConfig: userConfig, codeConfig: codeConfig);
253+
Future<List<ToolInstance>> resolve({required Logger? logger, UserConfig? userConfig}) async {
254+
final vswhereInstances = await vswhere.defaultResolver!.resolve(logger: logger, userConfig: userConfig);
255255

256256
final result = <ToolInstance>[];
257257
for (final vswhereInstance in vswhereInstances.take(1)) {

lib/src/native_toolchain/ninja.dart

Lines changed: 30 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -17,19 +17,18 @@ class _NinjaResolver implements ToolResolver {
1717
final executableName = OS.current.executableFileName('ninja');
1818

1919
@override
20-
Future<List<ToolInstance>> resolve({required Logger? logger, UserConfig? userConfig, CodeConfig? codeConfig}) async {
20+
Future<List<ToolInstance>> resolve({required Logger? logger, UserConfig? userConfig}) async {
2121
final androidResolver = CliVersionResolver(
2222
wrappedResolver: ToolResolvers([
23-
if ((userConfig?.preferAndroidNinja ?? false) || (codeConfig?.targetOS == OS.android))
24-
InstallLocationResolver(
25-
toolName: 'Ninja',
26-
paths: [
27-
if (userConfig?.androidHome != null) '${userConfig?.androidHome}/cmake/*/bin/$executableName',
28-
if (Platform.isLinux) r'$HOME/Android/Sdk/cmake/*/bin/' + executableName,
29-
if (Platform.isMacOS) r'$HOME/Library/Android/sdk/cmake/*/bin/' + executableName,
30-
if (Platform.isWindows) r'$HOME/AppData/Local/Android/Sdk/cmake/*/bin/' + executableName,
31-
],
32-
),
23+
InstallLocationResolver(
24+
toolName: 'Ninja',
25+
paths: [
26+
if (userConfig?.androidHome != null) '${userConfig?.androidHome}/cmake/*/bin/$executableName',
27+
if (Platform.isLinux) r'$HOME/Android/Sdk/cmake/*/bin/' + executableName,
28+
if (Platform.isMacOS) r'$HOME/Library/Android/sdk/cmake/*/bin/' + executableName,
29+
if (Platform.isWindows) r'$HOME/AppData/Local/Android/Sdk/cmake/*/bin/' + executableName,
30+
],
31+
),
3332
]),
3433
);
3534
final androidNinjaInstances = await androidResolver.resolve(logger: logger);
@@ -44,30 +43,33 @@ class _NinjaResolver implements ToolResolver {
4443
// sort latest version first
4544
androidNinjaInstances.sort((a, b) => a.version! > b.version! ? -1 : 1);
4645
final combinedNinjaInstances = <ToolInstance>[];
47-
if ((userConfig?.preferAndroidNinja ?? false) || (codeConfig?.targetOS == OS.android)) {
46+
if (userConfig?.preferAndroidNinja ?? userConfig?.targetOS == OS.android) {
4847
combinedNinjaInstances.addAll(androidNinjaInstances);
4948
}
5049
combinedNinjaInstances.addAll(systemNinjaInstances);
51-
52-
String? specificNinjaVersion;
53-
if (codeConfig?.targetOS == OS.android && userConfig?.androidTargetNinjaVersion != null) {
54-
specificNinjaVersion = userConfig?.androidTargetNinjaVersion;
55-
} else {
56-
specificNinjaVersion = userConfig?.ninjaVersion;
50+
for (final instance in combinedNinjaInstances) {
51+
if (instance.version == null) {
52+
logger?.warning('Can not determine version of: $instance');
53+
}
5754
}
5855

56+
final specificNinjaVersion = userConfig?.ninjaVersion;
5957
if (specificNinjaVersion != null) {
6058
final ninjaVer = Version.parse(specificNinjaVersion);
61-
combinedNinjaInstances.removeWhere((instance) {
62-
return instance.version == null ||
63-
(instance.version!.major != ninjaVer.major &&
64-
instance.version!.minor != ninjaVer.minor &&
65-
instance.version!.patch != ninjaVer.patch);
66-
});
67-
if (combinedNinjaInstances.isEmpty) {
68-
logger?.severe('Failed to find ninja version: $specificNinjaVersion');
69-
throw Exception('Failed to find ninja version: $specificNinjaVersion');
70-
}
59+
logger?.info('Filtering Ninja version: $ninjaVer');
60+
combinedNinjaInstances.removeWhere(
61+
(instance) =>
62+
instance.version == null ||
63+
instance.version!.major != ninjaVer.major ||
64+
instance.version!.minor != ninjaVer.minor ||
65+
instance.version!.patch != ninjaVer.patch,
66+
);
67+
}
68+
69+
logger?.info('Found Ninja: ${combinedNinjaInstances.map((e) => e.toString()).join(', ')}');
70+
if (combinedNinjaInstances.isEmpty) {
71+
logger?.severe('Failed to find ninja with version=${specificNinjaVersion ?? 'latest'}');
72+
throw Exception('Failed to find ninja version: ${specificNinjaVersion ?? 'latest'}');
7173
}
7274

7375
return combinedNinjaInstances;

0 commit comments

Comments
 (0)