Removed usage of --no-sound-null-safety flag. (#136)

* Removed usage of --no-sound-null-safety flag
* Update version, changelog
* Use flag when running tests on travis
diff --git a/.github/workflows/test-package.yml b/.github/workflows/test-package.yml
index 1026834..83fe4d6 100644
--- a/.github/workflows/test-package.yml
+++ b/.github/workflows/test-package.yml
@@ -50,7 +50,7 @@
       - name: Install libclang-10-dev
         run: sudo apt-get install libclang-10-dev
       - name: Setup ffigen
-        run: dart --no-sound-null-safety run ffigen:setup
+        run: dart run ffigen:setup
       - name: Build test dylib
         run: cd test/native_test && dart build_test_dylib.dart && cd ../..
       - name: Run VM tests
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2697d97..38fa2af 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,6 @@
+# 2.0.0-nullsafety.1
+- Removed the need for `--no-sound-null-safety` flag.
+
 # 2.0.0-nullsafety.0
 - Migrated to (unsound) null safety.
 
diff --git a/README.md b/README.md
index 3b56033..e6359eb 100644
--- a/README.md
+++ b/README.md
@@ -5,9 +5,6 @@
 Experimental binding generator for [FFI](https://dart.dev/guides/libraries/c-interop)
 bindings.
 
-<!-- TODO: Remove this when package can run with sound null safety -->
-> Due to a few unmigrated dependencies, ffigen currently runs in unsound null safety, run using `dart --no-sound-null-safety run ffigen`.
-
 ## Example
 
 For some header file _example.h_:
diff --git a/bin/ffigen.dart b/bin/ffigen.dart
index 82abac5..cfb482c 100644
--- a/bin/ffigen.dart
+++ b/bin/ffigen.dart
@@ -1,211 +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.
+//
+// TODO(128): Remove this when package can run with sound null safety.
+// @dart=2.7
 
-// Executable script to generate bindings for some C library.
-import 'dart:io';
-
-import 'package:args/args.dart';
-import 'package:cli_util/cli_logging.dart' show Ansi;
-import 'package:ffigen/ffigen.dart';
-import 'package:logging/logging.dart';
-import 'package:yaml/yaml.dart' as yaml;
-
-import 'setup.dart';
-
-final _logger = Logger('ffigen.ffigen');
-final _ansi = Ansi(Ansi.terminalSupportsAnsi);
-
-String successPen(String str) {
-  return '${_ansi.green}$str${_ansi.none}';
-}
-
-String errorPen(String str) {
-  return '${_ansi.red}$str${_ansi.none}';
-}
-
-void main(List<String> args) {
-  // Parses the cmd args. This will print usage and exit if --help was passed.
-  final argResult = getArgResults(args);
-
-  // Setup logging level and printing.
-  setupLogger(argResult);
-
-  /// 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()) {
-    _logger.severe('Unable to create dynamic library automatically.');
-    _logger.severe('If LLVM (9+) is installed, try running:');
-    _logger.severe(
-        '  pub run ffigen:setup -Ipath/to/llvm/include -Lpath/to/llvm/lib');
-    exit(1);
-  }
-
-  // Create a config object.
-  Config config;
-  try {
-    config = getConfig(argResult);
-  } on FormatException {
-    _logger.severe('Please fix configuration errors and re-run the tool.');
-    exit(1);
-  }
-
-  // Parse the bindings according to config object provided.
-  final library = parse(config);
-
-  // Generate file for the parsed bindings.
-  final gen = File(config.output);
-  library.generateFile(gen);
-  _logger
-      .info(successPen('Finished, Bindings generated in ${gen.absolute.path}'));
-}
-
-Config getConfig(ArgResults result) {
-  _logger.info('Running in ${Directory.current}');
-
-  if (result.wasParsed('config')) {
-    return getConfigFromCustomYaml(result['config'] as String);
-  } else {
-    return getConfigFromPubspec();
-  }
-}
-
-/// Extracts configuration from pubspec file.
-Config getConfigFromPubspec() {
-  final pubspecName = 'pubspec.yaml';
-  final configKey = 'ffigen';
-
-  final pubspecFile = File(pubspecName);
-
-  if (!pubspecFile.existsSync()) {
-    _logger.severe(
-        'Error: $pubspecName not found, please run this tool from the root of your package.');
-    exit(1);
-  }
-
-  // Casting this because pubspec is expected to be a YamlMap.
-
-  // Throws a [YamlException] if it's unable to parse the Yaml.
-  final bindingsConfigMap =
-      yaml.loadYaml(pubspecFile.readAsStringSync())[configKey] as yaml.YamlMap?;
-
-  if (bindingsConfigMap == null) {
-    _logger.severe("Couldn't find an entry for '$configKey' in $pubspecName.");
-    exit(1);
-  }
-  return Config.fromYaml(bindingsConfigMap);
-}
-
-/// Extracts configuration from a custom yaml file.
-Config getConfigFromCustomYaml(String yamlPath) {
-  final yamlFile = File(yamlPath);
-
-  if (!yamlFile.existsSync()) {
-    _logger.severe('Error: $yamlPath not found.');
-    exit(1);
-  }
-
-  // Throws a [YamlException] if it's unable to parse the Yaml.
-  final bindingsConfigMap =
-      yaml.loadYaml(yamlFile.readAsStringSync()) as yaml.YamlMap;
-
-  return Config.fromYaml(bindingsConfigMap);
-}
-
-/// Parses the cmd line arguments.
-ArgResults getArgResults(List<String> args) {
-  final parser = ArgParser(allowTrailingOptions: true);
-
-  parser.addSeparator(
-      'FFIGEN: Generate dart bindings from C header files\nUsage:');
-  parser.addOption(
-    'config',
-    help: 'path to Yaml file containing configurations if not in pubspec.yaml',
-  );
-  parser.addOption(
-    'verbose',
-    abbr: 'v',
-    defaultsTo: 'info',
-    allowed: [
-      'all',
-      'fine',
-      'info',
-      'warning',
-      'severe',
-    ],
-  );
-  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;
-}
-
-/// Sets up the logging level and printing.
-void setupLogger(ArgResults result) {
-  if (result.wasParsed('verbose')) {
-    switch (result['verbose'] as String?) {
-      case 'all':
-        // Logs everything, the entire AST touched by our parser.
-        Logger.root.level = Level.ALL;
-        break;
-      case 'fine':
-        // Logs AST parts relevant to user (i.e those included in filters).
-        Logger.root.level = Level.FINE;
-        break;
-      case 'info':
-        // Logs relevant info for general user (default).
-        Logger.root.level = Level.INFO;
-        break;
-      case 'warning':
-        // Logs warnings for relevant stuff.
-        Logger.root.level = Level.WARNING;
-        break;
-      case 'severe':
-        // Logs severe warnings and errors.
-        Logger.root.level = Level.SEVERE;
-        break;
-    }
-    // Setup logger for printing (if verbosity was set by user).
-    Logger.root.onRecord.listen((record) {
-      final level = '[${record.level.name}]'.padRight(9);
-      printLog('${level}: ${record.message}', record.level);
-    });
-  } else {
-    // Setup logger for printing (if verbosity was not set by user).
-    Logger.root.onRecord.listen((record) {
-      if (record.level.value > Level.INFO.value) {
-        final level = '[${record.level.name}]'.padRight(9);
-        printLog('${level}: ${record.message}', record.level);
-      } else {
-        printLog(record.message, record.level);
-      }
-    });
-  }
-}
-
-void printLog(String log, Level level) {
-  // Prints text in red for Severe logs only.
-  if (level < Level.SEVERE) {
-    print(log);
-  } else {
-    print(errorPen(log));
-  }
-}
+export 'package:ffigen/src/executables/ffigen.dart';
diff --git a/bin/setup.dart b/bin/setup.dart
index 9949ebc..5b07636 100644
--- a/bin/setup.dart
+++ b/bin/setup.dart
@@ -1,276 +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.
+//
+// TODO(128): Remove this when package can run with sound null safety.
+// @dart=2.7
 
-/// =======================================================================
-/// =============== 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/ -I/usr/lib/llvm-11/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:ffigen/src/find_resource.dart';
-import 'package:ffigen/src/strings.dart' as strings;
-import 'package:path/path.dart' as path;
-
-/// Default platform options.
-final _linuxOpts = _Options(
-  sharedFlag: '-shared',
-  inputHeader: _getWrapperPath('wrapper.c'),
-  fPIC: '-fpic',
-  ldLibFlag: '-lclang',
-  headerIncludes: [
-    '-I/usr/lib/llvm-9/include/',
-    '-I/usr/lib/llvm-10/include/',
-    '-I/usr/lib/llvm-11/include/',
-  ],
-);
-final _windowsOpts = _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',
-  ],
-);
-final _macOSOpts = _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) {
-  // Parses the cmd args. This will print usage and exit if --help was passed.
-  final argResults = _getArgResults(arguments);
-
-  print('Building Dynamic Library for libclang wrapper...');
-  final options = _getPlatformOptions();
-  _deleteOldDylib();
-
-  // Updates header/lib includes in platform options.
-  _changeIncludesUsingCmdArgs(argResults, options);
-
-  // Run clang compiler to generate the dynamic library.
-  final processResult = _runClangProcess(options);
-  _printDetails(processResult, 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 = _runClangProcess(options);
-  if ((processResult.stderr as String).isNotEmpty) {
-    print(processResult.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.fromUri(findWrapper(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('Generates LLVM Wrapper 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(ArgResults argResult, _Options options) {
-  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 _macOSOpts;
-  } else if (Platform.isWindows) {
-    return _windowsOpts;
-  } else if (Platform.isLinux) {
-    return _linuxOpts;
-  } 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 = '',
-  });
-}
+export 'package:ffigen/src/executables/setup.dart';
diff --git a/lib/src/executables/ffigen.dart b/lib/src/executables/ffigen.dart
new file mode 100644
index 0000000..22b3744
--- /dev/null
+++ b/lib/src/executables/ffigen.dart
@@ -0,0 +1,211 @@
+// Copyright (c) 2021, 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.
+
+// Executable script to generate bindings for some C library.
+import 'dart:io';
+
+import 'package:args/args.dart';
+import 'package:cli_util/cli_logging.dart' show Ansi;
+import 'package:ffigen/ffigen.dart';
+import 'package:logging/logging.dart';
+import 'package:yaml/yaml.dart' as yaml;
+
+import 'package:ffigen/src/executables/setup.dart';
+
+final _logger = Logger('ffigen.ffigen');
+final _ansi = Ansi(Ansi.terminalSupportsAnsi);
+
+String successPen(String str) {
+  return '${_ansi.green}$str${_ansi.none}';
+}
+
+String errorPen(String str) {
+  return '${_ansi.red}$str${_ansi.none}';
+}
+
+void main(List<String> args) {
+  // Parses the cmd args. This will print usage and exit if --help was passed.
+  final argResult = getArgResults(args);
+
+  // Setup logging level and printing.
+  setupLogger(argResult);
+
+  /// 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()) {
+    _logger.severe('Unable to create dynamic library automatically.');
+    _logger.severe('If LLVM (9+) is installed, try running:');
+    _logger.severe(
+        '  pub run ffigen:setup -Ipath/to/llvm/include -Lpath/to/llvm/lib');
+    exit(1);
+  }
+
+  // Create a config object.
+  Config config;
+  try {
+    config = getConfig(argResult);
+  } on FormatException {
+    _logger.severe('Please fix configuration errors and re-run the tool.');
+    exit(1);
+  }
+
+  // Parse the bindings according to config object provided.
+  final library = parse(config);
+
+  // Generate file for the parsed bindings.
+  final gen = File(config.output);
+  library.generateFile(gen);
+  _logger
+      .info(successPen('Finished, Bindings generated in ${gen.absolute.path}'));
+}
+
+Config getConfig(ArgResults result) {
+  _logger.info('Running in ${Directory.current}');
+
+  if (result.wasParsed('config')) {
+    return getConfigFromCustomYaml(result['config'] as String);
+  } else {
+    return getConfigFromPubspec();
+  }
+}
+
+/// Extracts configuration from pubspec file.
+Config getConfigFromPubspec() {
+  final pubspecName = 'pubspec.yaml';
+  final configKey = 'ffigen';
+
+  final pubspecFile = File(pubspecName);
+
+  if (!pubspecFile.existsSync()) {
+    _logger.severe(
+        'Error: $pubspecName not found, please run this tool from the root of your package.');
+    exit(1);
+  }
+
+  // Casting this because pubspec is expected to be a YamlMap.
+
+  // Throws a [YamlException] if it's unable to parse the Yaml.
+  final bindingsConfigMap =
+      yaml.loadYaml(pubspecFile.readAsStringSync())[configKey] as yaml.YamlMap?;
+
+  if (bindingsConfigMap == null) {
+    _logger.severe("Couldn't find an entry for '$configKey' in $pubspecName.");
+    exit(1);
+  }
+  return Config.fromYaml(bindingsConfigMap);
+}
+
+/// Extracts configuration from a custom yaml file.
+Config getConfigFromCustomYaml(String yamlPath) {
+  final yamlFile = File(yamlPath);
+
+  if (!yamlFile.existsSync()) {
+    _logger.severe('Error: $yamlPath not found.');
+    exit(1);
+  }
+
+  // Throws a [YamlException] if it's unable to parse the Yaml.
+  final bindingsConfigMap =
+      yaml.loadYaml(yamlFile.readAsStringSync()) as yaml.YamlMap;
+
+  return Config.fromYaml(bindingsConfigMap);
+}
+
+/// Parses the cmd line arguments.
+ArgResults getArgResults(List<String> args) {
+  final parser = ArgParser(allowTrailingOptions: true);
+
+  parser.addSeparator(
+      'FFIGEN: Generate dart bindings from C header files\nUsage:');
+  parser.addOption(
+    'config',
+    help: 'path to Yaml file containing configurations if not in pubspec.yaml',
+  );
+  parser.addOption(
+    'verbose',
+    abbr: 'v',
+    defaultsTo: 'info',
+    allowed: [
+      'all',
+      'fine',
+      'info',
+      'warning',
+      'severe',
+    ],
+  );
+  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;
+}
+
+/// Sets up the logging level and printing.
+void setupLogger(ArgResults result) {
+  if (result.wasParsed('verbose')) {
+    switch (result['verbose'] as String?) {
+      case 'all':
+        // Logs everything, the entire AST touched by our parser.
+        Logger.root.level = Level.ALL;
+        break;
+      case 'fine':
+        // Logs AST parts relevant to user (i.e those included in filters).
+        Logger.root.level = Level.FINE;
+        break;
+      case 'info':
+        // Logs relevant info for general user (default).
+        Logger.root.level = Level.INFO;
+        break;
+      case 'warning':
+        // Logs warnings for relevant stuff.
+        Logger.root.level = Level.WARNING;
+        break;
+      case 'severe':
+        // Logs severe warnings and errors.
+        Logger.root.level = Level.SEVERE;
+        break;
+    }
+    // Setup logger for printing (if verbosity was set by user).
+    Logger.root.onRecord.listen((record) {
+      final level = '[${record.level.name}]'.padRight(9);
+      printLog('${level}: ${record.message}', record.level);
+    });
+  } else {
+    // Setup logger for printing (if verbosity was not set by user).
+    Logger.root.onRecord.listen((record) {
+      if (record.level.value > Level.INFO.value) {
+        final level = '[${record.level.name}]'.padRight(9);
+        printLog('${level}: ${record.message}', record.level);
+      } else {
+        printLog(record.message, record.level);
+      }
+    });
+  }
+}
+
+void printLog(String log, Level level) {
+  // Prints text in red for Severe logs only.
+  if (level < Level.SEVERE) {
+    print(log);
+  } else {
+    print(errorPen(log));
+  }
+}
diff --git a/lib/src/executables/setup.dart b/lib/src/executables/setup.dart
new file mode 100644
index 0000000..84a069b
--- /dev/null
+++ b/lib/src/executables/setup.dart
@@ -0,0 +1,276 @@
+// Copyright (c) 2021, 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/ -I/usr/lib/llvm-11/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:ffigen/src/find_resource.dart';
+import 'package:ffigen/src/strings.dart' as strings;
+import 'package:path/path.dart' as path;
+
+/// Default platform options.
+final _linuxOpts = _Options(
+  sharedFlag: '-shared',
+  inputHeader: _getWrapperPath('wrapper.c'),
+  fPIC: '-fpic',
+  ldLibFlag: '-lclang',
+  headerIncludes: [
+    '-I/usr/lib/llvm-9/include/',
+    '-I/usr/lib/llvm-10/include/',
+    '-I/usr/lib/llvm-11/include/',
+  ],
+);
+final _windowsOpts = _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',
+  ],
+);
+final _macOSOpts = _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) {
+  // Parses the cmd args. This will print usage and exit if --help was passed.
+  final argResults = _getArgResults(arguments);
+
+  print('Building Dynamic Library for libclang wrapper...');
+  final options = _getPlatformOptions();
+  _deleteOldDylib();
+
+  // Updates header/lib includes in platform options.
+  _changeIncludesUsingCmdArgs(argResults, options);
+
+  // Run clang compiler to generate the dynamic library.
+  final processResult = _runClangProcess(options);
+  _printDetails(processResult, 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 = _runClangProcess(options);
+  if ((processResult.stderr as String).isNotEmpty) {
+    print(processResult.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.fromUri(findWrapper(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('Generates LLVM Wrapper 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(ArgResults argResult, _Options options) {
+  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 _macOSOpts;
+  } else if (Platform.isWindows) {
+    return _windowsOpts;
+  } else if (Platform.isLinux) {
+    return _linuxOpts;
+  } 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/pubspec.yaml b/pubspec.yaml
index 2a07001..aaddf64 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: 2.0.0-nullsafety.0
+version: 2.0.0-nullsafety.1
 homepage: https://github.com/dart-lang/ffigen
 description: Experimental generator for FFI bindings, using LibClang to parse C header files.