Skip to content

Commit e00afd9

Browse files
authored
[native_toolchain_c] Add libraries and libraryDirectories options to CTool (#1423)
1 parent 8ea1a9d commit e00afd9

File tree

17 files changed

+272
-75
lines changed

17 files changed

+272
-75
lines changed

.github/workflows/native.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,9 @@ jobs:
142142

143143
- run: dart --enable-experiment=native-assets test
144144
working-directory: pkgs/${{ matrix.package }}/example/build/native_dynamic_linking/
145+
# TODO(https://github.com/dart-lang/native/issues/190): Enable on windows once
146+
# https://github.com/dart-lang/sdk/commit/903eea6bfb8ee405587f0866a1d1e92eea45d29e
147+
# has landed in dev channel.
145148
if: ${{ matrix.package == 'native_assets_cli' && matrix.sdk == 'dev' && !matrix.breaking-change && matrix.os != 'windows' }}
146149

147150
- run: dart --enable-experiment=native-assets test

pkgs/native_assets_builder/test_data/native_dynamic_linking/hook/build.dart

Lines changed: 3 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -26,23 +26,19 @@ void main(List<String> args) async {
2626
sources: [
2727
'src/math.c',
2828
],
29-
// TODO(https://github.com/dart-lang/native/issues/190): Use specific
30-
// API for linking once available.
31-
flags: config.dynamicLinkingFlags('debug'),
29+
libraries: ['debug'],
3230
),
3331
CBuilder.library(
3432
name: 'add',
3533
assetName: 'add.dart',
3634
sources: [
3735
'src/add.c',
3836
],
39-
// TODO(https://github.com/dart-lang/native/issues/190): Use specific
40-
// API for linking once available.
41-
flags: config.dynamicLinkingFlags('math'),
37+
libraries: ['math'],
4238
)
4339
];
4440

45-
// Note: This builders need to be run sequentially because they depend on
41+
// Note: These builders need to be run sequentially because they depend on
4642
// each others output.
4743
for (final builder in builders) {
4844
await builder.run(
@@ -53,21 +49,3 @@ void main(List<String> args) async {
5349
}
5450
});
5551
}
56-
57-
extension on BuildConfig {
58-
List<String> dynamicLinkingFlags(String libraryName) => switch (targetOS) {
59-
OS.macOS => [
60-
'-L${outputDirectory.toFilePath()}',
61-
'-l$libraryName',
62-
],
63-
OS.linux => [
64-
r'-Wl,-rpath=$ORIGIN',
65-
'-L${outputDirectory.toFilePath()}',
66-
'-l$libraryName',
67-
],
68-
OS.windows => [
69-
outputDirectory.resolve('$libraryName.lib').toFilePath(),
70-
],
71-
_ => throw UnimplementedError('Unsupported OS: $targetOS'),
72-
};
73-
}

pkgs/native_assets_builder/test_data/native_dynamic_linking/src/debug.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,7 @@ int debug_printf(const char* format, ...) {
1616
int ret = vprintf(format, args);
1717
va_end(args);
1818
return ret;
19+
#else
20+
return 0;
1921
#endif
2022
}

pkgs/native_assets_cli/example/build/native_dynamic_linking/hook/build.dart

Lines changed: 3 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -26,23 +26,19 @@ void main(List<String> args) async {
2626
sources: [
2727
'src/math.c',
2828
],
29-
// TODO(https://github.com/dart-lang/native/issues/190): Use specific
30-
// API for linking once available.
31-
flags: config.dynamicLinkingFlags('debug'),
29+
libraries: ['debug'],
3230
),
3331
CBuilder.library(
3432
name: 'add',
3533
assetName: 'add.dart',
3634
sources: [
3735
'src/add.c',
3836
],
39-
// TODO(https://github.com/dart-lang/native/issues/190): Use specific
40-
// API for linking once available.
41-
flags: config.dynamicLinkingFlags('math'),
37+
libraries: ['math'],
4238
)
4339
];
4440

