blob: 0a6978a6f27d8ecd4067dd5f399f574f8ad998fd [file] [log] [blame]
// Copyright (c) 2020, 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:cli_util/cli_util.dart';
import 'package:ffigen/src/config_provider/config_types.dart';
import 'package:logging/logging.dart';
import 'package:path/path.dart' as p;
import 'binding.dart';
import 'struc.dart';
import 'utils.dart';
import 'writer.dart';
final _logger = Logger('ffigen.code_generator.library');
/// Container for all Bindings.
class Library {
/// List of bindings in this library.
late List<Binding> bindings;
late Writer _writer;
Writer get writer => _writer;
required String name,
String? description,
required List<Binding> bindings,
String? header,
bool dartBool = true,
bool sort = false,
StructPackingOverride? packingOverride,
}) {
/// Get all dependencies (includes itself).
final dependencies = <Binding>{};
for (final b in bindings) {
/// Save bindings.
this.bindings = dependencies.toList();
if (sort) {
/// Handle any declaration-declaration name conflicts.
final declConflictHandler = UniqueNamer({});
for (final b in this.bindings) {
_resolveIfNameConflicts(declConflictHandler, b);
// Override pack values according to config. We do this after declaration
// conflicts have been handled so that users can target the generated names.
if (packingOverride != null) {
for (final b in this.bindings) {
if (b is Struc && packingOverride.isOverriden( {
b.pack = packingOverride.getOverridenPackValue(;
// Seperate bindings which require lookup.
final lookUpBindings = this.bindings.whereType<LookUpBinding>().toList();
final noLookUpBindings =
_writer = Writer(
lookUpBindings: lookUpBindings,
noLookUpBindings: noLookUpBindings,
className: name,
classDocComment: description,
header: header,
dartBool: dartBool,
/// Logs a warning if generated declaration will be private.
void _warnIfPrivateDeclaration(Binding b) {
if ('_')) {
"Generated declaration '${}' start's with '_' and therefore will be private.");
/// Resolves name conflict(if any) and logs a warning.
void _resolveIfNameConflicts(UniqueNamer namer, Binding b) {
// Print warning if name was conflicting and has been changed.
if (namer.isUsed( {
final oldName =; = namer.makeUnique(;
"Resolved name conflict: Declaration '$oldName' and has been renamed to '${}'.");
} else {
/// Sort all bindings in alphabetical order.
void _sort() {
bindings.sort((b1, b2) =>;
/// Generates [file] by generating C bindings.
/// If format is true(default), 'dartfmt -w $PATH' will be called to format the generated file.
void generateFile(File file, {bool format = true}) {
if (!file.existsSync()) file.createSync(recursive: true);
if (format) {
/// Formats a file using `dartfmt`.
void _dartFmt(String path) {
final sdkPath = getSdkPath();
final result = Process.runSync(
p.join(sdkPath, 'bin', 'dart'), ['format', path],
runInShell: Platform.isWindows);
if (result.stderr.toString().isNotEmpty) {
throw FormatException('Unable to format generated file: $path.');
/// Generates the bindings.
String generate() {
return writer.generate();
bool operator ==(other) => other is Library && other.generate() == generate();
int get hashCode => bindings.hashCode;