Prepare for pub release (#36)
- Updated version, changelog, readme, tests
- Dynamic library is now created at `.dart_tool/ffigen` in user's project.
- Added `bin/setup.dart` to load dynamic library (custom header/lib includes can be specified using `-I` / `-L` respectively.
- Generated classes for enums are now abstract.
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8fcd296..40872de 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,11 @@
# 0.1.0
-- Initial release supporing binding generation for Functions, Structs, Enums.
+- Support for Functions, Structs and Enums.
- Glob support for specifying headers.
-- HeaderFilter - Include/Exclude declarations from specific header files.
+- HeaderFilter - Include/Exclude declarations from specific header files using name matching.
- Filters - Include/Exclude function, structs and enum declarations using Regexp or Name matching.
+- Prefixing - function, structs and enums can have a global prefix. Individual prefix Replacement support using Regexp.
+- Comment extraction: full/brief/none
+- Support for fixed size arrays in struct. `array-workaround` (if enabled) will generate helpers for accessing fixed size arrays in structs.
+- Size for ints can be specified using `size-map` in config.
+- Options to disable using supported typedefs (e.g `uint8_t => Uint8`), sort bindings.
+- Option to add a raw `preamble` which is included as is in the generated file.
diff --git a/README.md b/README.md
index 31dd16b..14053ce 100644
--- a/README.md
+++ b/README.md
@@ -33,27 +33,24 @@
typedef _dart_sum = int Function(int a,int b);
```
## Using this package
-- clone/download this repository.
-- Build it (see [building](#building)).
- Add this package as dev_dependency in your `pubspec.yaml`.
+- Setup for use (see [Setup](#Setup)).
- Configurations must be provided in `pubspec.yaml` or in a custom YAML file (see [configurations](#configurations)).
- Run the tool- `pub run ffigen`.
-## Building
-A dynamic library for a wrapper to libclang needs to be generated as it is used by the parser submodule.
+## Setup
+`package:ffigen` uses LLVM. Install LLVM in the following way.
#### ubuntu/linux
1. Install libclangdev - `sudo apt-get install libclang-dev`.
-2. `cd tool/wrapped_libclang`, then run `dart build.dart`.
#### Windows
1. Install Visual Studio with C++ development support.
-2. Install LLVM.
-3. `cd tool\wrapped_libclang`, then run `dart build.dart`.
+2. Install [LLVM](https://releases.llvm.org/download.html).
#### MacOS
-1. Install LLVM.
-2. `cd tool/wrapped_libclang`, then run `dart build.dart`.
+1. Install Xcode.
+2. Install LLVM - `brew install llvm`.
## Configurations
Configurations can be provided in 2 ways-
@@ -154,16 +151,6 @@
<td><pre lang='yaml'>comments: 'full'</pre></td>
</tr>
<tr>
- <td>libclang-dylib-folder</td>
- <td>no</td>
- <td>Path to the folder containing dynamic library for libclang wrapper.<br>
- Note: This is meant for programatic use of this package, and isn't required
- when using it via the <code>pub run ffigen</code> command.<br>
- <b>Default: tool/wrapped_libclang</b>
- </td>
- <td><pre lang='yaml'>libclang-dylib-folder: 'tool/wrapped_libclang'</pre></td>
- </tr>
- <tr>
<td>sort</td>
<td>no</td>
<td>Sort the bindings according to name.<br>
@@ -299,8 +286,9 @@
2. Run `pub run ffigen`.
## Running Tests
-Dynamic library for some tests need to be built before running the examples.
-1. `cd test/native_test`.
-2. Run `dart build_test_dylib.dart`.
+1. Run setup to build the LLVM wrapper - `pub run ffigen:setup`.
+2. Dynamic library for some tests also need to be built before running the examples.
+ 1. `cd test/native_test`.
+ 2. Run `dart build_test_dylib.dart`.
Run tests from the root of the package with `pub run test`.
diff --git a/bin/ffigen.dart b/bin/ffigen.dart
index 4a321ee..2f0a07f 100644
--- a/bin/ffigen.dart
+++ b/bin/ffigen.dart
@@ -11,9 +11,20 @@
import 'package:logging/logging.dart';
import 'package:yaml/yaml.dart' as yaml;
+import 'setup.dart';
+
var _logger = Logger('ffigen.ffigen');
void main(List<String> args) {
+ /// Prompt user if dylib doesn't exist and cannot be auto created to run
+ /// `pub run ffigen:setup -Ipath/to/llvm/include -Lpath/to/llvm/lib`.
+ if (!checkDylibExist() && !autoCreateDylib()) {
+ print('Unable to create dynamic library automatically.');
+ print('If LLVM is installed, try running:');
+ print(' pub run ffigen:setup -Ipath/to/llvm/include -Lpath/to/llvm/lib');
+ exit(1);
+ }
+
// Parses the cmd args.
final result = getArgResults(args);
diff --git a/bin/setup.dart b/bin/setup.dart
new file mode 100644
index 0000000..ee52412
--- /dev/null
+++ b/bin/setup.dart
@@ -0,0 +1,288 @@
+// 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.
+
+/// =======================================================================
+/// =============== Build script to generate dyamic library ===============
+/// =======================================================================
+/// This Script effectively calls the following (but user can provide
+/// command line args which will replace the defaults shown below)-
+///
+/// Linux:
+/// ```
+/// clang -I/usr/lib/llvm-9/include/ -I/usr/lib/llvm-10/include/ -lclang -shared -fpic path/to/wrapper.c -o path/to/libwrapped_clang.so
+/// ```
+/// MacOS:
+/// ```
+/// clang -I/usr/local/opt/llvm/include/ -L/usr/local/opt/llvm/lib/ -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/ -v -lclang -shared -fpic path/to/wrapper.c -o path/to/libwrapped_clang.dylib
+/// ```
+/// Windows:
+/// ```
+/// clang -IC:\Progra~1\LLVM\include -LC:\Progra~1\LLVM\lib -llibclang -shared path/to/wrapper.c -o path/to/wrapped_clang.dll -Wl,/DEF:path/to/wrapper.def
+/// ```
+/// =======================================================================
+/// =======================================================================
+/// =======================================================================
+
+import 'dart:io';
+import 'package:args/args.dart';
+import 'package:meta/meta.dart';
+import 'package:ffigen/src/find_dot_dart_tool.dart';
+import 'package:ffigen/src/strings.dart' as strings;
+import 'package:path/path.dart' as path;
+
+const _macOS = 'macos';
+const _windows = 'windows';
+const _linux = 'linux';
+
+/// Default platform options.
+Map<String, _Options> _platformOptions = {
+ _linux: _Options(
+ sharedFlag: '-shared',
+ inputHeader: _getWrapperPath('wrapper.c'),
+ fPIC: '-fpic',
+ ldLibFlag: '-lclang',
+ headerIncludes: [
+ '-I/usr/lib/llvm-9/include/',
+ '-I/usr/lib/llvm-10/include/',
+ ],
+ ),
+ _windows: _Options(
+ sharedFlag: '-shared',
+ inputHeader: _getWrapperPath('wrapper.c'),
+ moduleDefPath: '-Wl,/DEF:${_getWrapperPath("wrapper.def")}',
+ ldLibFlag: '-llibclang',
+ headerIncludes: [
+ r'-IC:\Progra~1\LLVM\include',
+ ],
+ libIncludes: [
+ r'-LC:\Progra~1\LLVM\lib',
+ ],
+ ),
+ _macOS: _Options(
+ sharedFlag: '-shared',
+ inputHeader: _getWrapperPath('wrapper.c'),
+ fPIC: '-fpic',
+ ldLibFlag: '-lclang',
+ headerIncludes: [
+ '-I/usr/local/opt/llvm/include/',
+ '-I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/',
+ ],
+ libIncludes: [
+ '-L/usr/local/opt/llvm/lib/',
+ ],
+ ),
+};
+
+/// If main is called directly we always re-create the dynamic library.
+void main(List<String> arguments) {
+ print('Building Dynamic Library for libclang wrapper...');
+ final options = _getPlatformOptions();
+ _deleteOldDylib();
+
+ // Updates header/lib includes in platform options.
+ _changeIncludesUsingCmdArgs(arguments, options);
+
+ // Run clang compiler to generate the dynamic library.
+ final ProcessResult result = _runClangProcess(options);
+ _printDetails(result, options);
+}
+
+/// Returns true if auto creating dylib was successful.
+///
+/// This will fail if llvm is not in default directories or if .dart_tool
+/// doesn't exist.
+bool autoCreateDylib() {
+ _deleteOldDylib();
+ final options = _getPlatformOptions();
+ final ProcessResult result = _runClangProcess(options);
+ if ((result.stderr as String).isNotEmpty) {
+ print(stderr);
+ }
+ return checkDylibExist();
+}
+
+bool checkDylibExist() {
+ return File(path.join(
+ _getDotDartToolPath(),
+ strings.ffigenFolderName,
+ strings.dylibFileName,
+ )).existsSync();
+}
+
+/// Removes old dynamic libraries(if any) by deleting .dart_tool/ffigen.
+///
+/// Throws error if '.dart_tool' is not found.
+void _deleteOldDylib() {
+ // Find .dart_tool.
+ final dtpath = _getDotDartToolPath();
+ // Find .dart_tool/ffigen and delete recursively if it exists.
+ final ffigenDir = Directory(path.join(dtpath, strings.ffigenFolderName));
+ if (ffigenDir.existsSync()) ffigenDir.deleteSync(recursive: true);
+}
+
+/// Creates necesarry parent folders and return full path to dylib.
+String _dylibPath() {
+ // Find .dart_tool.
+ final dtpath = _getDotDartToolPath();
+ // Create .dart_tool/ffigen if it doesn't exists.
+ final ffigenDir = Directory(path.join(dtpath, strings.ffigenFolderName));
+ if (!ffigenDir.existsSync()) ffigenDir.createSync();
+
+ // Return dylib path
+ return path.join(ffigenDir.absolute.path, strings.dylibFileName);
+}
+
+/// Returns full path of the wrapper files.
+///
+/// Throws error if not found.
+String _getWrapperPath(String wrapperName) {
+ final file = File(path.join(
+ Platform.script
+ .resolve(path.posix.join('..', 'lib', 'src', 'clang_library'))
+ // This needs to be in posix style or illegal character exception is
+ // thrown on windows.
+ .toFilePath(),
+ wrapperName,
+ ));
+ if (file.existsSync()) {
+ return file.absolute.path;
+ } else {
+ throw Exception('Unable to find $wrapperName file.');
+ }
+}
+
+/// Gets full path to .dart_tool.
+///
+/// Throws Exception if not found.
+String _getDotDartToolPath() {
+ final dtpath = findDotDartTool()?.toFilePath();
+ if (dtpath == null) {
+ throw Exception('.dart_tool not found.');
+ }
+ return dtpath;
+}
+
+/// Calls the clang compiler.
+ProcessResult _runClangProcess(_Options options) {
+ final result = Process.runSync(
+ 'clang',
+ [
+ ...options.headerIncludes,
+ ...options.libIncludes,
+ options.ldLibFlag,
+ options.sharedFlag,
+ options.fPIC,
+ options.inputHeader,
+ '-o',
+ _dylibPath(),
+ options.moduleDefPath,
+ '-Wno-nullability-completeness',
+ ],
+ );
+ return result;
+}
+
+/// Prints success message (or process error if any).
+void _printDetails(ProcessResult result, _Options options) {
+ print(result.stdout);
+ if ((result.stderr as String).isNotEmpty) {
+ print(result.stderr);
+ } else {
+ print('Created dynamic library.');
+ }
+}
+
+ArgResults _getArgResults(List<String> args) {
+ final parser = ArgParser(allowTrailingOptions: true);
+ parser.addSeparator(
+ 'Build Script to generate dynamic library used by this package:');
+ parser.addMultiOption('include-header',
+ abbr: 'I', help: 'Path to header include directories');
+ parser.addMultiOption('include-lib',
+ abbr: 'L', help: 'Path to library include directories');
+ parser.addFlag(
+ 'help',
+ abbr: 'h',
+ help: 'prints this usage',
+ negatable: false,
+ );
+
+ ArgResults results;
+ try {
+ results = parser.parse(args);
+
+ if (results.wasParsed('help')) {
+ print(parser.usage);
+ exit(0);
+ }
+ } catch (e) {
+ print(e);
+ print(parser.usage);
+ exit(1);
+ }
+
+ return results;
+}
+
+/// Use cmd args(if any) to change header/lib include paths.
+void _changeIncludesUsingCmdArgs(List<String> arguments, _Options options) {
+ final argResult = _getArgResults(arguments);
+ if (argResult.wasParsed('include-header')) {
+ options.headerIncludes = (argResult['include-header'] as List<String>)
+ .map((header) => '-I$header')
+ .toList();
+ }
+ if (argResult.wasParsed('include-lib')) {
+ options.libIncludes = (argResult['include-lib'] as List<String>)
+ .map((lib) => '-L$lib')
+ .toList();
+ }
+}
+
+/// Get options based on current platform.
+_Options _getPlatformOptions() {
+ if (Platform.isMacOS) {
+ return _platformOptions[_macOS];
+ } else if (Platform.isWindows) {
+ return _platformOptions[_windows];
+ } else if (Platform.isLinux) {
+ return _platformOptions[_linux];
+ } else {
+ throw Exception('Unknown Platform.');
+ }
+}
+
+/// Hold options which would be passed to clang.
+class _Options {
+ /// Tells compiler to generate a shared library.
+ final String sharedFlag;
+
+ /// Flag for generating Position Independant Code (Not used on windows).
+ final String fPIC;
+
+ /// Input file.
+ final String inputHeader;
+
+ /// Path to `.def` file containing symbols to export, windows use only.
+ final String moduleDefPath;
+
+ /// Path to header files.
+ List<String> headerIncludes;
+
+ /// Path to dynamic/static libraries
+ List<String> libIncludes;
+
+ /// Linker flag for linking to libclang.
+ final String ldLibFlag;
+
+ _Options({
+ @required this.sharedFlag,
+ @required this.inputHeader,
+ @required this.ldLibFlag,
+ this.headerIncludes = const [],
+ this.libIncludes = const [],
+ this.fPIC = '',
+ this.moduleDefPath = '',
+ });
+}
diff --git a/example/libclang-example/generated_bindings.dart b/example/libclang-example/generated_bindings.dart
index 924da2a..b58ac84 100644
--- a/example/libclang-example/generated_bindings.dart
+++ b/example/libclang-example/generated_bindings.dart
@@ -2531,7 +2531,7 @@
int Subminor;
}
-class CXGlobalOptFlags {
+abstract class CXGlobalOptFlags {
/// Used to indicate that no special CXIndex options are needed.
static const int CXGlobalOpt_None = 0;
@@ -2852,7 +2852,7 @@
class CXCursorSetImpl extends ffi.Struct {}
/// Describes the kind of type
-class CXTypeKind {
+abstract class CXTypeKind {
/// Represents an invalid type (e.g., where no type is available).
static const int CXType_Invalid = 0;
diff --git a/example/libclang-example/readme.md b/example/libclang-example/readme.md
index 33a01ef..0b8da39 100644
--- a/example/libclang-example/readme.md
+++ b/example/libclang-example/readme.md
@@ -1,7 +1,7 @@
# Libclang example
Demonstrates generating bindings for [Libclang](https://clang.llvm.org/doxygen/group__CINDEX.html).
-This example actually uses a C file used in this package itself, ([wrapper.c](../../tool/wrapped_libclang/wrapper.c)), which adds a few more wrapper functions atop Libclang.
+The C header source files for libclang are in [third_party/libclang](/third_party/libclang).
## Generating bindings
At the root of this example (`example/libclang-example`), run -
diff --git a/tool/wrapped_libclang/wrapper.c b/lib/src/clang_library/wrapper.c
similarity index 100%
rename from tool/wrapped_libclang/wrapper.c
rename to lib/src/clang_library/wrapper.c
diff --git a/tool/wrapped_libclang/wrapper.def b/lib/src/clang_library/wrapper.def
similarity index 100%
rename from tool/wrapped_libclang/wrapper.def
rename to lib/src/clang_library/wrapper.def
diff --git a/lib/src/code_generator/enum_class.dart b/lib/src/code_generator/enum_class.dart
index 8232ccb..5a96c39 100644
--- a/lib/src/code_generator/enum_class.dart
+++ b/lib/src/code_generator/enum_class.dart
@@ -49,7 +49,7 @@
final localUniqueNamer = UniqueNamer({enclosingClassName});
// Print enclosing class.
- s.write('class $enclosingClassName {\n');
+ s.write('abstract class $enclosingClassName {\n');
const depth = ' ';
for (final ec in enumConstants) {
final enum_value_name = localUniqueNamer.makeUnique(ec.name);
diff --git a/lib/src/config_provider/config.dart b/lib/src/config_provider/config.dart
index b0abcc0..054087b 100644
--- a/lib/src/config_provider/config.dart
+++ b/lib/src/config_provider/config.dart
@@ -2,8 +2,6 @@
// 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';
-
/// Validates the yaml input by the user, prints useful info for the user
import 'package:ffigen/src/code_generator.dart';
@@ -11,7 +9,6 @@
import 'package:logging/logging.dart';
import 'package:meta/meta.dart';
-import 'package:path/path.dart' as path;
import 'package:yaml/yaml.dart';
import '../strings.dart' as strings;
@@ -27,12 +24,6 @@
/// output file name.
String output;
- /// libclang path (in accordance with the platform).
- ///
- /// File may have the following extensions - `.so` / `.dll` / `.dylib`
- /// as extracted by configspec.
- String libclang_dylib_path;
-
/// Path to headers.
///
/// This contains all the headers, after extraction from Globs.
@@ -145,20 +136,6 @@
extractor: outputExtractor,
extractedResult: (dynamic result) => output = result as String,
),
- strings.libclang_dylib_folder: Specification<String>(
- description:
- 'Path to folder containing libclang dynamic library, used to parse C headers',
- requirement: Requirement.no,
- defaultValue: () => getDylibPath(Platform.script
- .resolve(path.posix.join('..', 'tool', 'wrapped_libclang'))
- // Path needs to be in posix style here or an illegal character
- // error is thrown on windows.
- .toFilePath()),
- validator: libclangDylibValidator,
- extractor: libclangDylibExtractor,
- extractedResult: (dynamic result) =>
- libclang_dylib_path = result as String,
- ),
strings.headers: Specification<List<String>>(
description: 'List of C headers to generate bindings of',
requirement: Requirement.yes,
diff --git a/lib/src/find_dot_dart_tool.dart b/lib/src/find_dot_dart_tool.dart
new file mode 100644
index 0000000..07c4a98
--- /dev/null
+++ b/lib/src/find_dot_dart_tool.dart
@@ -0,0 +1,8 @@
+// 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.
+
+library finddotdarttool;
+
+export 'find_dot_dart_tool/finddotdarttool_fallback.dart'
+ if (dart.library.cli) 'find_dot_dart_tool/finddotdarttool_cli.dart';
diff --git a/lib/src/find_dot_dart_tool/finddotdarttool_cli.dart b/lib/src/find_dot_dart_tool/finddotdarttool_cli.dart
new file mode 100644
index 0000000..0773410
--- /dev/null
+++ b/lib/src/find_dot_dart_tool/finddotdarttool_cli.dart
@@ -0,0 +1,29 @@
+// 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:cli' as cli;
+import 'dart:io' show File;
+import 'dart:isolate' show Isolate;
+import 'finddotdarttool_fallback.dart' as fallback;
+
+/// Find the `.dart_tool/` folder, returns `null` if unable to find it.
+Uri findDotDartTool() {
+ // Find [Isolate.packageConfig] and check if contains:
+ // * `package_config.json`, or,
+ // * `.dart_tool/package_config.json`.
+ // If either is the case we know the path to `.dart_tool/`.
+ final packageConfig = cli.waitFor(Isolate.packageConfig);
+ if (packageConfig != null &&
+ File.fromUri(packageConfig.resolve('package_config.json')).existsSync()) {
+ return packageConfig.resolve('./');
+ }
+ if (packageConfig != null &&
+ File.fromUri(packageConfig.resolve('.dart_tool/package_config.json'))
+ .existsSync()) {
+ return packageConfig.resolve('.dart_tool/');
+ }
+ // If [Isolate.packageConfig] isn't helpful we fallback to looking at the
+ // current script location and traverse up from there.
+ return fallback.findDotDartTool();
+}
diff --git a/lib/src/find_dot_dart_tool/finddotdarttool_fallback.dart b/lib/src/find_dot_dart_tool/finddotdarttool_fallback.dart
new file mode 100644
index 0000000..50e2c4c
--- /dev/null
+++ b/lib/src/find_dot_dart_tool/finddotdarttool_fallback.dart
@@ -0,0 +1,26 @@
+// 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' show Platform, File;
+
+/// Find the `.dart_tool/` folder, returns `null` if unable to find it.
+Uri findDotDartTool() {
+ // HACK: Because 'dart:isolate' is unavailable in Flutter we have no means
+ // by which we can find the location of the package_config.json file.
+ // Which we need, because the binary library created by:
+ // flutter pub run ffigen:setup
+ // is located relative to this path. As a workaround we use
+ // `Platform.script` and traverse level-up until we find a
+ // `.dart_tool/package_config.json` file.
+ // Find script directory
+ var root = Platform.script.resolve('./');
+ // Traverse up until we see a `.dart_tool/package_config.json` file.
+ do {
+ if (File.fromUri(root.resolve('.dart_tool/package_config.json'))
+ .existsSync()) {
+ return root.resolve('.dart_tool/');
+ }
+ } while (root != (root = root.resolve('..')));
+ return null;
+}
diff --git a/lib/src/header_parser/clang_bindings/clang_bindings.dart b/lib/src/header_parser/clang_bindings/clang_bindings.dart
index 7040fb5..975c9ad 100644
--- a/lib/src/header_parser/clang_bindings/clang_bindings.dart
+++ b/lib/src/header_parser/clang_bindings/clang_bindings.dart
@@ -704,7 +704,7 @@
}
/// Options to control the display of diagnostics.
-class CXDiagnosticDisplayOptions {
+abstract class CXDiagnosticDisplayOptions {
/// Display the source-location information where the diagnostic was located.
static const int CXDiagnostic_DisplaySourceLocation = 1;
@@ -727,7 +727,7 @@
}
/// Flags that control the creation of translation units.
-class CXTranslationUnit_Flags {
+abstract class CXTranslationUnit_Flags {
/// Used to indicate that no special translation-unit options are needed.
static const int CXTranslationUnit_None = 0;
@@ -791,7 +791,7 @@
}
/// Describes the kind of entity that a cursor refers to.
-class CXCursorKind {
+abstract class CXCursorKind {
/// A declaration whose specific kind is not exposed via this interface.
static const int CXCursor_UnexposedDecl = 1;
@@ -1509,7 +1509,7 @@
}
/// Describes the kind of type
-class CXTypeKind {
+abstract class CXTypeKind {
/// Represents an invalid type (e.g., where no type is available).
static const int CXType_Invalid = 0;
@@ -1693,7 +1693,7 @@
/// Describes how the traversal of the children of a particular cursor should
/// proceed after visiting a particular child cursor.
-class CXChildVisitResult {
+abstract class CXChildVisitResult {
/// Terminates the cursor traversal.
static const int CXChildVisit_Break = 0;
diff --git a/lib/src/header_parser/parser.dart b/lib/src/header_parser/parser.dart
index bc3c7f8..a2b1593 100644
--- a/lib/src/header_parser/parser.dart
+++ b/lib/src/header_parser/parser.dart
@@ -3,13 +3,15 @@
// BSD-style license that can be found in the LICENSE file.
import 'dart:ffi';
-import 'dart:io';
import 'package:ffi/ffi.dart';
import 'package:ffigen/src/code_generator.dart';
import 'package:ffigen/src/config_provider.dart';
+import 'package:ffigen/src/find_dot_dart_tool.dart';
import 'package:ffigen/src/header_parser/translation_unit_parser.dart';
+import 'package:ffigen/src/strings.dart' as strings;
import 'package:logging/logging.dart';
+import 'package:path/path.dart' as path;
import 'clang_bindings/clang_bindings.dart' as clang_types;
import 'data.dart';
@@ -42,11 +44,20 @@
/// Initialises parser, clears any previous values.
void initParser(Config c) {
+ // Set global configurations.
config = c;
- clang = clang_types.Clang(DynamicLibrary.open(
- File(config.libclang_dylib_path).absolute?.path ??
- config.libclang_dylib_path));
+ // Find full path of dynamic library and initialise bindings.
+ if (findDotDartTool() == null) {
+ throw Exception('Unable to find .dart_tool.');
+ } else {
+ final fullDylibPath = path.join(
+ findDotDartTool().toFilePath(),
+ strings.ffigenFolderName,
+ strings.dylibFileName,
+ );
+ clang = clang_types.Clang(DynamicLibrary.open(fullDylibPath));
+ }
}
/// Parses source files and adds generated bindings to [bindings].
diff --git a/lib/src/strings.dart b/lib/src/strings.dart
index 5951ef5..6bc47cd 100644
--- a/lib/src/strings.dart
+++ b/lib/src/strings.dart
@@ -1,11 +1,32 @@
// 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:ffigen/src/header_parser/clang_bindings/clang_bindings.dart'
as clang;
+// This version must be updated whenever we update the libclang wrapper.
+const dylibVersion = 'v1';
+
+/// Name of the dynamic library file according to current platform.
+String get dylibFileName {
+ String name;
+ if (Platform.isLinux) {
+ name = libclang_dylib_linux;
+ } else if (Platform.isMacOS) {
+ name = libclang_dylib_macos;
+ } else if (Platform.isWindows) {
+ name = libclang_dylib_windows;
+ } else {
+ throw Exception('Unsupported Platform.');
+ }
+ return '_${dylibVersion}_$name';
+}
+
+const ffigenFolderName = 'ffigen';
+
const output = 'output';
-const libclang_dylib_folder = 'libclang-dylib-folder';
const headers = 'headers';
const headerFilter = 'header-filter';
const compilerOpts = 'compiler-opts';
diff --git a/pubspec.yaml b/pubspec.yaml
index 64ecfbd..c412a92 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -3,7 +3,7 @@
# BSD-style license that can be found in the LICENSE file.
name: ffigen
-version: 0.0.1
+version: 0.1.0
homepage: https://github.com/dart-lang/ffigen
description: Experimental generator for FFI bindings.
diff --git a/test/code_generator_test.dart b/test/code_generator_test.dart
index 4fd5aea..a67f41e 100644
--- a/test/code_generator_test.dart
+++ b/test/code_generator_test.dart
@@ -545,7 +545,7 @@
/// test line 1
/// test line 2
-class Constants {
+abstract class Constants {
static const int a = 10;
/// negative
static const int b = -1;
@@ -715,10 +715,10 @@
class ArrayHelperPrefixCollisionTest extends ffi.Struct{
}
-class _c_Test {
+abstract class _c_Test {
}
-class init_dylib {
+abstract class init_dylib {
}
typedef _c_test1 = ffi.Void Function(
diff --git a/test/header_parser_tests/function_n_struct_test.dart b/test/header_parser_tests/function_n_struct_test.dart
index f3c7fe1..872bf1b 100644
--- a/test/header_parser_tests/function_n_struct_test.dart
+++ b/test/header_parser_tests/function_n_struct_test.dart
@@ -24,7 +24,7 @@
${strings.name}: 'NativeLibrary'
${strings.description}: 'Function And Struct Test'
${strings.output}: 'unused'
-${strings.libclang_dylib_folder}: 'tool/wrapped_libclang'
+
${strings.headers}:
- 'test/header_parser_tests/function_n_struct.h'
''') as yaml.YamlMap),
diff --git a/test/header_parser_tests/functions_test.dart b/test/header_parser_tests/functions_test.dart
index 67a147a..c88fd55 100644
--- a/test/header_parser_tests/functions_test.dart
+++ b/test/header_parser_tests/functions_test.dart
@@ -23,7 +23,7 @@
${strings.name}: 'NativeLibrary'
${strings.description}: 'Functions Test'
${strings.output}: 'unused'
-${strings.libclang_dylib_folder}: 'tool/wrapped_libclang'
+
${strings.headers}:
- 'test/header_parser_tests/functions.h'
${strings.headerFilter}:
diff --git a/test/header_parser_tests/nested_parsing_test.dart b/test/header_parser_tests/nested_parsing_test.dart
index 8783f46..0736231 100644
--- a/test/header_parser_tests/nested_parsing_test.dart
+++ b/test/header_parser_tests/nested_parsing_test.dart
@@ -23,7 +23,7 @@
${strings.name}: 'NativeLibrary'
${strings.description}: 'Nested Parsing Test'
${strings.output}: 'unused'
-${strings.libclang_dylib_folder}: 'tool/wrapped_libclang'
+
${strings.headers}:
- 'test/header_parser_tests/nested_parsing.h'
structs:
diff --git a/test/large_integration_tests/_expected_libclang_bindings.dart b/test/large_integration_tests/_expected_libclang_bindings.dart
index 1aa4cb5..7cb48a1 100644
--- a/test/large_integration_tests/_expected_libclang_bindings.dart
+++ b/test/large_integration_tests/_expected_libclang_bindings.dart
@@ -2149,7 +2149,7 @@
}
/// Error codes returned by libclang routines.
-class CXErrorCode {
+abstract class CXErrorCode {
/// No error.
static const int CXError_Success = 0;
@@ -2205,7 +2205,7 @@
/// Describes the availability of a particular entity, which indicates whether
/// the use of this entity will result in a warning or error due to it being
/// deprecated or unavailable.
-class CXAvailabilityKind {
+abstract class CXAvailabilityKind {
/// The entity is available.
static const int CXAvailability_Available = 0;
@@ -2241,7 +2241,7 @@
}
/// Describes the exception specification of a cursor.
-class CXCursor_ExceptionSpecificationKind {
+abstract class CXCursor_ExceptionSpecificationKind {
/// The cursor has no exception specification.
static const int CXCursor_ExceptionSpecificationKind_None = 0;
@@ -2273,7 +2273,7 @@
static const int CXCursor_ExceptionSpecificationKind_NoThrow = 9;
}
-class CXGlobalOptFlags {
+abstract class CXGlobalOptFlags {
/// Used to indicate that no special CXIndex options are needed.
static const int CXGlobalOpt_None = 0;
@@ -2477,7 +2477,7 @@
}
/// Describes the severity of a particular diagnostic.
-class CXDiagnosticSeverity {
+abstract class CXDiagnosticSeverity {
/// A diagnostic that has been suppressed, e.g., by a command-line option.
static const int CXDiagnostic_Ignored = 0;
@@ -2498,7 +2498,7 @@
/// Describes the kind of error that occurred (if any) in a call to
/// clang_loadDiagnostics.
-class CXLoadDiag_Error {
+abstract class CXLoadDiag_Error {
/// Indicates that no error occurred.
static const int CXLoadDiag_None = 0;
@@ -2515,7 +2515,7 @@
}
/// Options to control the display of diagnostics.
-class CXDiagnosticDisplayOptions {
+abstract class CXDiagnosticDisplayOptions {
/// Display the source-location information where the diagnostic was located.
static const int CXDiagnostic_DisplaySourceLocation = 1;
@@ -2538,7 +2538,7 @@
}
/// Flags that control the creation of translation units.
-class CXTranslationUnit_Flags {
+abstract class CXTranslationUnit_Flags {
/// Used to indicate that no special translation-unit options are needed.
static const int CXTranslationUnit_None = 0;
@@ -2602,14 +2602,14 @@
}
/// Flags that control how translation units are saved.
-class CXSaveTranslationUnit_Flags {
+abstract class CXSaveTranslationUnit_Flags {
/// Used to indicate that no special saving options are needed.
static const int CXSaveTranslationUnit_None = 0;
}
/// Describes the kind of error that occurred (if any) in a call to
/// clang_saveTranslationUnit().
-class CXSaveError {
+abstract class CXSaveError {
/// Indicates that no error occurred while saving a translation unit.
static const int CXSaveError_None = 0;
@@ -2627,13 +2627,13 @@
}
/// Flags that control the reparsing of translation units.
-class CXReparse_Flags {
+abstract class CXReparse_Flags {
/// Used to indicate that no special reparsing options are needed.
static const int CXReparse_None = 0;
}
/// Categorizes how memory is being used by a translation unit.
-class CXTUResourceUsageKind {
+abstract class CXTUResourceUsageKind {
static const int CXTUResourceUsage_AST = 1;
static const int CXTUResourceUsage_Identifiers = 2;
static const int CXTUResourceUsage_Selectors = 3;
@@ -2673,7 +2673,7 @@
}
/// Describes the kind of entity that a cursor refers to.
-class CXCursorKind {
+abstract class CXCursorKind {
/// A declaration whose specific kind is not exposed via this interface.
static const int CXCursor_UnexposedDecl = 1;
@@ -3391,7 +3391,7 @@
}
/// Describe the linkage of the entity referred to by a cursor.
-class CXLinkageKind {
+abstract class CXLinkageKind {
/// This value indicates that no linkage information is available for a
/// provided CXCursor.
static const int CXLinkage_Invalid = 0;
@@ -3411,7 +3411,7 @@
static const int CXLinkage_External = 4;
}
-class CXVisibilityKind {
+abstract class CXVisibilityKind {
/// This value indicates that no visibility information is available for a
/// provided CXCursor.
static const int CXVisibility_Invalid = 0;
@@ -3431,7 +3431,7 @@
class CXPlatformAvailability extends ffi.Struct {}
/// Describe the "language" of the entity referred to by a cursor.
-class CXLanguageKind {
+abstract class CXLanguageKind {
static const int CXLanguage_Invalid = 0;
static const int CXLanguage_C = 1;
static const int CXLanguage_ObjC = 2;
@@ -3440,7 +3440,7 @@
/// Describe the "thread-local storage (TLS) kind" of the declaration referred
/// to by a cursor.
-class CXTLSKind {
+abstract class CXTLSKind {
static const int CXTLS_None = 0;
static const int CXTLS_Dynamic = 1;
static const int CXTLS_Static = 2;
@@ -3449,7 +3449,7 @@
class CXCursorSetImpl extends ffi.Struct {}
/// Describes the kind of type
-class CXTypeKind {
+abstract class CXTypeKind {
/// Represents an invalid type (e.g., where no type is available).
static const int CXType_Invalid = 0;
@@ -3576,7 +3576,7 @@
}
/// Describes the calling convention of a function type
-class CXCallingConv {
+abstract class CXCallingConv {
static const int CXCallingConv_Default = 0;
static const int CXCallingConv_C = 1;
static const int CXCallingConv_X86StdCall = 2;
@@ -3656,7 +3656,7 @@
}
/// Describes the kind of a template argument.
-class CXTemplateArgumentKind {
+abstract class CXTemplateArgumentKind {
static const int CXTemplateArgumentKind_Null = 0;
static const int CXTemplateArgumentKind_Type = 1;
static const int CXTemplateArgumentKind_Declaration = 2;
@@ -3669,7 +3669,7 @@
static const int CXTemplateArgumentKind_Invalid = 9;
}
-class CXTypeNullabilityKind {
+abstract class CXTypeNullabilityKind {
/// Values of this type can never be null.
static const int CXTypeNullability_NonNull = 0;
@@ -3687,7 +3687,7 @@
/// List the possible error codes for clang_Type_getSizeOf,
/// clang_Type_getAlignOf, clang_Type_getOffsetOf and clang_Cursor_getOffsetOf.
-class CXTypeLayoutError {
+abstract class CXTypeLayoutError {
/// Type is of kind CXType_Invalid.
static const int CXTypeLayoutError_Invalid = -1;
@@ -3707,7 +3707,7 @@
static const int CXTypeLayoutError_Undeduced = -6;
}
-class CXRefQualifierKind {
+abstract class CXRefQualifierKind {
/// No ref-qualifier was provided.
static const int CXRefQualifier_None = 0;
@@ -3720,7 +3720,7 @@
/// Represents the C++ access control level to a base class for a cursor with
/// kind CX_CXXBaseSpecifier.
-class CX_CXXAccessSpecifier {
+abstract class CX_CXXAccessSpecifier {
static const int CX_CXXInvalidAccessSpecifier = 0;
static const int CX_CXXPublic = 1;
static const int CX_CXXProtected = 2;
@@ -3729,7 +3729,7 @@
/// Represents the storage classes as declared in the source. CX_SC_Invalid was
/// added for the case that the passed cursor in not a declaration.
-class CX_StorageClass {
+abstract class CX_StorageClass {
static const int CX_SC_Invalid = 0;
static const int CX_SC_None = 1;
static const int CX_SC_Extern = 2;
@@ -3742,7 +3742,7 @@
/// Describes how the traversal of the children of a particular cursor should
/// proceed after visiting a particular child cursor.
-class CXChildVisitResult {
+abstract class CXChildVisitResult {
/// Terminates the cursor traversal.
static const int CXChildVisit_Break = 0;
@@ -3756,7 +3756,7 @@
}
/// Properties for the printing policy.
-class CXPrintingPolicyProperty {
+abstract class CXPrintingPolicyProperty {
static const int CXPrintingPolicy_Indentation = 0;
static const int CXPrintingPolicy_SuppressSpecifiers = 1;
static const int CXPrintingPolicy_SuppressTagKeyword = 2;
@@ -3787,7 +3787,7 @@
}
/// Property attributes for a CXCursor_ObjCPropertyDecl.
-class CXObjCPropertyAttrKind {
+abstract class CXObjCPropertyAttrKind {
static const int CXObjCPropertyAttr_noattr = 0;
static const int CXObjCPropertyAttr_readonly = 1;
static const int CXObjCPropertyAttr_getter = 2;
@@ -3806,7 +3806,7 @@
/// 'Qualifiers' written next to the return and parameter types in Objective-C
/// method declarations.
-class CXObjCDeclQualifierKind {
+abstract class CXObjCDeclQualifierKind {
static const int CXObjCDeclQualifier_None = 0;
static const int CXObjCDeclQualifier_In = 1;
static const int CXObjCDeclQualifier_Inout = 2;
@@ -3816,7 +3816,7 @@
static const int CXObjCDeclQualifier_Oneway = 32;
}
-class CXNameRefFlags {
+abstract class CXNameRefFlags {
/// Include the nested-name-specifier, e.g. Foo:: in x.Foo::y, in the range.
static const int CXNameRange_WantQualifier = 1;
@@ -3829,7 +3829,7 @@
}
/// Describes a kind of token.
-class CXTokenKind {
+abstract class CXTokenKind {
/// A token that contains some kind of punctuation.
static const int CXToken_Punctuation = 0;
@@ -3928,7 +3928,7 @@
}
/// Describes a single piece of text within a code-completion string.
-class CXCompletionChunkKind {
+abstract class CXCompletionChunkKind {
/// A code-completion string that describes "optional" text that could be a
/// part of the template (but is not required).
static const int CXCompletionChunk_Optional = 0;
@@ -4011,7 +4011,7 @@
}
/// Flags that can be passed to clang_codeCompleteAt() to modify its behavior.
-class CXCodeComplete_Flags {
+abstract class CXCodeComplete_Flags {
/// Whether to include macros within the set of code completions returned.
static const int CXCodeComplete_IncludeMacros = 1;
@@ -4035,7 +4035,7 @@
}
/// Bits that represent the context under which completion is occurring.
-class CXCompletionContext {
+abstract class CXCompletionContext {
/// The context for completions is unexposed, as only Clang results should be
/// included. (This is equivalent to having no context bits set.)
static const int CXCompletionContext_Unexposed = 0;
@@ -4127,7 +4127,7 @@
static const int CXCompletionContext_Unknown = 8388607;
}
-class CXEvalResultKind {
+abstract class CXEvalResultKind {
static const int CXEval_Int = 1;
static const int CXEval_Float = 2;
static const int CXEval_ObjCStrLiteral = 3;
@@ -4138,14 +4138,14 @@
}
/// @{
-class CXVisitorResult {
+abstract class CXVisitorResult {
static const int CXVisit_Break = 0;
static const int CXVisit_Continue = 1;
}
class CXCursorAndRangeVisitor extends ffi.Struct {}
-class CXResult {
+abstract class CXResult {
/// Function returned successfully.
static const int CXResult_Success = 0;
@@ -4217,7 +4217,7 @@
/// Data for IndexerCallbacks#importedASTFile.
class CXIdxImportedASTFileInfo extends ffi.Struct {}
-class CXIdxEntityKind {
+abstract class CXIdxEntityKind {
static const int CXIdxEntity_Unexposed = 0;
static const int CXIdxEntity_Typedef = 1;
static const int CXIdxEntity_Function = 2;
@@ -4247,7 +4247,7 @@
static const int CXIdxEntity_CXXInterface = 26;
}
-class CXIdxEntityLanguage {
+abstract class CXIdxEntityLanguage {
static const int CXIdxEntityLang_None = 0;
static const int CXIdxEntityLang_C = 1;
static const int CXIdxEntityLang_ObjC = 2;
@@ -4259,14 +4259,14 @@
/// CXIdxEntity_Function CXIdxEntity_CXXClass CXIdxEntity_CXXStaticMethod
/// CXIdxEntity_CXXInstanceMethod CXIdxEntity_CXXConstructor
/// CXIdxEntity_CXXConversionFunction CXIdxEntity_CXXTypeAlias
-class CXIdxEntityCXXTemplateKind {
+abstract class CXIdxEntityCXXTemplateKind {
static const int CXIdxEntity_NonTemplate = 0;
static const int CXIdxEntity_Template = 1;
static const int CXIdxEntity_TemplatePartialSpecialization = 2;
static const int CXIdxEntity_TemplateSpecialization = 3;
}
-class CXIdxAttrKind {
+abstract class CXIdxAttrKind {
static const int CXIdxAttr_Unexposed = 0;
static const int CXIdxAttr_IBAction = 1;
static const int CXIdxAttr_IBOutlet = 2;
@@ -4281,13 +4281,13 @@
class CXIdxIBOutletCollectionAttrInfo extends ffi.Struct {}
-class CXIdxDeclInfoFlags {
+abstract class CXIdxDeclInfoFlags {
static const int CXIdxDeclFlag_Skipped = 1;
}
class CXIdxDeclInfo extends ffi.Struct {}
-class CXIdxObjCContainerKind {
+abstract class CXIdxObjCContainerKind {
static const int CXIdxObjCContainer_ForwardRef = 0;
static const int CXIdxObjCContainer_Interface = 1;
static const int CXIdxObjCContainer_Implementation = 2;
@@ -4339,7 +4339,7 @@
}
/// Data for IndexerCallbacks#indexEntityReference.
-class CXIdxEntityRefKind {
+abstract class CXIdxEntityRefKind {
/// The entity is referenced directly in user's code.
static const int CXIdxEntityRef_Direct = 1;
@@ -4349,7 +4349,7 @@
}
/// Roles that are attributed to symbol occurrences.
-class CXSymbolRole {
+abstract class CXSymbolRole {
static const int CXSymbolRole_None = 0;
static const int CXSymbolRole_Declaration = 1;
static const int CXSymbolRole_Definition = 2;
@@ -4392,7 +4392,7 @@
ffi.Pointer<ffi.NativeFunction<_typedefC_10>> indexEntityReference;
}
-class CXIndexOptFlags {
+abstract class CXIndexOptFlags {
/// Used to indicate that no special indexing options are needed.
static const int CXIndexOpt_None = 0;
diff --git a/test/large_integration_tests/large_test.dart b/test/large_integration_tests/large_test.dart
index a78d903..79d4a88 100644
--- a/test/large_integration_tests/large_test.dart
+++ b/test/large_integration_tests/large_test.dart
@@ -24,7 +24,6 @@
${strings.name}: LibClang
${strings.description}: Bindings to LibClang.
${strings.output}: unused
-${strings.libclang_dylib_folder}: tool/wrapped_libclang
${strings.compilerOpts}: -I${path.join('third_party', 'libclang', 'include')}
${strings.arrayWorkaround}: true
${strings.headers}:
@@ -66,7 +65,6 @@
${strings.description}: Bindings to Cjson.
${strings.output}: unused
${strings.comments}: full
-${strings.libclang_dylib_folder}: tool/wrapped_libclang
${strings.arrayWorkaround}: true
${strings.headers}:
- third_party/cjson_library/cJSON.h
@@ -102,7 +100,6 @@
${strings.name}: SQLite
${strings.description}: Bindings to SQLite.
${strings.output}: unused
-${strings.libclang_dylib_folder}: tool/wrapped_libclang
${strings.arrayWorkaround}: true
${strings.comments}: full
${strings.headers}:
diff --git a/test/native_test/.gitignore b/test/native_test/.gitignore
new file mode 100644
index 0000000..d97e2fc
--- /dev/null
+++ b/test/native_test/.gitignore
@@ -0,0 +1,3 @@
+# Ignore files generated by clang on windows.
+native_test.exp
+native_test.lib
diff --git a/test/native_test/config.yaml b/test/native_test/config.yaml
index a25821a..479988d 100644
--- a/test/native_test/config.yaml
+++ b/test/native_test/config.yaml
@@ -7,7 +7,6 @@
# ===============================================================
output: 'native_test_bindings.dart'
-libclang-dylib-folder: '../../tool/wrapped_libclang'
sort: true
headers:
- 'native_test.c'
diff --git a/test/prefix_tests/prefix_test.dart b/test/prefix_tests/prefix_test.dart
index dea22fc..abbe2b4 100644
--- a/test/prefix_tests/prefix_test.dart
+++ b/test/prefix_tests/prefix_test.dart
@@ -29,7 +29,7 @@
${strings.name}: 'NativeLibrary'
${strings.description}: 'Prefix Test'
${strings.output}: 'unused'
-${strings.libclang_dylib_folder}: 'tool/wrapped_libclang'
+
${strings.headers}:
- 'test/prefix_tests/prefix.h'
${strings.headerFilter}:
diff --git a/tool/libclang_config.yaml b/tool/libclang_config.yaml
index fe10d98..aec3071 100644
--- a/tool/libclang_config.yaml
+++ b/tool/libclang_config.yaml
@@ -5,18 +5,17 @@
# Config file for generating the libclang bindings used by this package.
# ===================== GENERATING BINDINGS =====================
-# cd to `tool`, and run -
-# dart ../bin/ffigen.dart --config libclang_config.yaml
+# cd to project's root, and run -
+# pub run ffigen --config libclang_config.yaml
# ===============================================================
name: Clang
description: Holds bindings to LibClang.
-output: '../lib/src/header_parser/clang_bindings/clang_bindings.dart'
-libclang-dylib-folder: 'wrapped_libclang'
+output: 'lib/src/header_parser/clang_bindings/clang_bindings.dart'
sort: true
compiler-opts: '-I/usr/lib/llvm-9/include/ -I/usr/lib/llvm-10/include/ -I/usr/local/opt/llvm/include/ -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/ -Wno-nullability-completeness'
headers:
- - 'wrapped_libclang/wrapper.c'
+ - 'lib/src/clang_library/wrapper.c'
header-filter:
include:
- wrapper.c
diff --git a/tool/wrapped_libclang/.gitignore b/tool/wrapped_libclang/.gitignore
deleted file mode 100644
index 2b64ac7..0000000
--- a/tool/wrapped_libclang/.gitignore
+++ /dev/null
@@ -1,12 +0,0 @@
-# general gitignore for CMake.
-CMakeLists.txt.user
-CMakeCache.txt
-CMakeFiles
-CMakeScripts
-Testing
-Makefile
-cmake_install.cmake
-install_manifest.txt
-compile_commands.json
-CTestTestfile.cmake
-_deps
diff --git a/tool/wrapped_libclang/build.dart b/tool/wrapped_libclang/build.dart
deleted file mode 100644
index 3e1eff4..0000000
--- a/tool/wrapped_libclang/build.dart
+++ /dev/null
@@ -1,217 +0,0 @@
-// 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.
-
-/// =======================================================================
-/// =============== Build script to generate dyamic library ===============
-/// =======================================================================
-/// This Script effectively calls the following (but user can provide
-/// command line args which will replace the defaults shown below)-
-///
-/// Linux:
-/// ```
-/// clang -I/usr/lib/llvm-9/include/ -I/usr/lib/llvm-10/include/ -lclang -shared -fpic wrapper.c -o libwrapped_clang.so
-/// ```
-/// MacOS:
-/// ```
-/// clang -I/usr/local/opt/llvm/include/ -L/usr/local/opt/llvm/lib/ -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/ -v -lclang -shared -fpic wrapper.c -o libwrapped_clang.dylib
-/// ```
-/// Windows:
-/// ```
-/// clang -IC:\Progra~1\LLVM\include -LC:\Progra~1\LLVM\lib -llibclang -shared wrapper.c -o wrapped_clang.dll -Wl,/DEF:wrapper.def
-/// del wrapped_clang.exp
-/// del wrapped_clang.lib
-/// ```
-/// =======================================================================
-/// =======================================================================
-/// =======================================================================
-
-import 'dart:io';
-import 'package:args/args.dart';
-import 'package:meta/meta.dart';
-
-const macOS = 'macos';
-const windows = 'windows';
-const linux = 'linux';
-
-/// Default platform options.
-Map<String, Options> platformOptions = {
- linux: Options(
- outputfilename: 'libwrapped_clang.so',
- sharedFlag: '-shared',
- inputHeader: 'wrapper.c',
- fPIC: '-fpic',
- ldLibFlag: '-lclang',
- headerIncludes: [
- '-I/usr/lib/llvm-9/include/',
- '-I/usr/lib/llvm-10/include/',
- ],
- ),
- windows: Options(
- outputfilename: 'wrapped_clang.dll',
- sharedFlag: '-shared',
- inputHeader: 'wrapper.c',
- moduleDefPath: '-Wl,/DEF:wrapper.def',
- ldLibFlag: '-llibclang',
- headerIncludes: [
- r'-IC:\Progra~1\LLVM\include',
- ],
- libIncludes: [
- r'-LC:\Progra~1\LLVM\lib',
- ],
- ),
- macOS: Options(
- outputfilename: 'libwrapped_clang.dylib',
- sharedFlag: '-shared',
- inputHeader: 'wrapper.c',
- fPIC: '-fpic',
- ldLibFlag: '-lclang',
- headerIncludes: [
- '-I/usr/local/opt/llvm/include/',
- '-I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/',
- ],
- libIncludes: [
- '-L/usr/local/opt/llvm/lib/',
- ],
- ),
-};
-
-void main(List<String> arguments) {
- print('Building Dynamic Library for libclang wrapper... ');
- final options = getPlatformOptions();
-
- // Updates header/lib includes in platform options.
- changeIncludesUsingCmdArgs(arguments, options);
-
- // Run clang compiler to generate the dynamic library.
- final ProcessResult result = runClangProcess(options);
- printSuccess(result, options);
-}
-
-/// Calls the clang compiler.
-ProcessResult runClangProcess(Options options) {
- final result = Process.runSync(
- 'clang',
- [
- ...options.headerIncludes,
- ...options.libIncludes,
- options.ldLibFlag,
- options.sharedFlag,
- options.fPIC,
- options.inputHeader,
- '-o',
- options.outputfilename,
- options.moduleDefPath,
- '-Wno-nullability-completeness',
- ],
- );
- return result;
-}
-
-/// Prints success message (or process error if any).
-void printSuccess(ProcessResult result, Options options) {
- print(result.stdout);
- if ((result.stderr as String).isEmpty) {
- print('Generated file: ${options.outputfilename}');
- } else {
- print(result.stderr);
- }
-}
-
-ArgResults getArgResults(List<String> args) {
- final parser = ArgParser(allowTrailingOptions: true);
- parser.addSeparator(
- 'Build Script to generate dynamic library used by this package:');
- parser.addMultiOption('include-header',
- abbr: 'I', help: 'Path to header include directories');
- parser.addMultiOption('include-lib',
- abbr: 'L', help: 'Path to library include directories');
- parser.addFlag(
- 'help',
- abbr: 'h',
- help: 'prints this usage',
- negatable: false,
- );
-
- ArgResults results;
- try {
- results = parser.parse(args);
-
- if (results.wasParsed('help')) {
- print(parser.usage);
- exit(0);
- }
- } catch (e) {
- print(e);
- print(parser.usage);
- exit(1);
- }
-
- return results;
-}
-
-/// Use cmd args(if any) to change header/lib include paths.
-void changeIncludesUsingCmdArgs(List<String> arguments, Options options) {
- final argResult = getArgResults(arguments);
- if (argResult.wasParsed('include-header')) {
- options.headerIncludes = (argResult['include-header'] as List<String>)
- .map((header) => '-I$header')
- .toList();
- }
- if (argResult.wasParsed('include-lib')) {
- options.libIncludes = (argResult['include-lib'] as List<String>)
- .map((lib) => '-L$lib')
- .toList();
- }
-}
-
-/// Get options based on current platform.
-Options getPlatformOptions() {
- if (Platform.isMacOS) {
- return platformOptions[macOS];
- } else if (Platform.isWindows) {
- return platformOptions[windows];
- } else if (Platform.isLinux) {
- return platformOptions[linux];
- } else {
- throw Exception('Unknown Platform.');
- }
-}
-
-/// Hold options which would be passed to clang.
-class Options {
- /// Name of dynamic library to generate.
- final String outputfilename;
-
- /// Tells compiler to generate a shared library.
- final String sharedFlag;
-
- /// Flag for generating Position Independant Code (Not used on windows).
- final String fPIC;
-
- /// Input file.
- final String inputHeader;
-
- /// Path to `.def` file containing symbols to export, windows use only.
- final String moduleDefPath;
-
- /// Path to header files.
- List<String> headerIncludes;
-
- /// Path to dynamic/static libraries
- List<String> libIncludes;
-
- /// Linker flag for linking to libclang.
- final String ldLibFlag;
-
- Options({
- @required this.outputfilename,
- @required this.sharedFlag,
- @required this.inputHeader,
- @required this.ldLibFlag,
- this.headerIncludes = const [],
- this.libIncludes = const [],
- this.fPIC = '',
- this.moduleDefPath = '',
- });
-}