45-
// Note: This builders need to be run sequentially because they depend on
41+
// Note: These builders need to be run sequentially because they depend on
4642
// each others output.
4743
for (final builder in builders) {
4844
await builder.run(
@@ -53,20 +49,3 @@ void main(List<String> args) async {
5349
}
5450
});
5551
}
56-
57-
extension on BuildConfig {
58-
List<String> dynamicLinkingFlags(String libraryName) => switch (targetOS) {
59-
OS.macOS => [
60-
'-L${outputDirectory.toFilePath()}',
61-
'-l$libraryName',
62-
],
63-
OS.linux => [
64-
'-Wl,-rpath=\$ORIGIN/.',
65-
'-L${outputDirectory.toFilePath()}',
66-
'-l$libraryName',
67-
],
68-
// TODO(https://github.com/dart-lang/native/issues/1415): Enable support
69-
// for Windows once linker flags are supported by CBuilder.
70-
_ => throw UnimplementedError('Unsupported OS: $targetOS'),
71-
};
72-
}

pkgs/native_assets_cli/example/build/native_dynamic_linking/src/debug.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,7 @@ int debug_printf(const char* format, ...) {
1616
int ret = vprintf(format, args);
1717
va_end(args);
1818
return ret;
19+
#else
20+
return 0;
1921
#endif
2022
}

pkgs/native_assets_cli/test/example/native_dynamic_linking_test.dart

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@
66
'mac-os': Timeout.factor(2),
77
'windows': Timeout.factor(10),
88
})
9-
// TODO(https://github.com/dart-lang/native/issues/1415): Enable support
10-
// for Windows once linker flags are supported by CBuilder.
9+
// TODO(https://github.com/dart-lang/native/issues/190): Enable on windows once
10+
// https://github.com/dart-lang/sdk/commit/903eea6bfb8ee405587f0866a1d1e92eea45d29e
11+
// has landed in dev channel.
1112
@TestOn('!windows')
1213
library;
1314

@@ -20,12 +21,6 @@ import 'package:test/test.dart';
2021
import '../helpers.dart';
2122

2223
void main() async {
23-
if (Platform.isWindows) {
24-
// TODO(https://github.com/dart-lang/native/issues/1415): Enable support
25-
// for Windows once linker flags are supported by CBuilder.
26-
return;
27-
}
28-
2924
late Uri tempUri;
3025
const name = 'native_dynamic_linking';
3126

@@ -51,19 +46,22 @@ void main() async {
5146

5247
final configBuilder = BuildConfigBuilder()
5348
..setupHookConfig(
54-
packageRoot: testPackageUri,
55-
packageName: name,
56-
targetOS: OS.current,
57-
buildAssetTypes: [CodeAsset.type],
58-
buildMode: dryRun ? null : BuildMode.debug)
49+
packageRoot: testPackageUri,
50+
packageName: name,
51+
targetOS: OS.current,
52+
buildAssetTypes: [CodeAsset.type],
53+
buildMode: dryRun ? null : BuildMode.debug,
54+
)
5955
..setupBuildRunConfig(
60-
outputDirectory: outputDirectory,
61-
outputDirectoryShared: outputDirectoryShared)
56+
outputDirectory: outputDirectory,
57+
outputDirectoryShared: outputDirectoryShared,
58+
)
6259
..setupBuildConfig(linkingEnabled: false, dryRun: dryRun)
6360
..setupCodeConfig(
64-
targetArchitecture: dryRun ? null : Architecture.current,
65-
linkModePreference: LinkModePreference.dynamic,
66-
cCompilerConfig: dryRun ? null : cCompiler);
61+
targetArchitecture: dryRun ? null : Architecture.current,
62+
linkModePreference: LinkModePreference.dynamic,
63+
cCompilerConfig: dryRun ? null : cCompiler,
64+
);
6765

6866
final buildConfigUri = testTempUri.resolve('build_config.json');
6967
await File.fromUri(buildConfigUri)

pkgs/native_toolchain_c/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
https://github.com/dart-lang/native/issues/1611
55
- Make optimization level configurable. Defaults to `-3s` and `/O3`.
66
https://github.com/dart-lang/native/issues/1267
7+
- Add `libraries` and `libraryDirectories` to `CTool`.
78

