blob: 3e1eff485976178b722e51bab43f8cf65de675b9 [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.
/// =======================================================================
/// =============== 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 = '',
});
}