Skip to content

Commit

Permalink
[native_toolchain_c] Add linking on Linux (#987)
Browse files Browse the repository at this point in the history
Extend API by providing a `CLinker`, supporting development only on linux for now. Other platforms to be added in future PRs.

---

<details>
  <summary>Contribution guidelines:</summary><br>

- See our [contributor guide](https://github.com/dart-lang/.github/blob/main/CONTRIBUTING.md) for general expectations for PRs.
- Larger or significant changes should be discussed in an issue before creating a PR.
- Contributions to our repos should follow the [Dart style guide](https://dart.dev/guides/language/effective-dart) and use `dart format`.
- Most changes should add an entry to the changelog and may need to [rev the pubspec package version](https://github.com/dart-lang/sdk/wiki/External-Package-Maintenance#making-a-change).
- Changes to packages require [corresponding tests](https://github.com/dart-lang/.github/blob/main/CONTRIBUTING.md#Testing).

Note that many Dart repos have a weekly cadence for reviewing PRs - please allow for some latency before initial review feedback.
</details>
  • Loading branch information
mosuem authored Jul 29, 2024
1 parent bb30bdb commit 7339ed1
Show file tree
Hide file tree
Showing 41 changed files with 1,230 additions and 286 deletions.
3 changes: 3 additions & 0 deletions .github/workflows/native.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,9 @@ jobs:
- run: dart pub get -C test_data/add_asset_link/
if: ${{ matrix.package == 'native_assets_builder' }}

- run: dart pub get -C test_data/treeshaking_native_libs/
if: ${{ matrix.package == 'native_assets_builder' }}

- run: dart pub get -C example/build/native_add_app/
if: ${{ matrix.package == 'native_assets_cli' }}

Expand Down
33 changes: 33 additions & 0 deletions pkgs/native_assets_builder/test/build_runner/link_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,39 @@ void main() async {
);
});
});

test('treeshaking assets using CLinker', timeout: longTimeout, () async {
await inTempDir((tempUri) async {
await copyTestProjects(targetUri: tempUri);
final packageUri = tempUri.resolve('treeshaking_native_libs/');

// First, run `pub get`, we need pub to resolve our dependencies.
await runPubGet(
workingDirectory: packageUri,
logger: logger,
);

final buildResult = await build(
packageUri,
logger,
dartExecutable,
linkingEnabled: true,
);
expect(buildResult.assets.length, 0);
expect(buildResult.assetsForLinking.length, 1);

final logMessages = <String>[];
final linkResult = await link(
packageUri,
logger,
dartExecutable,
buildResult: buildResult,
capturedLogs: logMessages,
);
expect(linkResult.assets.length, 1);
expect(linkResult.assets.first, isA<NativeCodeAsset>());
});
});
}

Iterable<String> _getNames(List<AssetImpl> assets) =>
Expand Down
12 changes: 12 additions & 0 deletions pkgs/native_assets_builder/test_data/manifest.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,18 @@
- simple_data_asset/bin/deep_modify_data_asset.dart.debug
- some_dev_dep/bin/some_dev_dep.dart
- some_dev_dep/pubspec.yaml
- treeshaking_native_libs/pubspec.yaml
- treeshaking_native_libs/lib/treeshaking_native_libs.dart
- treeshaking_native_libs/lib/src/treeshaking_native_libs.dart
- treeshaking_native_libs/lib/src/treeshaking_native_libs_bindings_generated.dart
- treeshaking_native_libs/src/native_add.h
- treeshaking_native_libs/src/native_multiply.h
- treeshaking_native_libs/src/native_add.c
- treeshaking_native_libs/src/native_multiply.c
- treeshaking_native_libs/hook/link.dart
- treeshaking_native_libs/hook/build.dart
- treeshaking_native_libs/bin/treeshaking_native_libs.dart
- treeshaking_native_libs/ffigen.yaml
- wrong_build_output_2/hook/build.dart
- wrong_build_output_2/pubspec.yaml
- wrong_build_output_3/hook/build.dart
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// 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.

import 'package:treeshaking_native_libs/treeshaking_native_libs.dart';

