Skip to content

Commit fdd79e6

Browse files
committed
refactor: split up some of the build runner logic
1 parent 1253793 commit fdd79e6

11 files changed

+321
-190
lines changed

lib/native_toolchain_rs.dart

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ import 'package:code_assets/code_assets.dart';
22
import 'package:hooks/hooks.dart';
33
import 'package:logging/logging.dart';
44
import 'package:meta/meta.dart';
5+
import 'package:native_toolchain_rs/src/build_environment.dart';
56
import 'package:native_toolchain_rs/src/build_runner.dart';
7+
import 'package:native_toolchain_rs/src/crate_info_validator.dart';
68
import 'package:native_toolchain_rs/src/crate_resolver.dart';
79
import 'package:native_toolchain_rs/src/process_runner.dart';
810
import 'package:native_toolchain_rs/src/toml_parsing.dart';
@@ -97,15 +99,19 @@ final class RustBuilder {
9799
logger,
98100
tomlDocumentWrapperFactory,
99101
);
102+
const buildEnvironmentFactory = BuildEnvironmentFactory();
103+
final crateInfoValidator = CrateInfoValidator(
104+
toolchainTomlParser: toolchainTomlParser,
105+
cargoManifestParser: cargoManifestParser,
106+
);
100107

101108
return RustBuildRunner(
102109
config: this,
103110
logger: logger,
104111
processRunner: processRunner,
105112
crateDirectoryResolver: crateDirectoryResolver,
106-
tomlDocumentWrapperFactory: tomlDocumentWrapperFactory,
107-
cargoManifestParser: cargoManifestParser,
108-
toolchainTomlParser: toolchainTomlParser,
113+
buildEnvironmentFactory: buildEnvironmentFactory,
114+
crateInfoValidator: crateInfoValidator,
109115
).run(input: input, output: output, assetRouting: assetRouting);
110116
}
111117
}

lib/src/build_environment.dart

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import 'dart:io';
2+
3+
import 'package:code_assets/code_assets.dart';
4+
import 'package:meta/meta.dart';
5+
import 'package:native_toolchain_rs/src/config_mapping.dart';
6+
import 'package:native_toolchain_rs/src/exception.dart';
7+
import 'package:path/path.dart' as path;
8+
9+
@internal
10+
interface class BuildEnvironmentFactory {
11+
const BuildEnvironmentFactory();
12+
13+
Map<String, String> createBuildEnvVars(CodeConfig codeConfig) {
14+
final CodeConfig(:targetOS, :cCompiler) = codeConfig;
15+
final targetTriple = codeConfig.targetTriple;
16+
final targetTripleEnvVar = targetTriple.replaceAll('-', '_');
17+
18+
String getBinary(String binaryName) {
19+
if (cCompiler == null) {
20+
throw UnsupportedError(
21+
'CCompilerConfig was not provided but is required for $targetTriple',
22+
);
23+
}
24+
25+
final binaryPath = path.join(
26+
path.dirname(path.fromUri(cCompiler.compiler)),
27+
OS.current.executableFileName(binaryName),
28+
);
29+
30+
if (!File(binaryPath).existsSync()) {
31+
throw RustValidationException([
32+
'Binary $binaryPath not found; is your installed compiler too old?',
33+
]);
34+
}
35+
36+
return binaryPath;
37+
}
38+
39+
return {
40+
// NOTE: XCode makes some injections into PATH that break host build
41+
// for crates with a build.rs
42+
// See also: https://github.com/irondash/native_toolchain_rust/issues/17
43+
if (Platform.isMacOS) ...{
44+
'PATH': Platform.environment['PATH']!
45+
.split(':')
46+
.where((e) => !e.contains('Contents/Developer/'))
47+
.join(':'),
48+
},
49+
50+
// NOTE: we need to point to NDK >=27 vended LLVM for Android.
51+
// The `${targetTriple}35-clang`s were introduced in NDK 27,
52+
// so using these binaries:
53+
// 1. Ensures we are using a compatible NDK
54+
// 2. Also fixes build issues when just using the `clang`s directly
55+
if (targetOS == OS.android) ...{
56+
'AR_$targetTripleEnvVar': getBinary('llvm-ar'),
57+
'CC_$targetTripleEnvVar': getBinary('${targetTriple}35-clang'),
58+
'CXX_$targetTripleEnvVar': getBinary('${targetTriple}35-clang++'),
59+
'CARGO_TARGET_${targetTripleEnvVar.toUpperCase()}_LINKER': getBinary(
60+
'${targetTriple}35-clang',
61+
),
62+
},
63+
};
64+
}
65+
}

