Skip to content

Commit fe942d1

Browse files
committed
Initial version
1 parent 7e2398e commit fe942d1

Some content is hidden

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

54 files changed

+3354
-50
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@v1
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@v1
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/lib/c_compiler.dart

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,10 @@
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' show androidNdk, androidNdkClang;
10+
export 'src/native_toolchain/clang.dart' show clang;
11+
export 'src/tool/tool.dart';
12+
export 'src/tool/tool_instance.dart';
13+
export 'src/tool/tool_requirement.dart';
14+
export 'src/tool/tool_resolver.dart';
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
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 'package:cli_config/cli_config.dart';
6+
import 'package:logging/logging.dart';
7+
8+
import '../utils/run_process.dart';
9+
import 'compiler_resolver.dart';
10+
import 'target.dart';
11+
12+
class CBuilder {
13+
final Config config;
14+
final Logger logger;
15+
final List<Uri> sources;
16+
final List<Uri> includePaths;
17+
final Uri? executable;
18+
final Uri? dynamicLibrary;
19+
final Uri? staticLibrary;
20+
final Uri outDir;
21+
final String target;
22+
23+
CBuilder({
24+
required this.config,
25+
required this.logger,
26+
this.sources = const [],
27+
this.includePaths = const [],
28+
this.executable,
29+
this.dynamicLibrary,
30+
this.staticLibrary,
31+
}) : outDir = config.path('out_dir'),
32+
target = config.optionalString('target') ?? Target.current() {
33+
if ([executable, dynamicLibrary, staticLibrary].whereType<Uri>().length !=
34+
1) {
35+
throw ArgumentError(
36+
'Provide one of executable, dynamicLibrary, or staticLibrary.');
37+
}
38+
}
39+
40+
Uri? _compilerCached;
41+
42+
Future<Uri> compiler() async {
43+
if (_compilerCached != null) {
44+
return _compilerCached!;
45+
}
46+
final resolver = CompilerResolver(config: config, logger: logger);
47+
_compilerCached = (await resolver.resolve()).first.uri;
48+
return _compilerCached!;
49+
}
50+
51+
Uri? _archiverCached;
52+
53+
Future<Uri> archiver() async {
54+
if (_archiverCached != null) {
55+
return _archiverCached!;
56+
}
57+
final compiler_ = await compiler();
58+
final resolver = CompilerResolver(config: config, logger: logger);
59+
_linkerCached = await resolver.resolveArchiver(
60+
compiler_,
61+
);
62+
return _linkerCached!;
63+
}
64+
65+
Uri? _linkerCached;
66+
67+
Future<Uri> linker() async {
68+
if (_linkerCached != null) {
69+
return _linkerCached!;
70+
}
71+
final compiler_ = await compiler();
72+
final resolver = CompilerResolver(config: config, logger: logger);
73+
_linkerCached = await resolver.resolveLinker(
74+
compiler_,
75+
);
76+
return _linkerCached!;
77+
}
78+
79+
Future<void> run() async {
80+
final compiler_ = await compiler();
81+
final isStaticLib = staticLibrary != null;
82+
Uri? archiver_;
83+
if (isStaticLib) {
84+
archiver_ = await archiver();
85+
}
86+
87+
await RunProcess(
88+
executable: compiler_.path,
89+
arguments: [
90+
if (target.startsWith('android')) ...[
91+
// TODO(dacoharkes): How to solve linking issues?
92+
// Workaround:
93+
'-nostartfiles',
94+
// Non-working fix: --sysroot=$NDKPATH/toolchains/llvm/prebuilt/linux-x86_64/sysroot.
95+
// The sysroot should be discovered automatically after NDK 22.
96+
'--target=${androidNdkClangTargetFlags[target]!}',
97+
],
98+
...sources.map((e) => e.path),
99+
if (executable != null) ...[
100+
'-o',
101+
outDir.resolveUri(executable!).path,
102+
],
103+
if (dynamicLibrary != null) ...[
104+
'--shared',
105+
'-o',
106+
outDir.resolveUri(dynamicLibrary!).path,
107+
] else if (staticLibrary != null) ...[
108+
'-c',
109+
'-o',
110+
outDir.resolve('out.o').path,
111+
],
112+
],
113+
).run(logger: logger);
114+
if (staticLibrary != null) {
115+
await RunProcess(
116+
executable: archiver_!.path,
117+
arguments: [
118+
'rc',
119+
outDir.resolveUri(staticLibrary!).path,
120+
outDir.resolve('out.o').path,
121+
],
122+
).run(logger: logger);
123+
}
124+
}
125+
126+
static const androidNdkClangTargetFlags = {
127+
Target.androidArm: 'armv7a-linux-androideabi',
128+
Target.androidArm64: 'aarch64-linux-android',
129+
Target.androidIA32: 'i686-linux-android',
130+
Target.androidX64: 'x86_64-linux-android',
131+
};
132+
}
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
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:cli_config/cli_config.dart';
8+
import 'package:logging/logging.dart';
9+
10+
import '../native_toolchain/android_ndk.dart';
11+
import '../native_toolchain/clang.dart';
12+
import '../tool/tool.dart';
13+
import '../tool/tool_instance.dart';
14+
import '../tool/tool_resolver.dart';
15+
import 'target.dart';
16+
17+
class CompilerResolver implements ToolResolver {
18+
final Config config;
19+
final Logger? logger;
20+
21+
CompilerResolver({
22+
required this.config,
23+
required this.logger,
24+
});
25+
26+
@override
27+
Future<List<ToolInstance>> resolve() async {
28+
final tool = selectCompiler();
29+
ToolInstance? result;
30+
result ??= await _tryLoadCompilerFromConfig(tool, _configKeyCC);
31+
result ??= await _tryLoadCompilerFromConfig(
32+
tool,
33+
_configKeyNativeToolchainClang,
34+
);
35+
result ??= await _tryLoadCompilerFromNativeToolchain(
36+
tool,
37+
);
38+
39+
if (result != null) {
40+
return [result];
41+
}
42+
const errorMessage = 'No C compiler found.';
43+
logger?.severe(errorMessage);
44+
throw Exception(errorMessage);
45+
}
46+
47+
/// Select the right compiler for cross compiling to the specified target.
48+
Tool selectCompiler() {
49+
final target = config.optionalString('target') ?? Target.current();
50+
switch (target) {
51+
case Target.linuxArm:
52+
return armLinuxGnueabihfGcc;
53+
case Target.linuxArm64:
54+
return aarch64LinuxGnuGcc;
55+
case Target.linuxIA32:
56+
return i686LinuxGnuGcc;
57+
case Target.linuxX64:
58+
return clang;
59+
case Target.androidArm:
60+
case Target.androidArm64:
61+
case Target.androidIA32:
62+
case Target.androidX64:
63+
return androidNdkClang;
64+
}
65+
throw Exception('No tool available for target: $target.');
66+
}
67+
68+
/// Provided by launchers.
69+
static const _configKeyCC = 'cc';
70+
71+
/// Provided by package:native_toolchain.
72+
static const _configKeyNativeToolchainClang = 'deps.native_toolchain.clang';
73+
74+
Future<ToolInstance?> _tryLoadCompilerFromConfig(
75+
Tool tool, String configKey) async {
76+
final configCcUri = config.optionalPath(_configKeyCC);
77+
if (configCcUri != null) {
78+
if (await File.fromUri(configCcUri).exists()) {
79+
logger?.finer(
80+
'Using compiler ${configCcUri.path} from config[$_configKeyCC].');
81+
return ToolInstance(tool: tool, uri: configCcUri);
82+
} else {
83+
logger?.warning(
84+
'Compiler ${configCcUri.path} from config[$_configKeyCC] does not '
85+
'exist.');
86+
}
87+
}
88+
return null;
89+
}
90+
91+
/// If a build is invoked
92+
Future<ToolInstance?> _tryLoadCompilerFromNativeToolchain(Tool tool) async {
93+
final resolved = (await tool.defaultResolver!.resolve())
94+
.where((i) => i.tool == tool)
95+
.toList()
96+
..sort();
97+
if (resolved.isEmpty) {
98+
logger?.warning('Clang could not be found by package:native_toolchain.');
99+
return null;
100+
}
101+
return resolved.last;
102+
}
103+
104+
Future<Uri> resolveLinker(
105+
Uri compiler,
106+
) async {
107+
if (compiler.pathSegments.last == 'clang') {
108+
final lld = compiler.resolve('lld');
109+
if (await File.fromUri(lld).exists()) {
110+
return lld;
111+
}
112+
}
113+
const errorMessage = 'No native linker found.';
114+
logger?.severe(errorMessage);
115+
throw Exception(errorMessage);
116+
}
117+
118+
Future<Uri> resolveArchiver(
119+
Uri compiler,
120+
) async {
121+
final compilerExecutable = compiler.pathSegments.last;
122+
if (compilerExecutable == 'clang') {
123+
final ar = compiler.resolve('llvm-ar');
124+
if (await File.fromUri(ar).exists()) {
125+
return ar;
126+
}
127+
} else if (compilerExecutable.contains('-gcc')) {
128+
final ar =
129+
compiler.resolve(compilerExecutable.replaceAll('-gcc', '-gcc-ar'));
130+
if (await File.fromUri(ar).exists()) {
131+
return ar;
132+
} else {
133+
print(ar);
134+
}
135+
}
136+
final errorMessage =
137+
'No native linker found for compiler: $compilerExecutable $compiler.';
138+
logger?.severe(errorMessage);
139+
throw Exception(errorMessage);
140+
}
141+
}
142+
143+
final i686LinuxGnuGcc = Tool(
144+
name: 'i686-linux-gnu-gcc',
145+
defaultResolver: PathToolResolver(toolName: 'i686-linux-gnu-gcc'),
146+
);
147+
148+
final armLinuxGnueabihfGcc = Tool(
149+
name: 'arm-linux-gnueabihf-gcc',
150+
defaultResolver: PathToolResolver(toolName: 'arm-linux-gnueabihf-gcc'),
151+
);
152+
153+
final aarch64LinuxGnuGcc = Tool(
154+
name: 'aarch64-linux-gnu-gcc',
155+
defaultResolver: PathToolResolver(toolName: 'aarch64-linux-gnu-gcc'),
156+
);
157+
158+
final clangLike = [
159+
clang,
160+
i686LinuxGnuGcc,
161+
armLinuxGnueabihfGcc,
162+
aarch64LinuxGnuGcc,
163+
];

0 commit comments

Comments
 (0)