Native assets integration test
diff --git a/integration_tests/native_add/bin/native.dart b/integration_tests/native_add/bin/native.dart
new file mode 100644
index 0000000..4e4cded
--- /dev/null
+++ b/integration_tests/native_add/bin/native.dart
@@ -0,0 +1,60 @@
+// Copyright (c) 2023, 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:cc/cc.dart';
+import 'package:config/config.dart';
+import 'package:native_assets_cli/native_assets_cli.dart';
+import 'package:task_runner/task_runner.dart';
+
+const packageName = 'native_add';
+const assetName =
+    'package:$packageName/src/${packageName}_bindings_generated.dart';
+const sourcePaths = [
+  'src/$packageName.c',
+];
+
+void main(List<String> args) async {
+  final taskRunner = TaskRunner();
+  final config =
+      await Config.fromArgs(args: args, environment: Platform.environment);
+  final nativeAssetsConfig = NativeAssetsCliConfig.fromConfig(config);
+  final outDir = nativeAssetsConfig.outDir;
+  final packageRoot = nativeAssetsConfig.packageRoot;
+  await Directory.fromUri(outDir).create(recursive: true);
+  final packaging = nativeAssetsConfig.packaging.preferredPackaging.first;
+  final libUri = outDir.resolve(
+      nativeAssetsConfig.target.os.libraryFileName(packageName, packaging));
+  final sources = [for (final path in sourcePaths) packageRoot.resolve(path)];
+
+  final task = CBuilder(
+    config: config,
+    sources: sources,
+    dynamicLibrary: packaging == Packaging.dynamic ? libUri : null,
+    staticLibrary: packaging == Packaging.static ? libUri : null,
+  );
+  await task.run(taskRunner: taskRunner);
+
+  final builtInfo = BuiltInfo(
+    timestamp: DateTime.now().copyWith(millisecond: 0, microsecond: 0),
+    assets: [
+      Asset(
+        name: assetName,
+        packaging: packaging,
+        target: nativeAssetsConfig.target,
+        path: AssetAbsolutePath(libUri),
+      )
+    ],
+  );
+  final builtInfoUri = outDir.resolve('built_info.yaml');
+  await File.fromUri(builtInfoUri).writeAsString(builtInfo.toYaml());
+
+  final dependencies = Dependencies([
+    ...sources,
+    packageRoot.resolve('bin/native.dart'),
+  ]);
+  final dependenciesUri = outDir.resolve('dependencies.yaml');
+  await File.fromUri(dependenciesUri).writeAsString(dependencies.toYaml());
+}
diff --git a/integration_tests/native_add/ffigen.yaml b/integration_tests/native_add/ffigen.yaml
new file mode 100644
index 0000000..b8716bd
--- /dev/null
+++ b/integration_tests/native_add/ffigen.yaml
@@ -0,0 +1,20 @@
+# Run with `flutter pub run ffigen --config ffigen.yaml`.
+name: NativeAddBindings
+description: |
+  Bindings for `src/native_add.h`.
+
+  Regenerate bindings with `flutter pub run ffigen --config ffigen.yaml`.
+output: "lib/src/native_add_bindings_generated.dart"
+headers:
+  entry-points:
+    - "src/native_add.h"
+  include-directives:
+    - "src/native_add.h"
+preamble: |
+  // Copyright (c) 2023, 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.
+comments:
+  style: any
+  length: full
+ffi-native:
diff --git a/integration_tests/native_add/lib/native_add.dart b/integration_tests/native_add/lib/native_add.dart
new file mode 100644
index 0000000..9eda681
--- /dev/null
+++ b/integration_tests/native_add/lib/native_add.dart
@@ -0,0 +1,5 @@
+// Copyright (c) 2023, 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.
+
+export 'src/native_add.dart';
diff --git a/integration_tests/native_add/lib/src/native_add.dart b/integration_tests/native_add/lib/src/native_add.dart
new file mode 100644
index 0000000..3eb06d5
--- /dev/null
+++ b/integration_tests/native_add/lib/src/native_add.dart
@@ -0,0 +1,9 @@
+// Copyright (c) 2023, 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 'native_add_bindings_generated.dart' as bindings;
+
+// TODO(dacoharkes): This will fail lookup until the Dart SDK consumes the
+// output of `bin/native.dart`.
+int add(int a, int b) => bindings.add(a, b);
diff --git a/integration_tests/native_add/lib/src/native_add_bindings_generated.dart b/integration_tests/native_add/lib/src/native_add_bindings_generated.dart
new file mode 100644
index 0000000..07b6fc5
--- /dev/null
+++ b/integration_tests/native_add/lib/src/native_add_bindings_generated.dart
@@ -0,0 +1,15 @@
+// Copyright (c) 2023, 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.
+
+// AUTO GENERATED FILE, DO NOT EDIT.
+//
+// Generated by `package:ffigen`.
+// ignore_for_file: type=lint
+import 'dart:ffi' as ffi;
+
+@ffi.FfiNative<ffi.Int32 Function(ffi.Int32, ffi.Int32)>('add')
+external int add(
+  int a,
+  int b,
+);
diff --git a/integration_tests/native_add/pubspec.yaml b/integration_tests/native_add/pubspec.yaml
new file mode 100644
index 0000000..44dfa29
--- /dev/null
+++ b/integration_tests/native_add/pubspec.yaml
@@ -0,0 +1,44 @@
+publish_to: none
+
+name: native_add
+description: Sums two numbers with native code.
+version: 0.1.0
+repository: https://github.com/dcharkes/native_assets_cli
+
+environment:
+  sdk: ">=2.19.3 <4.0.0"
+
+dependencies:
+  cc:
+    git:
+      url: https://github.com/dcharkes/cc.git
+      ref: 5f6a3c8d5248648b21a66788438f39bf8629d7cb
+  config:
+    git:
+      url: https://github.com/dcharkes/config.git
+      ref: a8059f7e7785a9411d915ac27d175059c822b54d
+  native_assets_cli:
+    git:
+      url: https://github.com/dcharkes/native_assets_cli.git
+      ref: 1b8b2b7c08d0ef20c1e66d734b34cd3a306915bf
+  task_runner:
+    git:
+      url: https://github.com/dcharkes/task_runner.git
+      ref: 23a378bf4fac090fe9c46a07daf32945c598ef4a
+
+dev_dependencies:
+  ffigen: ^7.2.9
+  lints: ^2.0.0
+  test: any
+
+dependency_overrides:
+  test:
+    path: ../../pkgs/test
+  test_api:
+    path: ../../pkgs/test_api
+  test_core:
+    path: ../../pkgs/test_core
+  matcher:
+    git:
+      url: https://github.com/dart-lang/matcher.git
+      ref: 3fc9b3c35c3c0465ebef9158971f8203a4fc9416
diff --git a/integration_tests/native_add/src/native_add.c b/integration_tests/native_add/src/native_add.c
new file mode 100644
index 0000000..cf21076
--- /dev/null
+++ b/integration_tests/native_add/src/native_add.c
@@ -0,0 +1,9 @@
+// Copyright (c) 2023, 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.
+
+#include "native_add.h"
+
+int32_t add(int32_t a, int32_t b) {
+   return a + b;
+}
diff --git a/integration_tests/native_add/src/native_add.h b/integration_tests/native_add/src/native_add.h
new file mode 100644
index 0000000..5824f98
--- /dev/null
+++ b/integration_tests/native_add/src/native_add.h
@@ -0,0 +1,13 @@
+// Copyright (c) 2023, 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.
+
+#include <stdint.h>
+
+#if _WIN32
+#define MYLIB_EXPORT __declspec(dllexport)
+#else
+#define MYLIB_EXPORT
+#endif
+
+MYLIB_EXPORT int32_t add(int32_t a, int32_t b);
diff --git a/integration_tests/native_add/test/native_add_test.dart b/integration_tests/native_add/test/native_add_test.dart
new file mode 100644
index 0000000..aed9d8d
--- /dev/null
+++ b/integration_tests/native_add/test/native_add_test.dart
@@ -0,0 +1,13 @@
+// Copyright (c) 2023, 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 'package:native_add/native_add.dart';
+import 'package:test/test.dart';
+
+void main() {
+  test('foo', () {
+    final result = add(4, 6);
+    expect(result, equals(10));
+  });
+}