lib/src/build_runner.dart

Lines changed: 17 additions & 168 deletions
Original file line numberDiff line numberDiff line change
@@ -3,33 +3,32 @@ import 'dart:io';
33
import 'package:code_assets/code_assets.dart';
44
import 'package:hooks/hooks.dart';
55
import 'package:logging/logging.dart';
6+
import 'package:meta/meta.dart';
67
import 'package:native_toolchain_rs/native_toolchain_rs.dart';
8+
import 'package:native_toolchain_rs/src/build_environment.dart';
9+
import 'package:native_toolchain_rs/src/config_mapping.dart';
10+
import 'package:native_toolchain_rs/src/crate_info_validator.dart';
711
import 'package:native_toolchain_rs/src/crate_resolver.dart';
812
import 'package:native_toolchain_rs/src/process_runner.dart';
9-
import 'package:native_toolchain_rs/src/toml_parsing.dart';
1013
import 'package:path/path.dart' as path;
1114

12-
// NOTE: this is an internal implementation detail
13-
// ignore_for_file: public_member_api_docs
14-
15-
final class RustBuildRunner {
15+
@internal
16+
interface class RustBuildRunner {
1617
const RustBuildRunner({
1718
required this.config,
1819
required this.logger,
1920
required this.crateDirectoryResolver,
20-
required this.tomlDocumentWrapperFactory,
21-
required this.toolchainTomlParser,
22-
required this.cargoManifestParser,
2321
required this.processRunner,
22+
required this.buildEnvironmentFactory,
23+
required this.crateInfoValidator,
2424
});
2525

2626
final RustBuilder config;
2727
final Logger? logger;
2828
final CrateDirectoryResolver crateDirectoryResolver;
29-
final TomlDocumentWrapperFactory tomlDocumentWrapperFactory;
30-
final ToolchainTomlParser toolchainTomlParser;
31-
final CargoManifestParser cargoManifestParser;
3229
final ProcessRunner processRunner;
30+
final BuildEnvironmentFactory buildEnvironmentFactory;
31+
final CrateInfoValidator crateInfoValidator;
3332

3433
Future<void> run({
3534
required BuildInput input,
@@ -49,7 +48,8 @@ final class RustBuildRunner {
4948
}
5049

5150
logger?.info('Gathering all data required for the build');
52-
final CodeConfig(:targetOS, :targetTriple, :linkMode) = input.config.code;
51+
final codeConfig = input.config.code;
52+
final CodeConfig(:targetOS, :targetTriple, :linkMode) = codeConfig;
5353
final RustBuilder(
5454
:assetName,
5555
:cratePath,
@@ -65,7 +65,10 @@ final class RustBuildRunner {
6565
);
6666
final outputDir = path.join(path.fromUri(input.outputDirectory), 'target');
6767
final manifestPath = path.join(crateDirectory.path, 'Cargo.toml');
68-
final (:crateName, :toolchainChannel) = fetchAndValidateCrateInfo(
68+
final (
69+
:crateName,
70+
:toolchainChannel,
71+
) = crateInfoValidator.fetchAndValidateCrateInfo(
6972
targetTriple: targetTriple,
7073
manifestPath: manifestPath,
7174
toolchainTomlPath: path.join(crateDirectory.path, 'rust-toolchain.toml'),
@@ -98,7 +101,7 @@ final class RustBuildRunner {
98101
...extraCargoBuildArgs,
99102
],
100103
environment: {
101-
...createBuildEnvVars(input.config.code),
104+
...buildEnvironmentFactory.createBuildEnvVars(codeConfig),
102105
...extraCargoEnvironmentVariables,
103106
},
104107
);
@@ -133,158 +136,4 @@ final class RustBuildRunner {
133136
], workingDirectory: crateDirectory),
134137
);
135138
}
136-
137-
({String crateName, String toolchainChannel}) fetchAndValidateCrateInfo({
138-
required String manifestPath,
139-
required String toolchainTomlPath,
140-
required String targetTriple,
141-
}) {
142-
final [
143-
String crateName,
144-
String toolchainChannel,
145-
] = RustValidationException.compose<dynamic>([
146-
() {
147-
final (:crateName, :libCrateTypes) = cargoManifestParser.parseManifest(
148-
manifestPath,
149-
);
150-
151-
const requiredTypes = ['staticlib', 'cdylib'];
152-
if (!requiredTypes.every(libCrateTypes.contains)) {
153-
throw RustValidationException([
154-
'Cargo.toml must specify $requiredTypes under lib.crate-types',
155-
]);
156-
}
157-
158-
return crateName;
159-
},
160-
() {
161-
final (:channel, :targets) = toolchainTomlParser.parseToolchainToml(
162-
toolchainTomlPath,
163-
);
164-
165-
final toolchainIssues = <String>[];
166-
167-
const deniedChannels = {'stable', 'beta', 'nightly'};
168-
if (deniedChannels.contains(channel)) {
169-
toolchainIssues.add(
170-
'Your current channel in rust-toolchain.toml is $channel; '
171-
'this is dangerous and consequently is not allowed! '
172-
'Please specify an exact version to fix this issue.',
173-
);
174-
}
175-
176-
if (!targets.contains(targetTriple)) {
177-
toolchainIssues.add(
178-
'$targetTriple is not one of the supported targets: $targets',
179-
);
180-
}
181-
182-
return channel;
183-
},
184-
]);
185-
186-
return (crateName: crateName, toolchainChannel: toolchainChannel);
187-
}
188-
189-
Map<String, String> createBuildEnvVars(CodeConfig codeConfig) {
190-
final CodeConfig(:targetOS, :targetTriple, :cCompiler) = codeConfig;
191-
final targetTripleEnvVar = targetTriple.replaceAll('-', '_');
192-
193-
String getBinary(String binaryName) {
194-
if (cCompiler == null) {
195-
throw UnsupportedError(
196-
'CCompilerConfig was not provided but is required for $targetTriple',
197-
);
198-
}
199-
200-
final binaryPath = path.join(
201-
path.dirname(path.fromUri(cCompiler.compiler)),
202-
OS.current.executableFileName(binaryName),
203-
);
204-
205-
if (!File(binaryPath).existsSync()) {
206-
throw RustValidationException([
207-
'Binary $binaryPath not found; is your installed compiler too old?',
208-
]);
209-
}
210-
211-
return binaryPath;
212-
}
213-
214-
return {
215-
// NOTE: XCode makes some injections into PATH that break host build
216-
// for crates with a build.rs
217-
// See also: https://github.com/irondash/native_toolchain_rust/issues/17
218-
if (Platform.isMacOS) ...{
219-
'PATH': Platform.environment['PATH']!
220-
.split(':')
221-
.where((e) => !e.contains('Contents/Developer/'))
222-
.join(':'),
223-
},
224-
225-
// NOTE: we need to point to NDK >=27 vended LLVM for Android.
226-
// The `${targetTriple}35-clang`s were introduced in NDK 27,
227-
// so using these binaries:
228-
// 1. Ensures we are using a compatible NDK
229-
// 2. Also fixes build issues when just using the `clang`s directly
230-
if (targetOS == OS.android) ...{
231-
'AR_$targetTripleEnvVar': getBinary('llvm-ar'),
232-
'CC_$targetTripleEnvVar': getBinary('${targetTriple}35-clang'),
233-
'CXX_$targetTripleEnvVar': getBinary('${targetTriple}35-clang++'),
234-
'CARGO_TARGET_${targetTripleEnvVar.toUpperCase()}_LINKER': getBinary(
235-
'${targetTriple}35-clang',
236-
),
237-
},
238-
};
239-
}
240-
}
241-
242-
extension on CodeConfig {
243-
String get targetTriple {
244-
return switch ((targetOS, targetArchitecture)) {
245-
// Android
246-
(OS.android, Architecture.arm64) => 'aarch64-linux-android',
247-
(OS.android, Architecture.arm) => 'armv7-linux-androideabi',
248-
(OS.android, Architecture.x64) => 'x86_64-linux-android',
249-
250-
// iOS
251-
(OS.iOS, Architecture.arm64)
252-
when iOS.targetSdk == IOSSdk.iPhoneSimulator =>
253-
'aarch64-apple-ios-sim',
254-
(OS.iOS, Architecture.arm64) when iOS.targetSdk == IOSSdk.iPhoneOS =>
255-
'aarch64-apple-ios',
256-
(OS.iOS, Architecture.arm64) => throw UnsupportedError(
257-
'Unknown IOSSdk: ${iOS.targetSdk}',
258-
),
259-
(OS.iOS, Architecture.x64) => 'x86_64-apple-ios',
260-
261-
// Windows
262-
(OS.windows, Architecture.arm64) => 'aarch64-pc-windows-msvc',
263-
(OS.windows, Architecture.x64) => 'x86_64-pc-windows-msvc',
264-
265-
// Linux
266-
(OS.linux, Architecture.arm64) => 'aarch64-unknown-linux-gnu',
267-
(OS.linux, Architecture.x64) => 'x86_64-unknown-linux-gnu',
268-
269-
// macOS
270-
(OS.macOS, Architecture.arm64) => 'aarch64-apple-darwin',
271-
(OS.macOS, Architecture.x64) => 'x86_64-apple-darwin',
272-
273-
(_, _) => throw UnsupportedError(
274-
'Unsupported target: $targetOS on $targetArchitecture',
275-
),
276-
};
277-
}
278-
279-
LinkMode get linkMode {
280-
return switch (linkModePreference) {
281-
LinkModePreference.dynamic ||
282-
LinkModePreference.preferDynamic => DynamicLoadingBundled(),
283-
LinkModePreference.static ||
284-
LinkModePreference.preferStatic => StaticLinking(),
285-
_ => throw UnsupportedError(
286-
'Unsupported LinkModePreference: $linkModePreference',
287-
),
288-
};
289-
}
290139
}

lib/src/config_mapping.dart

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import 'package:code_assets/code_assets.dart';
2+
import 'package:meta/meta.dart';
3+
4+
@internal
5+
extension CodeConfigMapping on CodeConfig {
6+
String get targetTriple {
7+
return switch ((targetOS, targetArchitecture)) {
8+
// Android
9+
(OS.android, Architecture.arm64) => 'aarch64-linux-android',
10+
(OS.android, Architecture.arm) => 'armv7-linux-androideabi',
11+
(OS.android, Architecture.x64) => 'x86_64-linux-android',
12+
13+
// iOS
14+
(OS.iOS, Architecture.arm64)
15+
when iOS.targetSdk == IOSSdk.iPhoneSimulator =>
16+
'aarch64-apple-ios-sim',
17+
(OS.iOS, Architecture.arm64) when iOS.targetSdk == IOSSdk.iPhoneOS =>
18+
'aarch64-apple-ios',
19+
(OS.iOS, Architecture.arm64) => throw UnsupportedError(
20+
'Unknown IOSSdk: ${iOS.targetSdk}',
21+
),
22+
(OS.iOS, Architecture.x64) => 'x86_64-apple-ios',
23+
24+
// Windows
25+
(OS.windows, Architecture.arm64) => 'aarch64-pc-windows-msvc',
26+
(OS.windows, Architecture.x64) => 'x86_64-pc-windows-msvc',
27+
28+
// Linux
29+
(OS.linux, Architecture.arm64) => 'aarch64-unknown-linux-gnu',
30+
(OS.linux, Architecture.x64) => 'x86_64-unknown-linux-gnu',
31+
32+
// macOS
33+
(OS.macOS, Architecture.arm64) => 'aarch64-apple-darwin',
34+
(OS.macOS, Architecture.x64) => 'x86_64-apple-darwin',
35+
36+
(_, _) => throw UnsupportedError(
37+
'Unsupported target: $targetOS on $targetArchitecture',
38+
),
39+
};
40+
}
41+
42+
LinkMode get linkMode {
43+
return switch (linkModePreference) {
44+
LinkModePreference.dynamic ||
45+
LinkModePreference.preferDynamic => DynamicLoadingBundled(),
46+
LinkModePreference.static ||
47+
LinkModePreference.preferStatic => StaticLinking(),
48+
_ => throw UnsupportedError(
49+
'Unsupported LinkModePreference: $linkModePreference',
50+
),
51+
};
52+
}
53+
}

0 commit comments

Comments
 (0)