Skip to content

Commit 7faf62c

Browse files
authored
Add includes, flags, std, language, cppLinkStdLib options (#125)
1 parent bbcbc1f commit 7faf62c

20 files changed

+1061
-629
lines changed

pkgs/native_toolchain_c/CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
## 0.2.4
2+
3+
- Added `includes` for specifying include directories.
4+
- Added `flags` for specifying arbitrary compiler flags.
5+
- Added `std` for specifying a language standard.
6+
- Added `language` for selecting the language (`c` and `cpp`) to compile source files as.
7+
- Added `cppLinkStdLib` for specifying the C++ standard library to link against.
8+
19
## 0.2.3
210

311
- Fix MSVC tool resolution inside (x86) folder

pkgs/native_toolchain_c/lib/src/cbuilder/cbuilder.dart

Lines changed: 89 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,25 @@ abstract class Builder {
1818
});
1919
}
2020

21+
/// A programming language that can be selected for compilation of source files.
22+
///
23+
/// See [CBuilder.language] for more information.
24+
class Language {
25+
/// The name of the language.
26+
final String name;
27+
28+
const Language._(this.name);
29+
30+
static const Language c = Language._('c');
31+
static const Language cpp = Language._('c++');
32+
33+
/// Known values for [Language].
34+
static const List<Language> values = [c, cpp];
35+
36+
@override
37+
String toString() => name;
38+
}
39+
2140
/// Specification for building an artifact with a C compiler.
2241
class CBuilder implements Builder {
2342
/// What kind of artifact to build.
@@ -45,6 +64,13 @@ class CBuilder implements Builder {
4564
/// Used to output the [BuildOutput.dependencies].
4665
final List<String> sources;
4766

67+
/// Include directories to pass to the compiler.
68+
///
69+
/// Resolved against [BuildConfig.packageRoot].
70+
///
71+
/// Used to output the [BuildOutput.dependencies].
72+
final List<String> includes;
73+
4874
/// The dart files involved in building this artifact.
4975
///
5076
/// Resolved against [BuildConfig.packageRoot].
@@ -57,6 +83,9 @@ class CBuilder implements Builder {
5783
@visibleForTesting
5884
final Uri? installName;
5985

86+
/// Flags to pass to the compiler.
87+
final List<String> flags;
88+
6089
/// Definitions of preprocessor macros.
6190
///
6291
/// When the value is `null`, the macro is defined without a value.
@@ -95,26 +124,64 @@ class CBuilder implements Builder {
95124
/// Defaults to `true` for libraries and `false` for executables.
96125
final bool? pic;
97126

127+
/// The language standard to use.
128+
///
129+
/// When set to `null`, the default behavior of the compiler will be used.
130+
final String? std;
131+
132+
/// The language to compile [sources] as.
133+
///
134+
/// [cppLinkStdLib] only has an effect when this option is set to
135+
/// [Langauge.cpp].
136+
final Language language;
137+
138+
/// The C++ standard library to link against.
139+
///
140+
/// This option has no effect when [language] is not set to [Language.cpp] or
141+
/// when compiling for Windows.
142+
///
143+
/// When set to `null`, the following defaults will be used, based on the
144+
/// target OS:
145+
///
146+
/// | OS | Library |
147+
/// | :------ | :----------- |
148+
/// | Android | `c++_shared` |
149+
/// | iOS | `c++` |
150+
/// | Linux | `stdc++` |
151+
/// | macOS | `c++` |
152+
/// | Fuchsia | `c++` |
153+
final String? cppLinkStdLib;
154+
98155
CBuilder.library({
99156
required this.name,
100157
required this.assetId,
101158
this.sources = const [],
159+
this.includes = const [],
102160
this.dartBuildFiles = const ['build.dart'],
103161
@visibleForTesting this.installName,
162+
this.flags = const [],
104163
this.defines = const {},
105164
this.buildModeDefine = true,
106165
this.ndebugDefine = true,
107166
this.pic = true,
167+
this.std,
168+
this.language = Language.c,
169+
this.cppLinkStdLib,
108170
}) : _type = _CBuilderType.library;
109171

110172
CBuilder.executable({
111173
required this.name,
112174
this.sources = const [],
175+
this.includes = const [],
113176
this.dartBuildFiles = const ['build.dart'],
177+
this.flags = const [],
114178
this.defines = const {},
115179
this.buildModeDefine = true,
116180
this.ndebugDefine = true,
117181
bool? pie = false,
182+
this.std,
183+
this.language = Language.c,
184+
this.cppLinkStdLib,
118185
}) : _type = _CBuilderType.executable,
119186
assetId = null,
120187
installName = null,
@@ -141,6 +208,10 @@ class CBuilder implements Builder {
141208
for (final source in this.sources)
142209
packageRoot.resolveUri(Uri.file(source)),
143210
];
211+
final includes = [
212+
for (final directory in this.includes)
213+
packageRoot.resolveUri(Uri.file(directory)),
214+
];
144215
final dartBuildFiles = [
145216
for (final source in this.dartBuildFiles) packageRoot.resolve(source),
146217
];
@@ -149,6 +220,7 @@ class CBuilder implements Builder {
149220
buildConfig: buildConfig,
150221
logger: logger,
151222
sources: sources,
223+
includes: includes,
152224
dynamicLibrary:
153225
_type == _CBuilderType.library && linkMode == LinkMode.dynamic
154226
? libUri
@@ -159,13 +231,17 @@ class CBuilder implements Builder {
159231
: null,
160232
executable: _type == _CBuilderType.executable ? exeUri : null,
161233
installName: installName,
234+
flags: flags,
162235
defines: {
163236
...defines,
164237
if (buildModeDefine) buildConfig.buildMode.name.toUpperCase(): null,
165238
if (ndebugDefine && buildConfig.buildMode != BuildMode.debug)
166239
'NDEBUG': null,
167240
},
168241
pic: pic,
242+
std: std,
243+
language: language,
244+
cppLinkStdLib: cppLinkStdLib,
169245
);
170246
await task.run();
171247
}
@@ -188,10 +264,21 @@ class CBuilder implements Builder {
188264
}
189265
}
190266
if (!buildConfig.dryRun) {
191-
buildOutput.dependencies.dependencies.addAll([
267+
final includeFiles = await Stream.fromIterable(includes)
268+
.asyncExpand(
269+
(include) => Directory(include.toFilePath())
270+
.list(recursive: true)
271+
.where((entry) => entry is File)
272+
.map((file) => file.uri),
273+
)
274+
.toList();
275+
276+
buildOutput.dependencies.dependencies.addAll({
277+
// Note: We use a Set here to deduplicate the dependencies.
192278
...sources,
279+
...includeFiles,
193280
...dartBuildFiles,
194-
]);
281+
});
195282
}
196283
}
197284
}

pkgs/native_toolchain_c/lib/src/cbuilder/run_cbuilder.dart

Lines changed: 55 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,14 @@ import '../native_toolchain/xcode.dart';
1313
import '../tool/tool_instance.dart';
1414
import '../utils/env_from_bat.dart';
1515
import '../utils/run_process.dart';
16+
import 'cbuilder.dart';
1617
import 'compiler_resolver.dart';
1718

1819
class RunCBuilder {
1920
final BuildConfig buildConfig;
2021
final Logger? logger;
2122
final List<Uri> sources;
23+
final List<Uri> includes;
2224
final Uri? executable;
2325
final Uri? dynamicLibrary;
2426
final Uri? staticLibrary;
@@ -32,25 +34,42 @@ class RunCBuilder {
3234
/// Can be modified with `install_name_tool`.
3335
final Uri? installName;
3436

37+
final List<String> flags;
3538
final Map<String, String?> defines;
3639
final bool? pic;
40+
final String? std;
41+
final Language language;
42+
final String? cppLinkStdLib;
3743

3844
RunCBuilder({
3945
required this.buildConfig,
4046
this.logger,
4147
this.sources = const [],
48+
this.includes = const [],
4249
this.executable,
4350
this.dynamicLibrary,
4451
this.staticLibrary,
4552
this.installName,
53+
this.flags = const [],
4654
this.defines = const {},
4755
this.pic,
56+
this.std,
57+
this.language = Language.c,
58+
this.cppLinkStdLib,
4859
}) : outDir = buildConfig.outDir,
4960
target = buildConfig.target,
5061
assert([executable, dynamicLibrary, staticLibrary]
5162
.whereType<Uri>()
5263
.length ==
53-
1);
64+
1) {
65+
if (target.os == OS.windows && cppLinkStdLib != null) {
66+
throw ArgumentError.value(
67+
cppLinkStdLib,
68+
'cppLinkStdLib',
69+
'is not supported when targeting Windows',
70+
);
71+
}
72+
}
5473

5574
late final _resolver =
5675
CompilerResolver(buildConfig: buildConfig, logger: logger);
@@ -133,20 +152,6 @@ class RunCBuilder {
133152
'-install_name',
134153
installName!.toFilePath(),
135154
],
136-
...sources.map((e) => e.toFilePath()),
137-
if (executable != null) ...[
138-
'-o',
139-
outDir.resolveUri(executable!).toFilePath(),
140-
],
141-
if (dynamicLibrary != null) ...[
142-
'--shared',
143-
'-o',
144-
outDir.resolveUri(dynamicLibrary!).toFilePath(),
145-
] else if (staticLibrary != null) ...[
146-
'-c',
147-
'-o',
148-
outDir.resolve('out.o').toFilePath(),
149-
],
150155
if (pic != null)
151156
if (pic!) ...[
152157
if (dynamicLibrary != null) '-fPIC',
@@ -165,8 +170,31 @@ class RunCBuilder {
165170
'notext',
166171
]
167172
],
173+
if (std != null) '-std=$std',
174+
if (language == Language.cpp) ...[
175+
'-x',
176+
'c++',
177+
'-l',
178+
cppLinkStdLib ?? defaultCppLinkStdLib[target.os]!
179+
],
180+
...flags,
168181
for (final MapEntry(key: name, :value) in defines.entries)
169182
if (value == null) '-D$name' else '-D$name=$value',
183+
for (final include in includes) '-I${include.toFilePath()}',
184+
...sources.map((e) => e.toFilePath()),
185+
if (executable != null) ...[
186+
'-o',
187+
outDir.resolveUri(executable!).toFilePath(),
188+
],
189+
if (dynamicLibrary != null) ...[
190+
'--shared',
191+
'-o',
192+
outDir.resolveUri(dynamicLibrary!).toFilePath(),
193+
] else if (staticLibrary != null) ...[
194+
'-c',
195+
'-o',
196+
outDir.resolve('out.o').toFilePath(),
197+
],
170198
],
171199
logger: logger,
172200
captureOutput: false,
@@ -201,8 +229,12 @@ class RunCBuilder {
201229
final result = await runProcess(
202230
executable: compiler.uri,
203231
arguments: [
232+
if (std != null) '/std:$std',
233+
if (language == Language.cpp) '/TP',
234+
...flags,
204235
for (final MapEntry(key: name, :value) in defines.entries)
205236
if (value == null) '/D$name' else '/D$name=$value',
237+
for (final directory in includes) '/I${directory.toFilePath()}',
206238
if (executable != null) ...[
207239
...sources.map((e) => e.toFilePath()),
208240
'/link',
@@ -265,4 +297,12 @@ class RunCBuilder {
265297
IOSSdk.iPhoneSimulator: 'x86_64-apple-ios-simulator',
266298
},
267299
};
300+
301+
static const defaultCppLinkStdLib = {
302+
OS.android: 'c++_shared',
303+
OS.fuchsia: 'c++',
304+
OS.iOS: 'c++',
305+
OS.linux: 'stdc++',
306+
OS.macOS: 'c++',
307+
};
268308
}

pkgs/native_toolchain_c/lib/src/native_toolchain/xcode.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,11 +90,11 @@ class XCodeSdkResolver implements ToolResolver {
9090
}
9191
assert(result.exitCode == 0);
9292
final uriSymbolic = Uri.directory(result.stdout.trim());
93-
logger?.fine('Found $sdk at ${uriSymbolic.toFilePath()}}');
93+
logger?.fine('Found $sdk at ${uriSymbolic.toFilePath()}');
9494
final uri = Uri.directory(
9595
await Directory.fromUri(uriSymbolic).resolveSymbolicLinks());
9696
if (uriSymbolic != uri) {
97-
logger?.fine('Found $sdk at ${uri.toFilePath()}}');
97+
logger?.fine('Found $sdk at ${uri.toFilePath()}');
9898
}
9999
assert(await Directory.fromUri(uri).exists());
100100
return [ToolInstance(tool: tool, uri: uri)];

pkgs/native_toolchain_c/pubspec.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name: native_toolchain_c
22
description: >-
33
A library to invoke the native C compiler installed on the host machine.
4-
version: 0.2.3
4+
version: 0.2.4
55
repository: https://github.com/dart-lang/native/tree/main/pkgs/native_toolchain_c
66

77
topics:
@@ -12,7 +12,7 @@ topics:
1212
- native-toolchain
1313

1414
environment:
15-
sdk: '>=3.0.0 <4.0.0'
15+
sdk: '>=3.1.0 <4.0.0'
1616

1717
dependencies:
1818
cli_config: ^0.1.1

0 commit comments

Comments
 (0)