blob: ff0d6fb571a958b03f1f15e452985601179527aa [file]
// 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 'dart:io';
import 'package:code_assets/code_assets.dart';
import 'package:hooks/hooks.dart';
import 'package:logging/logging.dart';
import 'package:meta/meta.dart';
import 'cbuilder.dart';
import 'clibrary.dart';
import 'ctool.dart';
import 'linker_options.dart';
import 'linkmode.dart';
import 'logger.dart';
import 'output_type.dart';
import 'run_cbuilder.dart';
/// Specification for linking an artifact with a C linker.
class CLinker extends CTool implements Linker {
final LinkerOptions? linkerOptions;
CLinker.library({
required super.name,
super.packageName,
super.assetName,
this.linkerOptions,
super.sources = const [],
super.includes = const [],
super.forcedIncludes = const [],
super.frameworks = CTool.defaultFrameworks,
super.libraries = const [],
super.libraryDirectories = CTool.defaultLibraryDirectories,
@visibleForTesting super.installName,
super.flags = const [],
super.defines = const {},
super.pic = true,
super.std,
super.language = .c,
super.cppLinkStdLib,
super.linkModePreference,
super.optimizationLevel = .o3,
}) : super(type: OutputType.library);
/// Runs the C Linker with on this C build spec.
///
/// Completes with an error if the linking fails.
///
/// If provided, uses [logger] to output logs. Otherwise, uses a default
/// logger that streams [Level.WARNING] to stdout and higher levels to stderr.
///
/// [linkerOptions] overrides the [CLinker.linkerOptions] of this [CLinker].
/// See [CLinker.linkerOptions] for more documentation.
///
/// [linkModePreference] overrides the [CTool.linkModePreference] of this
/// [CLinker]. See [CTool.linkModePreference] for more documentation.
///
/// [sources] overrides the [CTool.sources] of this [CLinker]. See
/// [CTool.sources] for more documentation.
///
/// [defines] are merged with the [CTool.defines] of this [CLinker]. See
/// [CTool.defines] for more documentation.
///
/// If you're using [CBuilder] in a build hook and [CLinker] in a link hook,
/// see [CLibrary] to combine them.
@override
Future<void> run({
required LinkInput input,
required LinkOutputBuilder output,
Logger? logger,
LinkerOptions? linkerOptions,
LinkModePreference? linkModePreference,
List<String>? sources,
Map<String, String?>? defines,
}) async {
logger ??= createDefaultLogger();
final outDir = input.outputDirectory;
final packageRoot = input.packageRoot;
await Directory.fromUri(outDir).create(recursive: true);
final linkMode = getLinkMode(
linkModePreference ??
this.linkModePreference ??
input.config.code.linkModePreference,
);
final libUri = outDir.resolve(
input.config.code.targetOS.libraryFileName(name, linkMode),
);
final resolvedSources = [
for (final source in sources ?? this.sources)
packageRoot.resolveUri(Uri.file(source)),
];
final includes = [
for (final directory in this.includes)
packageRoot.resolveUri(Uri.file(directory)),
];
final libraryDirectories = [
for (final directory in this.libraryDirectories)
outDir.resolveUri(Uri.file(directory)),
];
final task = RunCBuilder(
input: input,
codeConfig: input.config.code,
linkerOptions: linkerOptions ?? this.linkerOptions,
logger: logger,
sources: resolvedSources,
includes: includes,
frameworks: frameworks,
libraries: libraries,
libraryDirectories: libraryDirectories,
dynamicLibrary: linkMode == DynamicLoadingBundled() ? libUri : null,
staticLibrary: linkMode == StaticLinking() ? libUri : null,
// ignore: invalid_use_of_visible_for_testing_member
installName: installName,
flags: flags,
defines: {...this.defines, ...?defines},
pic: pic,
std: std,
language: language,
cppLinkStdLib: cppLinkStdLib,
optimizationLevel: optimizationLevel,
);
await task.run();
if (assetName != null) {
output.assets.code.add(
CodeAsset(
package: packageName ?? input.packageName,
name: assetName!,
file: libUri,
linkMode: linkMode,
),
);
}
final includeFiles = await Stream.fromIterable(includes)
.asyncExpand(
(include) => Directory(include.toFilePath())
.list(recursive: true)
.where((entry) => entry is File)
.map((file) => file.uri),
)
.toList();
output.dependencies.addAll({
// Note: We use a Set here to deduplicate the dependencies.
...resolvedSources,
...includeFiles,
});
}
}