void main(List<String> args) {
print(add(5, 3));
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Run with `flutter pub run ffigen --config ffigen.yaml`.
name: NativeCalcBindings
description: |
Bindings for `src/native_add.h` and `src/native_multiply.h`.
Regenerate bindings with `flutter pub run ffigen --config ffigen.yaml`.
output: "lib/src/treeshaking_native_libs_bindings_generated.dart"
headers:
entry-points:
- "src/native_add.h"
- "src/native_multiply.h"
include-directives:
- "src/native_add.h"
- "src/native_multiply.h"
preamble: |
// 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.
comments:
style: any
length: full
ffi-native:
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// 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.

import 'package:logging/logging.dart';
import 'package:native_assets_cli/native_assets_cli.dart';
import 'package:native_toolchain_c/native_toolchain_c.dart';

void main(List<String> arguments) async {
await build(arguments, (config, output) async {
final cbuilder = CBuilder.library(
name: config.packageName + (config.linkingEnabled ? '_static' : ''),
assetName: 'src/${config.packageName}_bindings_generated.dart',
sources: [
'src/native_add.c',
'src/native_multiply.c',
],
linkModePreference: config.linkingEnabled
? LinkModePreference.static
: LinkModePreference.dynamic,
);
await cbuilder.run(
config: config,
output: output,
linkInPackage: config.linkingEnabled ? config.packageName : null,
logger: Logger('')
..level = Level.ALL
..onRecord.listen((record) {
print('${record.level.name}: ${record.time}: ${record.message}');
}),
);
});
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// 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.

import 'package:logging/logging.dart';
import 'package:native_assets_cli/native_assets_cli.dart';
import 'package:native_toolchain_c/native_toolchain_c.dart';

void main(List<String> arguments) async {
await link(
arguments,
(config, output) async {
final linker = CLinker.library(
name: config.packageName,
assetName: config.assets.single.id.split('/').skip(1).join('/'),
linkerOptions: LinkerOptions.treeshake(symbols: ['add']),
sources: [config.assets.single.file!.toFilePath()],
);
await linker.run(
config: config,
output: output,
logger: Logger('')
..level = Level.ALL
..onRecord.listen((record) {
print('${record.level.name}: ${record.time}: ${record.message}');
}),
);
},
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// 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.

import 'treeshaking_native_libs_bindings_generated.dart' as bindings;

int add(int a, int b) => bindings.add(a, b);
int multiply(int a, int b) => bindings.multiply(a, b);
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright (c) 2023, 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.

// AUTO GENERATED FILE, DO NOT EDIT.
//
// Generated by `package:ffigen`.
// ignore_for_file: type=lint
import 'dart:ffi' as ffi;

@ffi.Native<ffi.Int32 Function(ffi.Int32, ffi.Int32)>(symbol: 'add')
external int add(
int a,
int b,
);

@ffi.Native<ffi.IntPtr Function(ffi.IntPtr, ffi.IntPtr)>(symbol: 'multiply')
external int multiply(
int a,
int b,
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// 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.

export 'src/treeshaking_native_libs.dart';
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
name: treeshaking_native_libs
description: Treeshake symbols by name from a native library.
version: 0.1.0

publish_to: none

environment:
sdk: '>=3.3.0 <4.0.0'

dependencies:
logging: ^1.1.1
# native_assets_cli: ^0.7.1
native_assets_cli:
path: ../../../native_assets_cli/
# native_toolchain_c: ^0.5.2
native_toolchain_c:
path: ../../../native_toolchain_c/

dev_dependencies:
ffigen: ^8.0.2
lints: ^3.0.0
some_dev_dep:
path: ../some_dev_dep/
test: ^1.23.1
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// 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 "native_add.h"

int32_t add(int32_t a, int32_t b) {
return a + b;
}
Original file line number Diff line number Diff line change
@@ -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 <stdint.h>

#if _WIN32
#define MYLIB_EXPORT __declspec(dllexport)
#else
#define MYLIB_EXPORT
#endif

MYLIB_EXPORT int32_t add(int32_t a, int32_t b);
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// 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 "native_multiply.h"

MYLIB_EXPORT intptr_t multiply(intptr_t a, intptr_t b) {
return a * b;
}
Original file line number Diff line number Diff line change
@@ -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 <stdint.h>

#if _WIN32
#define MYLIB_EXPORT __declspec(dllexport)
#else
#define MYLIB_EXPORT
#endif

MYLIB_EXPORT intptr_t multiply(intptr_t a, intptr_t b);
Original file line number Diff line number Diff line change
@@ -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.

import 'package:test/test.dart';
import 'package:treeshaking_native_libs/treeshaking_native_libs.dart';

void main() {
test('native add test', () {
final result = add(4, 6);
expect(result, equals(10));
});
}
5 changes: 4 additions & 1 deletion pkgs/native_toolchain_c/lib/native_toolchain_c.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,8 @@
/// A library to invoke the native C compiler installed on the host machine.
library;

export 'src/cbuilder/cbuilder.dart';
export 'src/cbuilder/cbuilder.dart' show CBuilder;
export 'src/cbuilder/clinker.dart' show CLinker, LinkerOptions;
export 'src/cbuilder/language.dart' show Language;
export 'src/cbuilder/output_type.dart' show OutputType;
export 'src/utils/env_from_bat.dart';
Loading

0 comments on commit 7339ed1

Please sign in to comment.