89
## 0.6.0
910

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ class CBuilder extends CTool implements Builder {
5353
super.sources = const [],
5454
super.includes = const [],
5555
super.frameworks = CTool.defaultFrameworks,
56+
super.libraries = const [],
57+
super.libraryDirectories = CTool.defaultLibraryDirectories,
5658
@Deprecated(
5759
'Newer Dart and Flutter SDKs automatically add the Dart hook '
5860
'sources as dependencies.',
@@ -76,6 +78,8 @@ class CBuilder extends CTool implements Builder {
7678
super.sources = const [],
7779
super.includes = const [],
7880
super.frameworks = CTool.defaultFrameworks,
81+
super.libraries = const [],
82+
super.libraryDirectories = CTool.defaultLibraryDirectories,
7983
@Deprecated(
8084
'Newer Dart and Flutter SDKs automatically add the Dart hook '
8185
'sources as dependencies.',
@@ -132,6 +136,10 @@ class CBuilder extends CTool implements Builder {
132136
// ignore: deprecated_member_use_from_same_package
133137
for (final source in this.dartBuildFiles) packageRoot.resolve(source),
134138
];
139+
final libraryDirectories = [
140+
for (final directory in this.libraryDirectories)
141+
outDir.resolveUri(Uri.file(directory)),
142+
];
135143
// ignore: deprecated_member_use
136144
if (!config.dryRun) {
137145
final task = RunCBuilder(
@@ -141,6 +149,8 @@ class CBuilder extends CTool implements Builder {
141149
sources: sources,
142150
includes: includes,
143151
frameworks: frameworks,
152+
libraries: libraries,
153+
libraryDirectories: libraryDirectories,
144154
dynamicLibrary:
145155
type == OutputType.library && linkMode == DynamicLoadingBundled()
146156
? libUri

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ class CLinker extends CTool implements Linker {
2929
super.sources = const [],
3030
super.includes = const [],
3131
super.frameworks = CTool.defaultFrameworks,
32+
super.libraries = const [],
33+
super.libraryDirectories = CTool.defaultLibraryDirectories,
3234
@visibleForTesting super.installName,
3335
super.flags = const [],
3436
super.defines = const {},
@@ -68,6 +70,10 @@ class CLinker extends CTool implements Linker {
6870
for (final directory in this.includes)
6971
packageRoot.resolveUri(Uri.file(directory)),
7072
];
73+
final libraryDirectories = [
74+
for (final directory in this.libraryDirectories)
75+
outDir.resolveUri(Uri.file(directory)),
76+
];
7177
final task = RunCBuilder(
7278
config: config,
7379
codeConfig: config.codeConfig,
@@ -76,6 +82,8 @@ class CLinker extends CTool implements Linker {
7682
sources: sources,
7783
includes: includes,
7884
frameworks: frameworks,
85+
libraries: libraries,
86+
libraryDirectories: libraryDirectories,
7987
dynamicLibrary: linkMode == DynamicLoadingBundled() ? libUri : null,
8088
staticLibrary: linkMode == StaticLinking() ? libUri : null,
8189
// ignore: invalid_use_of_visible_for_testing_member

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

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,17 @@ import 'package:meta/meta.dart';
66
import 'package:native_assets_cli/code_assets.dart';
77

88
import 'cbuilder.dart';
9+
import 'clinker.dart';
910
import 'language.dart';
1011
import 'optimization_level.dart';
1112
import 'output_type.dart';
1213

14+
/// Common options for [CBuilder] and [CLinker].
1315
abstract class CTool {
1416
/// What kind of artifact to build.
1517
final OutputType type;
1618

17-
/// Name of the library or executable to linkg.
19+
/// Name of the library or executable to build or link.
1820
///
1921
/// The filename will be decided by [LinkConfig.targetOS] and
2022
/// [OSLibraryNaming.libraryFileName] or
@@ -50,16 +52,39 @@ abstract class CTool {
5052
///
5153
/// Defaults to `['Foundation']`.
5254
///
53-
/// Framworks will not be automatically reported as dependencies of the hook.
55+
/// Frameworks will not be automatically reported as dependencies of the hook.
5456
/// Frameworks can be mentioned by name if they are available on the system,
5557
/// so the file path is not known. If you're depending on your own frameworks
5658
/// report them as dependencies of the hook by calling
5759
/// [BuildOutputBuilder.addDependency] / [LinkOutputBuilder.addDependency]
5860
/// manually.
5961
final List<String> frameworks;
6062

63+
/// The default [frameworks].
6164
static const List<String> defaultFrameworks = ['Foundation'];
6265

66+
/// Libraries to link to.
67+
///
68+
/// In addition to the system default directories, libraries will be searched
69+
/// for in [libraryDirectories].
70+
///
71+
/// If you want to link to a library that was built by another [CBuilder] or
72+
/// [CLinker], either leave the default [libraryDirectories] or include `'.'`
73+
/// in the list.
74+
final List<String> libraries;
75+
76+
/// Directories to search for [libraries], in addition to the system default
77+
/// directories.
78+
///
79+
/// Resolved against [LinkConfig.outputDirectory].
80+
///
81+
/// Defaults to `['.']`, which means the [LinkConfig.outputDirectory] will be
82+
/// searched for libraries.
83+
final List<String> libraryDirectories;
84+
85+
/// The default [libraryDirectories].
86+
static const List<String> defaultLibraryDirectories = ['.'];
87+
6388
/// TODO(https://github.com/dart-lang/native/issues/54): Move to [LinkConfig]
6489
/// or hide in public API.
6590
@visibleForTesting
@@ -130,6 +155,8 @@ abstract class CTool {
130155
required this.sources,
131156
required this.includes,
132157
required this.frameworks,
158+
required this.libraries,
159+
required this.libraryDirectories,
133160
required this.installName,
134161
required this.flags,
135162
required this.defines,

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

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ class RunCBuilder {
2828
final List<Uri> sources;
2929
final List<Uri> includes;
3030
final List<String> frameworks;
31+
final List<String> libraries;
32+
final List<Uri> libraryDirectories;
3133
final Uri? executable;
3234
final Uri? dynamicLibrary;
3335
final Uri? staticLibrary;
@@ -56,6 +58,8 @@ class RunCBuilder {
5658
this.sources = const [],
5759
this.includes = const [],
5860
required this.frameworks,
61+
this.libraries = const [],
62+
this.libraryDirectories = const [],
5963
this.executable,
6064
this.dynamicLibrary,
6165
this.staticLibrary,
@@ -298,8 +302,7 @@ class RunCBuilder {
298302
if (executable != null) ...[
299303
'-o',
300304
outDir.resolveUri(executable!).toFilePath(),
301-
],
302-
if (dynamicLibrary != null) ...[
305+
] else if (dynamicLibrary != null) ...[
303306
'--shared',
304307
'-o',
305308
outFile!.toFilePath(),
@@ -310,6 +313,19 @@ class RunCBuilder {
310313
],
311314
...linkerOptions?.postSourcesFlags(toolInstance.tool, sourceFiles) ??
312315
[],
316+
if (executable != null || dynamicLibrary != null) ...[
317+
if (config.targetOS case OS.android || OS.linux)
318+
// During bundling code assets are all placed in the same directory.
319+
// Setting this rpath allows the binary to find other code assets
320+
// it is linked against.
321+
if (linkerOptions != null)
322+
'-rpath=\$ORIGIN'
323+
else
324+
'-Wl,-rpath=\$ORIGIN',
325+
for (final directory in libraryDirectories)
326+
'-L${directory.toFilePath()}',
327+
for (final library in libraries) '-l$library',
328+
],
313329
],
314330
logger: logger,
315331
captureOutput: false,
@@ -344,17 +360,20 @@ class RunCBuilder {
344360
...sources.map((e) => e.toFilePath()),
345361
'/link',
346362
'/out:${outDir.resolveUri(executable!).toFilePath()}',
347-
],
348-
if (dynamicLibrary != null) ...[
363+
] else if (dynamicLibrary != null) ...[
349364
...sources.map((e) => e.toFilePath()),
350365
'/link',
351366
'/DLL',
352367
'/out:${outDir.resolveUri(dynamicLibrary!).toFilePath()}',
353-
],
354-
if (staticLibrary != null) ...[
368+
] else if (staticLibrary != null) ...[
355369
'/c',
356370
...sources.map((e) => e.toFilePath()),
357371
],
372+
if (executable != null || dynamicLibrary != null) ...[
373+
for (final directory in libraryDirectories)
374+
'/LIBPATH:${directory.toFilePath()}',
375+
for (final library in libraries) '$library.lib',
376+
],
358377
],
359378
workingDirectory: outDir,
360379
environment: environment,

0 commit comments

Comments
 (0)