Skip to content

Commit 5f50fb8

Browse files
authored
Initial version (#3)
1 parent 3cc9333 commit 5f50fb8

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

70 files changed

+4565
-43
lines changed

.github/workflows/c_compiler.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ jobs:
3232
- uses: dart-lang/setup-dart@d6a63dab3335f427404425de0fbfed4686d93c4f
3333
with:
3434
sdk: ${{matrix.sdk}}
35+
- uses: nttld/setup-ndk@deccd078bf3db957dbdee9862f51955b35ac81dd
36+
with:
37+
ndk-version: r25b
3538

3639
- run: dart pub get
3740

@@ -40,6 +43,9 @@ jobs:
4043
- run: dart format --output=none --set-exit-if-changed .
4144
if: ${{matrix.run-tests}}
4245

46+
- name: Install native toolchains
47+
run: sudo apt-get install clang-14 gcc-i686-linux-gnu gcc-aarch64-linux-gnu gcc-arm-linux-gnueabihf
48+
4349
- run: dart test
4450
if: ${{matrix.run-tests}}
4551

.github/workflows/native_assets_cli.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,22 @@ jobs:
3232
- uses: dart-lang/setup-dart@d6a63dab3335f427404425de0fbfed4686d93c4f
3333
with:
3434
sdk: ${{matrix.sdk}}
35+
- uses: nttld/setup-ndk@deccd078bf3db957dbdee9862f51955b35ac81dd
36+
with:
37+
ndk-version: r25b
3538

3639
- run: dart pub get
40+
- run: dart pub get
41+
working-directory: pkgs/native_assets_cli/example/native_add/
3742

3843
- run: dart analyze --fatal-infos
3944

4045
- run: dart format --output=none --set-exit-if-changed .
4146
if: ${{matrix.run-tests}}
4247

48+
- name: Install native toolchains
49+
run: sudo apt-get install clang-14 gcc-i686-linux-gnu gcc-aarch64-linux-gnu gcc-arm-linux-gnueabihf
50+
4351
- run: dart test
4452
if: ${{matrix.run-tests}}
4553

pkgs/c_compiler/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
[![package:c_compiler](https://github.com/dart-lang/native/actions/workflows/c_compiler.yml/badge.svg)](https://github.com/dart-lang/native/actions/workflows/c_compiler.yml)
1+
[![package:c_compiler](https://github.com/dart-lang/native/actions/workflows/c_compiler.yaml/badge.svg)](https://github.com/dart-lang/native/actions/workflows/c_compiler.yaml)
22
[![pub package](https://img.shields.io/pub/v/c_compiler.svg)](https://pub.dev/packages/c_compiler)
33
[![Coverage Status](https://coveralls.io/repos/github/dart-lang/native/badge.svg?branch=main)](https://coveralls.io/github/dart-lang/tools?branch=main)
44
<!-- [![package publisher](https://img.shields.io/pub/publisher/c_compiler.svg)](https://pub.dev/packages/c_compiler/publisher) -->

pkgs/c_compiler/lib/c_compiler.dart

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,11 @@
55
/// A library to invoke the native C compiler installed on the host machine.
66
library;
77

8-
export 'src/c_compiler_base.dart';
8+
export 'src/cbuilder/cbuilder.dart';
9+
export 'src/native_toolchain/android_ndk.dart';
10+
export 'src/native_toolchain/clang.dart';
11+
export 'src/native_toolchain/gcc.dart';
12+
export 'src/tool/tool.dart';
13+
export 'src/tool/tool_instance.dart';
14+
export 'src/tool/tool_requirement.dart';
15+
export 'src/tool/tool_resolver.dart';
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'dart:io';
6+
7+
import 'package:logging/logging.dart';
8+
import 'package:native_assets_cli/native_assets_cli.dart';
9+
10+
import 'run_cbuilder.dart';
11+
12+
abstract class Builder {
13+
Future<void> run({
14+
required BuildConfig buildConfig,
15+
required BuildOutput buildOutput,
16+
Logger? logger,
17+
});
18+
}
19+
20+
/// Specification for building an artifact with a C compiler.
21+
class CBuilder implements Builder {
22+
/// What kind of artifact to build.
23+
final _CBuilderType _type;
24+
25+
/// Name of the library or executable to build.
26+
///
27+
/// The filename will be decided by [BuildConfig.target] and
28+
/// [OS.libraryFileName] or [OS.executableFileName].
29+
///
30+
/// File will be placed in [BuildConfig.outDir].
31+
final String name;
32+
33+
/// Asset identifier.
34+
///
35+
/// Used to output the [BuildOutput.assets].
36+
///
37+
/// If omitted, no asset will be added to the build output.
38+
final String? assetName;
39+
40+
/// Sources to build the library or executable.
41+
///
42+
/// Resolved against [BuildConfig.packageRoot].
43+
///
44+
/// Used to output the [BuildOutput.dependencies].
45+
final List<String> sources;
46+
47+
/// The dart files involved in building this artifact.
48+
///
49+
/// Resolved against [BuildConfig.packageRoot].
50+
///
51+
/// Used to output the [BuildOutput.dependencies].
52+
final List<String> dartBuildFiles;
53+
54+
CBuilder.library({
55+
required this.name,
56+
required this.assetName,
57+
this.sources = const [],
58+
this.dartBuildFiles = const ['build.dart'],
59+
}) : _type = _CBuilderType.library;
60+
61+
CBuilder.executable({
62+
required this.name,
63+
this.sources = const [],
64+
this.dartBuildFiles = const ['build.dart'],
65+
}) : _type = _CBuilderType.executable,
66+
assetName = null;
67+
68+
@override
69+
Future<void> run({
70+
required BuildConfig buildConfig,
71+
required BuildOutput buildOutput,
72+
Logger? logger,
73+
}) async {
74+
logger ??= Logger('');
75+
final outDir = buildConfig.outDir;
76+
final packageRoot = buildConfig.packageRoot;
77+
await Directory.fromUri(outDir).create(recursive: true);
78+
final packaging = buildConfig.packaging.preferredPackaging.first;
79+
final libUri =
80+
outDir.resolve(buildConfig.target.os.libraryFileName(name, packaging));
81+
final exeUri =
82+
outDir.resolve(buildConfig.target.os.executableFileName(name));
83+
final sources = [
84+
for (final source in this.sources) packageRoot.resolve(source),
85+
];
86+
final dartBuildFiles = [
87+
for (final source in this.dartBuildFiles) packageRoot.resolve(source),
88+
];
89+
90+
final task = RunCBuilder(
91+
buildConfig: buildConfig,
92+
logger: logger,
93+
sources: sources,
94+
dynamicLibrary:
95+
_type == _CBuilderType.library && packaging == Packaging.dynamic
96+
? libUri
97+
: null,
98+
staticLibrary:
99+
_type == _CBuilderType.library && packaging == Packaging.static
100+
? libUri
101+
: null,
102+
executable: _type == _CBuilderType.executable ? exeUri : null,
103+
);
104+
await task.run();
105+
106+
if (assetName != null) {
107+
buildOutput.assets.add(Asset(
108+
name: assetName!,
109+
packaging: packaging,
110+
target: buildConfig.target,
111+
path: AssetAbsolutePath(libUri),
112+
));
113+
}
114+
buildOutput.dependencies.dependencies.addAll([
115+
...sources,
116+
...dartBuildFiles,
117+
]);
118+
}
119+
}
120+
121+
enum _CBuilderType {
122+
executable,
123+
library,
124+
}
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'dart:io';
6+
7+
import 'package:logging/logging.dart';
8+
import 'package:native_assets_cli/native_assets_cli.dart';
9+
10+
import '../native_toolchain/android_ndk.dart';
11+
import '../native_toolchain/clang.dart';
12+
import '../native_toolchain/gcc.dart';
13+
import '../native_toolchain/recognizer.dart';
14+
import '../tool/tool.dart';
15+
import '../tool/tool_error.dart';
16+
import '../tool/tool_instance.dart';
17+
18+
// TODO(dacoharkes): This should support alternatives.
19+
// For example use Clang or MSVC on Windows.
20+
class CompilerResolver {
21+
final BuildConfig buildConfig;
22+
final Logger? logger;
23+
final Target host;
24+
25+
CompilerResolver({
26+
required this.buildConfig,
27+
required this.logger,
28+
Target? host, // Only visible for testing.
29+
}) : host = host ?? Target.current;
30+
31+
Future<ToolInstance> resolveCompiler() async {
32+
// First, check if the launcher provided a direct path to the compiler.
33+
var result = await _tryLoadCompilerFromConfig(
34+
BuildConfig.ccConfigKey,
35+
(buildConfig) => buildConfig.cc,
36+
);
37+
38+
// Then, try to detect on the host machine.
39+
final tool = _selectCompiler();
40+
if (tool != null) {
41+
result ??= await _tryLoadToolFromNativeToolchain(tool);
42+
}
43+
44+
if (result != null) {
45+
return result;
46+
}
47+
48+
final target = buildConfig.target;
49+
final errorMessage =
50+
"No tools configured on host '$host' with target '$target'.";
51+
logger?.severe(errorMessage);
52+
throw ToolError(errorMessage);
53+
}
54+
55+
/// Select the right compiler for cross compiling to the specified target.
56+
Tool? _selectCompiler() {
57+
final target = buildConfig.target;
58+
59+
if (target == host) return clang;
60+
if (target.os == OS.android) return androidNdkClang;
61+
if (host.os == OS.linux) {
62+
switch (target) {
63+
case Target.linuxArm:
64+
return armLinuxGnueabihfGcc;
65+
case Target.linuxArm64:
66+
return aarch64LinuxGnuGcc;
67+
case Target.linuxIA32:
68+
return i686LinuxGnuGcc;
69+
}
70+
}
71+
72+
return null;
73+
}
74+
75+
Future<ToolInstance?> _tryLoadCompilerFromConfig(
76+
String configKey, Uri? Function(BuildConfig) getter) async {
77+
final configCcUri = getter(buildConfig);
78+
if (configCcUri != null) {
79+
assert(await File.fromUri(configCcUri).exists());
80+
logger?.finer('Using compiler ${configCcUri.path} '
81+
'from config[${BuildConfig.ccConfigKey}].');
82+
return (await CompilerRecognizer(configCcUri).resolve(logger: logger))
83+
.first;
84+
}
85+
logger?.finer('No compiler set in config[${BuildConfig.ccConfigKey}].');
86+
return null;
87+
}
88+
89+
Future<ToolInstance?> _tryLoadToolFromNativeToolchain(Tool tool) async {
90+
final resolved = (await tool.defaultResolver!.resolve(logger: logger))
91+
.where((i) => i.tool == tool)
92+
.toList()
93+
..sort();
94+
return resolved.isEmpty ? null : resolved.first;
95+
}
96+
97+
Future<ToolInstance> resolveArchiver() async {
98+
// First, check if the launcher provided a direct path to the compiler.
99+
var result = await _tryLoadArchiverFromConfig(
100+
BuildConfig.arConfigKey,
101+
(buildConfig) => buildConfig.ar,
102+
);
103+
104+
// Then, try to detect on the host machine.
105+
final tool = _selectArchiver();
106+
if (tool != null) {
107+
result ??= await _tryLoadToolFromNativeToolchain(tool);
108+
}
109+
110+
if (result != null) {
111+
return result;
112+
}
113+
114+
final target = buildConfig.target;
115+
final errorMessage =
116+
"No tools configured on host '$host' with target '$target'.";
117+
logger?.severe(errorMessage);
118+
throw ToolError(errorMessage);
119+
}
120+
121+
/// Select the right archiver for cross compiling to the specified target.
122+
Tool? _selectArchiver() {
123+
final target = buildConfig.target;
124+
125+
if (target == host) return llvmAr;
126+
if (target.os == OS.android) return androidNdkLlvmAr;
127+
if (host.os == OS.linux) {
128+
switch (target) {
129+
case Target.linuxArm:
130+
return armLinuxGnueabihfGccAr;
131+
case Target.linuxArm64:
132+
return aarch64LinuxGnuGccAr;
133+
case Target.linuxIA32:
134+
return i686LinuxGnuGccAr;
135+
}
136+
}
137+
138+
return null;
139+
}
140+
141+
Future<ToolInstance?> _tryLoadArchiverFromConfig(
142+
String configKey, Uri? Function(BuildConfig) getter) async {
143+
final configArUri = getter(buildConfig);
144+
if (configArUri != null) {
145+
assert(await File.fromUri(configArUri).exists());
146+
logger?.finer('Using archiver ${configArUri.path} '
147+
'from config[${BuildConfig.arConfigKey}].');
148+
return (await ArchiverRecognizer(configArUri).resolve(logger: logger))
149+
.first;
150+
}
151+
logger?.finer('No archiver set in config[${BuildConfig.arConfigKey}].');
152+
return null;
153+
}
154+
}

0 commit comments

Comments
 (0)