Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
d496843
Initial version
dcharkes Apr 4, 2023
b10ce94
Rename CBuilder to RunCBuilder
dcharkes Apr 11, 2023
62551be
remove unused import
dcharkes Apr 11, 2023
92f04c2
new CBuilder API
dcharkes Apr 11, 2023
6f79a12
test new CBuilder API
dcharkes Apr 11, 2023
ce7ee20
Remove unused file
dcharkes Apr 12, 2023
e4638c3
Refactor c_compiler implementation
dcharkes Apr 12, 2023
652369c
massage dependencies
dcharkes Apr 12, 2023
1d0262b
which clang version on GitHub CI?
dcharkes Apr 12, 2023
983f332
Allow tool pre-release in smoke test
dcharkes Apr 12, 2023
40582b9
Use `printOnFailure`
dcharkes Apr 12, 2023
a59abce
Add archiver tools and logger to tool resolvers
dcharkes Apr 13, 2023
dac31bd
Find linkers
dcharkes Apr 13, 2023
eb11562
Cleanup tool naming
dcharkes Apr 13, 2023
157236d
Recognize tools from just Uri
dcharkes Apr 13, 2023
9a9b3e7
Use compiler recognizer
dcharkes Apr 13, 2023
5d4b2b9
Test compiler resolver
dcharkes Apr 13, 2023
13b5cba
Cover archiver in BuildConfig
dcharkes Apr 13, 2023
93ea2d6
Cleanup Process.run invocations
dcharkes Apr 13, 2023
1f95e02
Address comments
dcharkes Apr 14, 2023
40f9b35
Pin workflow action versions
dcharkes Apr 14, 2023
c68a59b
Enable running tests from different directories
dcharkes Apr 14, 2023
3907fc8
Fix imports
dcharkes Apr 14, 2023
d3874e9
Add reference to bug
dcharkes Apr 14, 2023
a25c01f
Address comments
dcharkes Apr 14, 2023
293a9a1
At logger output that no CC / AR env variables were set
dcharkes Apr 17, 2023
5bc4ba0
Pick up environment variables for native compilers in tests
dcharkes Apr 17, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .github/workflows/c_compiler.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ jobs:
- uses: dart-lang/setup-dart@d6a63dab3335f427404425de0fbfed4686d93c4f
with:
sdk: ${{matrix.sdk}}
- uses: nttld/setup-ndk@deccd078bf3db957dbdee9862f51955b35ac81dd
with:
ndk-version: r25b

- run: dart pub get

Expand All @@ -40,6 +43,9 @@ jobs:
- run: dart format --output=none --set-exit-if-changed .
if: ${{matrix.run-tests}}

- name: Install native toolchains
run: sudo apt-get install clang-14 gcc-i686-linux-gnu gcc-aarch64-linux-gnu gcc-arm-linux-gnueabihf

- run: dart test
if: ${{matrix.run-tests}}

Expand Down
8 changes: 8 additions & 0 deletions .github/workflows/native_assets_cli.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,22 @@ jobs:
- uses: dart-lang/setup-dart@d6a63dab3335f427404425de0fbfed4686d93c4f
with:
sdk: ${{matrix.sdk}}
- uses: nttld/setup-ndk@deccd078bf3db957dbdee9862f51955b35ac81dd
with:
ndk-version: r25b

- run: dart pub get
- run: dart pub get
working-directory: pkgs/native_assets_cli/example/native_add/

- run: dart analyze --fatal-infos

- run: dart format --output=none --set-exit-if-changed .
if: ${{matrix.run-tests}}

- name: Install native toolchains
run: sudo apt-get install clang-14 gcc-i686-linux-gnu gcc-aarch64-linux-gnu gcc-arm-linux-gnueabihf

- run: dart test
if: ${{matrix.run-tests}}

