From d059e0014ba7e213da06e21673b44a5870f7a178 Mon Sep 17 00:00:00 2001 From: Gabriel Terwesten Date: Tue, 13 Aug 2024 09:57:53 +0200 Subject: [PATCH 1/2] Add `CBuilder.dynamicallyLinkTo` option --- .github/workflows/native.yaml | 2 +- .../native_dynamic_linking/hook/build.dart | 27 +------- .../example/native_dynamic_linking_test.dart | 3 - .../lib/src/cbuilder/cbuilder.dart | 36 ++++++++++- .../test/cbuilder/cbuilder_test.dart | 63 +++++++++++++++++++ .../test/cbuilder/testfiles/add/src/add.c | 12 +--- .../test/cbuilder/testfiles/add/src/add.h | 13 ++++ .../src/dynamically_linked.c | 5 ++ 8 files changed, 121 insertions(+), 40 deletions(-) create mode 100644 pkgs/native_toolchain_c/test/cbuilder/testfiles/add/src/add.h create mode 100644 pkgs/native_toolchain_c/test/cbuilder/testfiles/dynamically_linked/src/dynamically_linked.c diff --git a/.github/workflows/native.yaml b/.github/workflows/native.yaml index 18b822e3d..ed6d89fc0 100644 --- a/.github/workflows/native.yaml +++ b/.github/workflows/native.yaml @@ -141,7 +141,7 @@ jobs: - run: dart --enable-experiment=native-assets test working-directory: pkgs/${{ matrix.package }}/example/build/native_dynamic_linking/ - if: ${{ matrix.package == 'native_assets_cli' && matrix.sdk == 'dev' && !matrix.breaking-chang && matrix.os != 'windows' }} + if: ${{ matrix.package == 'native_assets_cli' && matrix.sdk == 'dev' && !matrix.breaking-change }} - run: dart --enable-experiment=native-assets test working-directory: pkgs/${{ matrix.package }}/example/build/native_add_app/ diff --git a/pkgs/native_assets_cli/example/build/native_dynamic_linking/hook/build.dart b/pkgs/native_assets_cli/example/build/native_dynamic_linking/hook/build.dart index 88b3d2d12..a396df219 100644 --- a/pkgs/native_assets_cli/example/build/native_dynamic_linking/hook/build.dart +++ b/pkgs/native_assets_cli/example/build/native_dynamic_linking/hook/build.dart @@ -26,9 +26,7 @@ void main(List args) async { sources: [ 'src/math.c', ], - // TODO(https://github.com/dart-lang/native/issues/190): Use specific - // API for linking once available. - flags: config.dynamicLinkingFlags('debug'), + dynamicallyLinkTo: ['debug'], ), CBuilder.library( name: 'add', @@ -36,13 +34,11 @@ void main(List args) async { sources: [ 'src/add.c', ], - // TODO(https://github.com/dart-lang/native/issues/190): Use specific - // API for linking once available. - flags: config.dynamicLinkingFlags('math'), + dynamicallyLinkTo: ['math'], ) ]; - // Note: This builders need to be run sequentially because they depend on + // Note: These builders need to be run sequentially because they depend on // each others output. for (final builder in builders) { await builder.run( @@ -53,20 +49,3 @@ void main(List args) async { } }); } - -extension on BuildConfig { - List dynamicLinkingFlags(String libraryName) => switch (targetOS) { - OS.macOS => [ - '-L${outputDirectory.toFilePath()}', - '-l$libraryName', - ], - OS.linux => [ - '-Wl,-rpath=\$ORIGIN/.', - '-L${outputDirectory.toFilePath()}', - '-l$libraryName', - ], - // TODO(https://github.com/dart-lang/native/issues/1415): Enable support - // for Windows once linker flags are supported by CBuilder. - _ => throw UnimplementedError('Unsupported OS: $targetOS'), - }; -} diff --git a/pkgs/native_assets_cli/test/example/native_dynamic_linking_test.dart b/pkgs/native_assets_cli/test/example/native_dynamic_linking_test.dart index c3d235848..9a989ce63 100644 --- a/pkgs/native_assets_cli/test/example/native_dynamic_linking_test.dart +++ b/pkgs/native_assets_cli/test/example/native_dynamic_linking_test.dart @@ -6,9 +6,6 @@ 'mac-os': Timeout.factor(2), 'windows': Timeout.factor(10), }) -// TODO(https://github.com/dart-lang/native/issues/1415): Enable support -// for Windows once linker flags are supported by CBuilder. -@TestOn('!windows') library; import 'dart:io'; diff --git a/pkgs/native_toolchain_c/lib/src/cbuilder/cbuilder.dart b/pkgs/native_toolchain_c/lib/src/cbuilder/cbuilder.dart index d2202b737..e4bd761f4 100644 --- a/pkgs/native_toolchain_c/lib/src/cbuilder/cbuilder.dart +++ b/pkgs/native_toolchain_c/lib/src/cbuilder/cbuilder.dart @@ -46,6 +46,15 @@ class CBuilder extends CTool implements Builder { /// Defaults to `true`. final bool ndebugDefine; + /// Libraries to dynamically link to. + /// + /// The libraries are expected to be at the root of the output directory of + /// the build hook invocation. + /// + /// When using this option ensure that the builders producing the libraries + /// to link to are run before this builder. + final List dynamicallyLinkTo; + CBuilder.library({ required super.name, super.assetName, @@ -62,6 +71,7 @@ class CBuilder extends CTool implements Builder { super.defines = const {}, this.buildModeDefine = true, this.ndebugDefine = true, + this.dynamicallyLinkTo = const [], super.pic = true, super.std, super.language = Language.c, @@ -83,6 +93,7 @@ class CBuilder extends CTool implements Builder { super.defines = const {}, this.buildModeDefine = true, this.ndebugDefine = true, + this.dynamicallyLinkTo = const [], bool? pie = false, super.std, super.language = Language.c, @@ -146,7 +157,10 @@ class CBuilder extends CTool implements Builder { executable: type == OutputType.executable ? exeUri : null, // ignore: invalid_use_of_visible_for_testing_member installName: installName, - flags: flags, + flags: [ + ...flags, + ...config.dynamicLinkingFlags(dynamicallyLinkTo), + ], defines: { ...defines, if (buildModeDefine) config.buildMode.name.toUpperCase(): null, @@ -195,3 +209,23 @@ class CBuilder extends CTool implements Builder { } } } + +extension on BuildConfig { + List dynamicLinkingFlags(List libraries) => + switch (targetOS) { + OS.macOS || OS.iOS => [ + '-L${outputDirectory.toFilePath()}', + for (final library in libraries) '-l$library', + ], + OS.linux || OS.android => [ + '-Wl,-rpath=\$ORIGIN/.', + '-L${outputDirectory.toFilePath()}', + for (final library in libraries) '-l$library', + ], + OS.windows => [ + for (final library in libraries) + outputDirectory.resolve('$library.lib').toFilePath(), + ], + _ => throw UnimplementedError('Unsupported OS: $targetOS'), + }; +} diff --git a/pkgs/native_toolchain_c/test/cbuilder/cbuilder_test.dart b/pkgs/native_toolchain_c/test/cbuilder/cbuilder_test.dart index 495e6091d..e530394ba 100644 --- a/pkgs/native_toolchain_c/test/cbuilder/cbuilder_test.dart +++ b/pkgs/native_toolchain_c/test/cbuilder/cbuilder_test.dart @@ -502,6 +502,69 @@ void main() { expect(compilerInvocation, contains('-l stdc++')); } }); + + test('CBuilder dynamicallyLinkTo', () async { + final tempUri = await tempDirForTest(); + final dynamicallyLinkedCUri = packageUri.resolve( + 'test/cbuilder/testfiles/dynamically_linked/src/dynamically_linked.c'); + final addSrcUri = packageUri.resolve('test/cbuilder/testfiles/add/src/'); + final addCUri = addSrcUri.resolve('add.c'); + + if (!await File.fromUri(dynamicallyLinkedCUri).exists()) { + throw Exception('Run the test from the root directory.'); + } + const name = 'dynamically_linked'; + + final logMessages = []; + final logger = createCapturingLogger(logMessages); + + final buildConfig = BuildConfig.build( + outputDirectory: tempUri, + packageName: name, + packageRoot: tempUri, + targetArchitecture: Architecture.current, + targetOS: OS.current, + buildMode: BuildMode.release, + // Ignored by executables. + linkModePreference: LinkModePreference.dynamic, + cCompiler: CCompilerConfig( + compiler: cc, + envScript: envScript, + envScriptArgs: envScriptArgs, + ), + linkingEnabled: false, + ); + final buildOutput = BuildOutput(); + + final builders = [ + CBuilder.library( + name: 'add', + assetName: 'add', + sources: [addCUri.toFilePath()], + ), + CBuilder.executable( + name: name, + includes: [addSrcUri.toFilePath()], + sources: [dynamicallyLinkedCUri.toFilePath()], + dynamicallyLinkTo: ['add'], + ) + ]; + for (final builder in builders) { + await builder.run( + config: buildConfig, + output: buildOutput, + logger: logger, + ); + } + + final executableUri = tempUri.resolve(OS.current.executableFileName(name)); + expect(await File.fromUri(executableUri).exists(), true); + final result = await runProcess( + executable: executableUri, + logger: logger, + ); + expect(result.exitCode, 3); + }); } Future testDefines({ diff --git a/pkgs/native_toolchain_c/test/cbuilder/testfiles/add/src/add.c b/pkgs/native_toolchain_c/test/cbuilder/testfiles/add/src/add.c index 6a16ccab6..dc755d033 100644 --- a/pkgs/native_toolchain_c/test/cbuilder/testfiles/add/src/add.c +++ b/pkgs/native_toolchain_c/test/cbuilder/testfiles/add/src/add.c @@ -2,17 +2,7 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -#include - -#ifdef DEBUG -#include -#endif - -#if _WIN32 -#define FFI_EXPORT __declspec(dllexport) -#else -#define FFI_EXPORT -#endif +#include "add.h" FFI_EXPORT int32_t add(int32_t a, int32_t b) { #ifdef DEBUG diff --git a/pkgs/native_toolchain_c/test/cbuilder/testfiles/add/src/add.h b/pkgs/native_toolchain_c/test/cbuilder/testfiles/add/src/add.h new file mode 100644 index 000000000..2fa187184 --- /dev/null +++ b/pkgs/native_toolchain_c/test/cbuilder/testfiles/add/src/add.h @@ -0,0 +1,13 @@ +// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include + +#if _WIN32 +#define FFI_EXPORT __declspec(dllexport) +#else +#define FFI_EXPORT +#endif + +FFI_EXPORT int32_t add(int32_t a, int32_t b); diff --git a/pkgs/native_toolchain_c/test/cbuilder/testfiles/dynamically_linked/src/dynamically_linked.c b/pkgs/native_toolchain_c/test/cbuilder/testfiles/dynamically_linked/src/dynamically_linked.c new file mode 100644 index 000000000..131f6c870 --- /dev/null +++ b/pkgs/native_toolchain_c/test/cbuilder/testfiles/dynamically_linked/src/dynamically_linked.c @@ -0,0 +1,5 @@ +#include "add.h" + +int main() { + return add(1, 2); +} From 84084246c74f80a149c80d16e129af9f4c1b1635 Mon Sep 17 00:00:00 2001 From: Gabriel Terwesten Date: Tue, 13 Aug 2024 09:59:50 +0200 Subject: [PATCH 2/2] Add file header --- .../testfiles/dynamically_linked/src/dynamically_linked.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pkgs/native_toolchain_c/test/cbuilder/testfiles/dynamically_linked/src/dynamically_linked.c b/pkgs/native_toolchain_c/test/cbuilder/testfiles/dynamically_linked/src/dynamically_linked.c index 131f6c870..57d182bbb 100644 --- a/pkgs/native_toolchain_c/test/cbuilder/testfiles/dynamically_linked/src/dynamically_linked.c +++ b/pkgs/native_toolchain_c/test/cbuilder/testfiles/dynamically_linked/src/dynamically_linked.c @@ -1,3 +1,7 @@ +// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + #include "add.h" int main() {