diff --git a/pkgs/ffigen/CHANGELOG.md b/pkgs/ffigen/CHANGELOG.md
index f95edb7b3..08ba80a32 100644
--- a/pkgs/ffigen/CHANGELOG.md
+++ b/pkgs/ffigen/CHANGELOG.md
@@ -1,3 +1,10 @@
+## 11.0.0-wip
+
+- Any compiler errors/warnings in source header files will now result in
+bindings to **not** be generated by default, since it may result in invalid
+bindings if the compiler makes a wrong guess. A flag `--ignore-source-errors` (or yaml config `ignore-source-errors: true`)
+must be passed to change this behaviour.
+
## 10.0.0
- Stable release targeting Dart 3.2 using new `dart:ffi` features available
diff --git a/pkgs/ffigen/README.md b/pkgs/ffigen/README.md
index b5845c341..4323f559a 100644
--- a/pkgs/ffigen/README.md
+++ b/pkgs/ffigen/README.md
@@ -452,6 +452,24 @@ use-supported-typedefs: true
```yaml
use-dart-handle: true
```
+
+
+
+
+ ignore-source-errors |
+ Where to ignore compiler warnings/errors in source header files.
+ Default: false
+ |
+
+
+```yaml
+ignore-source-errors: true
+```
+and/or via the command line -
+```bash
+dart run ffigen --ignore-source-errors
+```
+
|
diff --git a/pkgs/ffigen/doc/errors.md b/pkgs/ffigen/doc/errors.md
new file mode 100644
index 000000000..51ec63460
--- /dev/null
+++ b/pkgs/ffigen/doc/errors.md
@@ -0,0 +1,26 @@
+# Errors in ffigen
+
+This file documents various errors and their potential fixes related to ffigen.
+
+## Errors in source header files
+
+FFIgen uses libclang to parse header files. Any compiler warnings/errors should be logged (with SEVERE level).
+Compiler errors and warnings should be resolved as they can potentially generate invalid bindings that might cause silent errors and crashes at runtime.
+
+> You can pass in args to libclang using `compiler-opts` via cmd line or yaml config or both.
+
+Here we'll list some common usecases. You can find the full list of [supported args here](https://clang.llvm.org/docs/ClangCommandLineReference.html#id5).
+
+### Missing headers
+
+These are the most common source file errors. You can specify [include paths to clang](https://clang.llvm.org/docs/ClangCommandLineReference.html#id6) like this
+```yaml
+compiler-opts:
+ - "-I/path/to/folder"
+```
+
+### Ignoring source errors
+
+As a last resort, you can pass in `--ignore-source-errors` or set `ignore-source-errors: true` in yaml config.
+
+**Warning: This will likely lead to incorrect bindings!**
diff --git a/pkgs/ffigen/ffigen.schema.json b/pkgs/ffigen/ffigen.schema.json
index 5c196f1f1..2116c4f01 100644
--- a/pkgs/ffigen/ffigen.schema.json
+++ b/pkgs/ffigen/ffigen.schema.json
@@ -1,6 +1,6 @@
{
"$id": "https://json.schemastore.org/ffigen",
- "$comment": "This file is generated. To regenerate run: dart tool/generate_json_schema.dart in github.com/dart-lang/ffigen",
+ "$comment": "This file is generated. To regenerate run: dart tool/generate_json_schema.dart in github.com/dart-lang/native/tree/main/pkgs/ffigen",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"additionalProperties": false,
@@ -73,6 +73,9 @@
"entry-points"
]
},
+ "ignore-source-errors": {
+ "type": "boolean"
+ },
"compiler-opts": {
"$oneOf": [
{
diff --git a/pkgs/ffigen/lib/src/config_provider/config.dart b/pkgs/ffigen/lib/src/config_provider/config.dart
index d47e30e92..69740f4b7 100644
--- a/pkgs/ffigen/lib/src/config_provider/config.dart
+++ b/pkgs/ffigen/lib/src/config_provider/config.dart
@@ -176,6 +176,9 @@ class Config {
FfiNativeConfig get ffiNativeConfig => _ffiNativeConfig;
late FfiNativeConfig _ffiNativeConfig;
+ /// Where to ignore compiler warnings/errors in source header files.
+ bool ignoreSourceErrors = false;
+
Config._({required this.filename, required this.packageConfig});
/// Create config from Yaml map.
@@ -285,6 +288,15 @@ class Config {
transform: (node) => headersExtractor(node.value, filename),
result: (node) => _headers = node.value,
)),
+ HeterogeneousMapEntry(
+ key: strings.ignoreSourceErrors,
+ valueConfigSpec: BoolConfigSpec(),
+ defaultValue: (node) => false,
+ resultOrDefault: (node) {
+ // Set value to true if not already.
+ ignoreSourceErrors = ignoreSourceErrors || node.value as bool;
+ },
+ ),
HeterogeneousMapEntry(
key: strings.compilerOpts,
valueConfigSpec: OneOfConfigSpec, List>(
diff --git a/pkgs/ffigen/lib/src/config_provider/config_spec.dart b/pkgs/ffigen/lib/src/config_provider/config_spec.dart
index 3d67be9dc..320ed1668 100644
--- a/pkgs/ffigen/lib/src/config_provider/config_spec.dart
+++ b/pkgs/ffigen/lib/src/config_provider/config_spec.dart
@@ -66,7 +66,7 @@ abstract class ConfigSpec {
return {
r"$id": schemaId,
r"$comment":
- "This file is generated. To regenerate run: dart tool/generate_json_schema.dart in github.com/dart-lang/ffigen",
+ "This file is generated. To regenerate run: dart tool/generate_json_schema.dart in github.com/dart-lang/native/tree/main/pkgs/ffigen",
r"$schema": "https://json-schema.org/draft/2020-12/schema",
...schemaMap,
r"$defs": defs,
diff --git a/pkgs/ffigen/lib/src/executables/ffigen.dart b/pkgs/ffigen/lib/src/executables/ffigen.dart
index c37cb4f85..1c6008592 100644
--- a/pkgs/ffigen/lib/src/executables/ffigen.dart
+++ b/pkgs/ffigen/lib/src/executables/ffigen.dart
@@ -16,6 +16,7 @@ final _logger = Logger('ffigen.ffigen');
final _ansi = Ansi(Ansi.terminalSupportsAnsi);
const compilerOpts = 'compiler-opts';
+const ignoreSourceErrors = 'ignore-source-errors';
const conf = 'config';
const help = 'help';
const verbose = 'verbose';
@@ -87,6 +88,10 @@ Config getConfig(ArgResults result, PackageConfig? packageConfig) {
highPriority: true);
}
+ if (result.wasParsed(ignoreSourceErrors)) {
+ config.ignoreSourceErrors = true;
+ }
+
return config;
}
@@ -158,6 +163,11 @@ ArgResults getArgResults(List args) {
compilerOpts,
help: 'Compiler options for clang. (E.g --$compilerOpts "-I/headers -W")',
);
+ parser.addFlag(
+ ignoreSourceErrors,
+ help: 'Ignore any compiler warnings/errors in source header files',
+ negatable: false,
+ );
ArgResults results;
try {
diff --git a/pkgs/ffigen/lib/src/header_parser/clang_bindings/clang_bindings.dart b/pkgs/ffigen/lib/src/header_parser/clang_bindings/clang_bindings.dart
index d5431a958..1de29c93b 100644
--- a/pkgs/ffigen/lib/src/header_parser/clang_bindings/clang_bindings.dart
+++ b/pkgs/ffigen/lib/src/header_parser/clang_bindings/clang_bindings.dart
@@ -316,6 +316,21 @@ class Clang {
late final _clang_formatDiagnostic = _clang_formatDiagnosticPtr
.asFunction();
+ /// Determine the severity of the given diagnostic.
+ int clang_getDiagnosticSeverity(
+ CXDiagnostic arg0,
+ ) {
+ return _clang_getDiagnosticSeverity(
+ arg0,
+ );
+ }
+
+ late final _clang_getDiagnosticSeverityPtr =
+ _lookup>(
+ 'clang_getDiagnosticSeverity');
+ late final _clang_getDiagnosticSeverity =
+ _clang_getDiagnosticSeverityPtr.asFunction();
+
/// Same as \c clang_parseTranslationUnit2, but returns
/// the \c CXTranslationUnit instead of an error code. In case of an error this
/// routine returns a \c NULL \c CXTranslationUnit, without further detailed
@@ -1484,6 +1499,29 @@ abstract class CXDiagnosticDisplayOptions {
static const int CXDiagnostic_DisplayCategoryName = 32;
}
+/// Describes the severity of a particular diagnostic.
+abstract class CXDiagnosticSeverity {
+ /// A diagnostic that has been suppressed, e.g., by a command-line
+ /// option.
+ static const int CXDiagnostic_Ignored = 0;
+
+ /// This diagnostic is a note that should be attached to the
+ /// previous (non-note) diagnostic.
+ static const int CXDiagnostic_Note = 1;
+
+ /// This diagnostic indicates suspicious code that may not be
+ /// wrong.
+ static const int CXDiagnostic_Warning = 2;
+
+ /// This diagnostic indicates that the code is ill-formed.
+ static const int CXDiagnostic_Error = 3;
+
+ /// This diagnostic indicates that the code is ill-formed such
+ /// that future parser recovery is unlikely to produce useful
+ /// results.
+ static const int CXDiagnostic_Fatal = 4;
+}
+
/// Flags that control the creation of translation units.
///
/// The enumerators in this enumeration type are meant to be bitwise
@@ -2641,8 +2679,10 @@ abstract class CXChildVisitResult {
/// The visitor should return one of the \c CXChildVisitResult values
/// to direct clang_visitCursorChildren().
typedef CXCursorVisitor
- = ffi.Pointer>;
-typedef CXCursorVisitor_function = ffi.Int32 Function(
+ = ffi.Pointer>;
+typedef CXCursorVisitorFunction = ffi.Int32 Function(
+ CXCursor cursor, CXCursor parent, CXClientData client_data);
+typedef DartCXCursorVisitorFunction = int Function(
CXCursor cursor, CXCursor parent, CXClientData client_data);
/// Opaque pointer representing client data that will be passed through
diff --git a/pkgs/ffigen/lib/src/header_parser/data.dart b/pkgs/ffigen/lib/src/header_parser/data.dart
index 5bd4e903a..bab37af43 100644
--- a/pkgs/ffigen/lib/src/header_parser/data.dart
+++ b/pkgs/ffigen/lib/src/header_parser/data.dart
@@ -45,6 +45,10 @@ List _unnamedEnumConstants = [];
ObjCBuiltInFunctions get objCBuiltInFunctions => _objCBuiltInFunctions;
late ObjCBuiltInFunctions _objCBuiltInFunctions;
+/// Tracks if any source error/warning has occured which can potentially cause
+/// invalid generated bindings.
+bool hasSourceErrors = false;
+
void initializeGlobals({required Config config}) {
_config = config;
_clang = Clang(DynamicLibrary.open(config.libclangDylib));
@@ -54,4 +58,5 @@ void initializeGlobals({required Config config}) {
_cursorIndex = CursorIndex();
_bindingsIndex = BindingsIndex();
_objCBuiltInFunctions = ObjCBuiltInFunctions();
+ hasSourceErrors = false;
}
diff --git a/pkgs/ffigen/lib/src/header_parser/parser.dart b/pkgs/ffigen/lib/src/header_parser/parser.dart
index 5be826fdf..fce0b4926 100644
--- a/pkgs/ffigen/lib/src/header_parser/parser.dart
+++ b/pkgs/ffigen/lib/src/header_parser/parser.dart
@@ -22,7 +22,7 @@ import 'utils.dart';
Library parse(Config c) {
initParser(c);
- final bindings = parseToBindings();
+ final bindings = parseToBindings(c);
final library = Library(
bindings: bindings,
@@ -52,7 +52,7 @@ void initParser(Config c) {
}
/// Parses source files and adds generated bindings to [bindings].
-List parseToBindings() {
+List parseToBindings(Config c) {
final index = clang.clang_createIndex(0, 0);
Pointer> clangCmdArgs = nullptr;
@@ -113,6 +113,21 @@ List parseToBindings() {
tuList.add(tu);
}
+ if (hasSourceErrors) {
+ _logger.warning("The compiler found warnings/errors in source files.");
+ _logger.warning("This will likely generate invalid bindings.");
+ if (config.ignoreSourceErrors) {
+ _logger.warning(
+ "Ignored source errors. (User supplied --ignore-source-errors)");
+ } else if (config.language == Language.objc) {
+ _logger.warning("Ignored source errors. (ObjC)");
+ } else {
+ _logger.severe(
+ "Skipped generating bindings due to errors in source files. See https://github.com/dart-lang/native/blob/main/pkgs/ffigen/doc/errors.md.");
+ exit(1);
+ }
+ }
+
final tuCursors =
tuList.map((tu) => clang.clang_getTranslationUnitCursor(tu));
diff --git a/pkgs/ffigen/lib/src/header_parser/utils.dart b/pkgs/ffigen/lib/src/header_parser/utils.dart
index f03df3fb5..423d7cdba 100644
--- a/pkgs/ffigen/lib/src/header_parser/utils.dart
+++ b/pkgs/ffigen/lib/src/header_parser/utils.dart
@@ -39,6 +39,10 @@ void logTuDiagnostics(
logger.log(logLevel, 'Header $header: Total errors/warnings: $total.');
for (var i = 0; i < total; i++) {
final diag = clang.clang_getDiagnostic(tu, i);
+ if (clang.clang_getDiagnosticSeverity(diag) >=
+ clang_types.CXDiagnosticSeverity.CXDiagnostic_Warning) {
+ hasSourceErrors = true;
+ }
final cxstring = clang.clang_formatDiagnostic(
diag,
clang_types
diff --git a/pkgs/ffigen/lib/src/strings.dart b/pkgs/ffigen/lib/src/strings.dart
index 0918f48e7..b14a51791 100644
--- a/pkgs/ffigen/lib/src/strings.dart
+++ b/pkgs/ffigen/lib/src/strings.dart
@@ -200,6 +200,7 @@ const supportedNativeType_mappings = {
const sort = 'sort';
const useSupportedTypedefs = 'use-supported-typedefs';
const useDartHandle = 'use-dart-handle';
+const ignoreSourceErrors = 'ignore-source-errors';
const comments = 'comments';
// Sub-fields of comments.
diff --git a/pkgs/ffigen/pubspec.yaml b/pkgs/ffigen/pubspec.yaml
index 5504c2afa..b926ee5db 100644
--- a/pkgs/ffigen/pubspec.yaml
+++ b/pkgs/ffigen/pubspec.yaml
@@ -3,7 +3,7 @@
# BSD-style license that can be found in the LICENSE file.
name: ffigen
-version: 10.0.0
+version: 11.0.0-wip
description: >
Generator for FFI bindings, using LibClang to parse C, Objective-C, and Swift
files.
diff --git a/pkgs/ffigen/test/header_parser_tests/forward_decl_test.dart b/pkgs/ffigen/test/header_parser_tests/forward_decl_test.dart
index 73b9f3210..eed833467 100644
--- a/pkgs/ffigen/test/header_parser_tests/forward_decl_test.dart
+++ b/pkgs/ffigen/test/header_parser_tests/forward_decl_test.dart
@@ -23,6 +23,7 @@ ${strings.output}: 'unused'
${strings.headers}:
${strings.entryPoints}:
- 'test/header_parser_tests/forward_decl.h'
+${strings.ignoreSourceErrors}: true
'''),
);
});
diff --git a/pkgs/ffigen/test/header_parser_tests/unions_test.dart b/pkgs/ffigen/test/header_parser_tests/unions_test.dart
index e248dfb81..55084ac8c 100644
--- a/pkgs/ffigen/test/header_parser_tests/unions_test.dart
+++ b/pkgs/ffigen/test/header_parser_tests/unions_test.dart
@@ -23,6 +23,7 @@ ${strings.output}: 'unused'
${strings.headers}:
${strings.entryPoints}:
- 'test/header_parser_tests/unions.h'
+${strings.ignoreSourceErrors}: true
'''),
);
});
diff --git a/pkgs/ffigen/tool/libclang_config.yaml b/pkgs/ffigen/tool/libclang_config.yaml
index f569afd1c..90a3206b6 100644
--- a/pkgs/ffigen/tool/libclang_config.yaml
+++ b/pkgs/ffigen/tool/libclang_config.yaml
@@ -61,6 +61,7 @@ functions:
- clang_disposeIndex
- clang_getNumDiagnostics
- clang_getDiagnostic
+ - clang_getDiagnosticSeverity
- clang_disposeDiagnostic
- clang_parseTranslationUnit
- clang_disposeTranslationUnit