Expand Down
2 changes: 1 addition & 1 deletion pkgs/c_compiler/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
[![package:c_compiler](https://github.com/dart-lang/native/actions/workflows/c_compiler.yml/badge.svg)](https://github.com/dart-lang/native/actions/workflows/c_compiler.yml)
[![package:c_compiler](https://github.com/dart-lang/native/actions/workflows/c_compiler.yaml/badge.svg)](https://github.com/dart-lang/native/actions/workflows/c_compiler.yaml)
[![pub package](https://img.shields.io/pub/v/c_compiler.svg)](https://pub.dev/packages/c_compiler)
[![Coverage Status](https://coveralls.io/repos/github/dart-lang/native/badge.svg?branch=main)](https://coveralls.io/github/dart-lang/tools?branch=main)
<!-- [![package publisher](https://img.shields.io/pub/publisher/c_compiler.svg)](https://pub.dev/packages/c_compiler/publisher) -->
Expand Down
9 changes: 8 additions & 1 deletion pkgs/c_compiler/lib/c_compiler.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,11 @@
/// A library to invoke the native C compiler installed on the host machine.
library;

export 'src/c_compiler_base.dart';
export 'src/cbuilder/cbuilder.dart';
export 'src/native_toolchain/android_ndk.dart';
export 'src/native_toolchain/clang.dart';
export 'src/native_toolchain/gcc.dart';
export 'src/tool/tool.dart';
export 'src/tool/tool_instance.dart';
export 'src/tool/tool_requirement.dart';
export 'src/tool/tool_resolver.dart';
124 changes: 124 additions & 0 deletions pkgs/c_compiler/lib/src/cbuilder/cbuilder.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
// 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.

import 'dart:io';

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

import 'run_cbuilder.dart';

abstract class Builder {
Future<void> run({
required BuildConfig buildConfig,
required BuildOutput buildOutput,
Logger? logger,
});
}

/// Specification for building an artifact with a C compiler.
class CBuilder implements Builder {
/// What kind of artifact to build.
final _CBuilderType _type;

/// Name of the library or executable to build.
///
/// The filename will be decided by [BuildConfig.target] and
/// [OS.libraryFileName] or [OS.executableFileName].
///
/// File will be placed in [BuildConfig.outDir].
final String name;

/// Asset identifier.
///
/// Used to output the [BuildOutput.assets].
///
/// If omitted, no asset will be added to the build output.
final String? assetName;

/// Sources to build the library or executable.
///
/// Resolved against [BuildConfig.packageRoot].
///
/// Used to output the [BuildOutput.dependencies].
final List<String> sources;

/// The dart files involved in building this artifact.
///
/// Resolved against [BuildConfig.packageRoot].
///
/// Used to output the [BuildOutput.dependencies].
final List<String> dartBuildFiles;

CBuilder.library({
required this.name,
required this.assetName,
this.sources = const [],
this.dartBuildFiles = const ['build.dart'],
}) : _type = _CBuilderType.library;

CBuilder.executable({
required this.name,
this.sources = const [],
this.dartBuildFiles = const ['build.dart'],
}) : _type = _CBuilderType.executable,
assetName = null;

@override
Future<void> run({
required BuildConfig buildConfig,
required BuildOutput buildOutput,
Logger? logger,
}) async {
logger ??= Logger('');
final outDir = buildConfig.outDir;
final packageRoot = buildConfig.packageRoot;
await Directory.fromUri(outDir).create(recursive: true);
final packaging = buildConfig.packaging.preferredPackaging.first;
final libUri =
outDir.resolve(buildConfig.target.os.libraryFileName(name, packaging));
final exeUri =
outDir.resolve(buildConfig.target.os.executableFileName(name));
final sources = [
for (final source in this.sources) packageRoot.resolve(source),
];
final dartBuildFiles = [
for (final source in this.dartBuildFiles) packageRoot.resolve(source),
];

final task = RunCBuilder(
buildConfig: buildConfig,
logger: logger,
sources: sources,
dynamicLibrary:
_type == _CBuilderType.library && packaging == Packaging.dynamic
? libUri
: null,
staticLibrary:
_type == _CBuilderType.library && packaging == Packaging.static
? libUri
: null,
executable: _type == _CBuilderType.executable ? exeUri : null,
);
await task.run();

if (assetName != null) {
buildOutput.assets.add(Asset(
name: assetName!,
packaging: packaging,
target: buildConfig.target,
path: AssetAbsolutePath(libUri),
));
}
buildOutput.dependencies.dependencies.addAll([
...sources,
...dartBuildFiles,
]);
}
}

enum _CBuilderType {
executable,
library,
}
154 changes: 154 additions & 0 deletions pkgs/c_compiler/lib/src/cbuilder/compiler_resolver.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
// 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.

import 'dart:io';

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

import '../native_toolchain/android_ndk.dart';
import '../native_toolchain/clang.dart';
import '../native_toolchain/gcc.dart';
import '../native_toolchain/recognizer.dart';
import '../tool/tool.dart';
import '../tool/tool_error.dart';
import '../tool/tool_instance.dart';

// TODO(dacoharkes): This should support alternatives.
// For example use Clang or MSVC on Windows.
class CompilerResolver {
final BuildConfig buildConfig;
final Logger? logger;
final Target host;

CompilerResolver({
required this.buildConfig,
required this.logger,
Target? host, // Only visible for testing.
}) : host = host ?? Target.current;

Future<ToolInstance> resolveCompiler() async {
// First, check if the launcher provided a direct path to the compiler.
var result = await _tryLoadCompilerFromConfig(
BuildConfig.ccConfigKey,
(buildConfig) => buildConfig.cc,
);

// Then, try to detect on the host machine.
final tool = _selectCompiler();
if (tool != null) {
result ??= await _tryLoadToolFromNativeToolchain(tool);
}

if (result != null) {
return result;
}

final target = buildConfig.target;
final errorMessage =
"No tools configured on host '$host' with target '$target'.";
logger?.severe(errorMessage);
throw ToolError(errorMessage);
}

/// Select the right compiler for cross compiling to the specified target.
Tool? _selectCompiler() {
final target = buildConfig.target;

if (target == host) return clang;
if (target.os == OS.android) return androidNdkClang;
if (host.os == OS.linux) {
switch (target) {
case Target.linuxArm:
return armLinuxGnueabihfGcc;
case Target.linuxArm64:
return aarch64LinuxGnuGcc;
case Target.linuxIA32:
return i686LinuxGnuGcc;
}
}

return null;
}

Future<ToolInstance?> _tryLoadCompilerFromConfig(
String configKey, Uri? Function(BuildConfig) getter) async {
final configCcUri = getter(buildConfig);
if (configCcUri != null) {
assert(await File.fromUri(configCcUri).exists());
logger?.finer('Using compiler ${configCcUri.path} '
'from config[${BuildConfig.ccConfigKey}].');
return (await CompilerRecognizer(configCcUri).resolve(logger: logger))
.first;
}
logger?.finer('No compiler set in config[${BuildConfig.ccConfigKey}].');
return null;
}

Future<ToolInstance?> _tryLoadToolFromNativeToolchain(Tool tool) async {
final resolved = (await tool.defaultResolver!.resolve(logger: logger))
.where((i) => i.tool == tool)
.toList()
..sort();
return resolved.isEmpty ? null : resolved.first;
}

Future<ToolInstance> resolveArchiver() async {
// First, check if the launcher provided a direct path to the compiler.
var result = await _tryLoadArchiverFromConfig(
BuildConfig.arConfigKey,
(buildConfig) => buildConfig.ar,
);

// Then, try to detect on the host machine.
final tool = _selectArchiver();
if (tool != null) {
result ??= await _tryLoadToolFromNativeToolchain(tool);
}

if (result != null) {
return result;
}

final target = buildConfig.target;
final errorMessage =
"No tools configured on host '$host' with target '$target'.";
logger?.severe(errorMessage);
throw ToolError(errorMessage);
}

/// Select the right archiver for cross compiling to the specified target.
Tool? _selectArchiver() {
final target = buildConfig.target;

if (target == host) return llvmAr;
if (target.os == OS.android) return androidNdkLlvmAr;
if (host.os == OS.linux) {
switch (target) {
case Target.linuxArm:
return armLinuxGnueabihfGccAr;
case Target.linuxArm64:
return aarch64LinuxGnuGccAr;
case Target.linuxIA32:
return i686LinuxGnuGccAr;
}
}

return null;
}

Future<ToolInstance?> _tryLoadArchiverFromConfig(
String configKey, Uri? Function(BuildConfig) getter) async {
final configArUri = getter(buildConfig);
if (configArUri != null) {
assert(await File.fromUri(configArUri).exists());
logger?.finer('Using archiver ${configArUri.path} '
'from config[${BuildConfig.arConfigKey}].');
return (await ArchiverRecognizer(configArUri).resolve(logger: logger))
.first;
}
logger?.finer('No archiver set in config[${BuildConfig.arConfigKey}].');
return null;
}
}
Loading