From 831e0f6a57b863a69e2231a4d42790d3308a400e Mon Sep 17 00:00:00 2001 From: Daco Harkes Date: Thu, 9 Jan 2025 10:18:34 +0100 Subject: [PATCH 1/2] [native_assets_cli] Add example for downloading assets --- .github/workflows/download_asset.yaml | 117 ++++++++++++++++++ .github/workflows/native.yaml | 3 + .../example/build/download_asset/README.md | 15 +++ .../example/build/download_asset/ffigen.yaml | 20 +++ .../build/download_asset/hook/build.dart | 53 ++++++++ .../build/download_asset/lib/native_add.dart | 15 +++ .../lib/src/hook_helpers/c_build.dart | 39 ++++++ .../lib/src/hook_helpers/download.dart | 46 +++++++ .../lib/src/hook_helpers/hashes.dart | 31 +++++ .../lib/src/hook_helpers/target_versions.dart | 7 ++ .../lib/src/hook_helpers/targets.dart | 31 +++++ .../lib/src/hook_helpers/version.dart | 13 ++ .../example/build/download_asset/pubspec.yaml | 25 ++++ .../build/download_asset/src/native_add.c | 7 ++ .../build/download_asset/src/native_add.h | 13 ++ .../download_asset/test/native_add_test.dart | 12 ++ .../build/download_asset/tool/build.dart | 102 +++++++++++++++ .../tool/generate_asset_hashes.dart | 61 +++++++++ .../build/native_add_library/README.md | 4 +- 19 files changed, 612 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/download_asset.yaml create mode 100644 pkgs/native_assets_cli/example/build/download_asset/README.md create mode 100644 pkgs/native_assets_cli/example/build/download_asset/ffigen.yaml create mode 100644 pkgs/native_assets_cli/example/build/download_asset/hook/build.dart create mode 100644 pkgs/native_assets_cli/example/build/download_asset/lib/native_add.dart create mode 100644 pkgs/native_assets_cli/example/build/download_asset/lib/src/hook_helpers/c_build.dart create mode 100644 pkgs/native_assets_cli/example/build/download_asset/lib/src/hook_helpers/download.dart create mode 100644 pkgs/native_assets_cli/example/build/download_asset/lib/src/hook_helpers/hashes.dart create mode 100644 pkgs/native_assets_cli/example/build/download_asset/lib/src/hook_helpers/target_versions.dart create mode 100644 pkgs/native_assets_cli/example/build/download_asset/lib/src/hook_helpers/targets.dart create mode 100644 pkgs/native_assets_cli/example/build/download_asset/lib/src/hook_helpers/version.dart create mode 100644 pkgs/native_assets_cli/example/build/download_asset/pubspec.yaml create mode 100644 pkgs/native_assets_cli/example/build/download_asset/src/native_add.c create mode 100644 pkgs/native_assets_cli/example/build/download_asset/src/native_add.h create mode 100644 pkgs/native_assets_cli/example/build/download_asset/test/native_add_test.dart create mode 100644 pkgs/native_assets_cli/example/build/download_asset/tool/build.dart create mode 100644 pkgs/native_assets_cli/example/build/download_asset/tool/generate_asset_hashes.dart diff --git a/.github/workflows/download_asset.yaml b/.github/workflows/download_asset.yaml new file mode 100644 index 0000000000..f3391ba92d --- /dev/null +++ b/.github/workflows/download_asset.yaml @@ -0,0 +1,117 @@ +name: download_asset + +permissions: + contents: write + +on: + pull_request: + branches: [ main ] + paths: + - .github/workflows/download_asset.yaml + - pkgs/native_assets_cli/example/build/download_asset/ + push: + tags: + - 'download_asset-prebuild-assets-*' + workflow_dispatch: + +jobs: + build: + strategy: + fail-fast: false + matrix: + os: [ubuntu, macos, windows] + + runs-on: ${{ matrix.os }}-latest + + defaults: + run: + working-directory: pkgs/native_assets_cli/example/build/download_asset/ + + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + + - uses: dart-lang/setup-dart@e630b99d28a3b71860378cafdc2a067c71107f94 + with: + sdk: stable + + - uses: nttld/setup-ndk@afb4c9964b521afb97c864b7d40b11e6911bd410 + with: + ndk-version: r27 + if: ${{ matrix.os == 'ubuntu' }} # Only build on one host. + + - name: Install native toolchains + run: sudo apt-get update && sudo apt-get install clang-15 gcc-i686-linux-gnu gcc-aarch64-linux-gnu gcc-arm-linux-gnueabihf gcc-riscv64-linux-gnu + if: ${{ matrix.os == 'ubuntu' }} + + - run: dart pub get + + # Keep this list consistent with pkgs/native_assets_cli/example/build/download_asset/lib/src/hook_helpers/target_versions.dart + - name: Build Linux host + if: matrix.os == 'ubuntu' + run: | + dart tool/build.dart -oandroid -aarm + dart tool/build.dart -oandroid -aarm64 + dart tool/build.dart -oandroid -aia32 + dart tool/build.dart -oandroid -ariscv64 + dart tool/build.dart -oandroid -ax64 + dart tool/build.dart -olinux -aarm + dart tool/build.dart -olinux -aarm64 + dart tool/build.dart -olinux -aia32 + dart tool/build.dart -olinux -ariscv64 + dart tool/build.dart -olinux -ax64 + + - name: Build MacOS host + if: matrix.os == 'macos' + run: | + dart tool/build.dart -omacos -aarm64 + dart tool/build.dart -omacos -ax64 + dart tool/build.dart -oios -iiphoneos -aarm64 + dart tool/build.dart -oios -iiphonesimulator -aarm64 + dart tool/build.dart -oios -iiphonesimulator -ax64 + + - name: Build Windows host + if: matrix.os == 'windows' + run: | + dart tool/build.dart -owindows -aarm + dart tool/build.dart -owindows -aarm64 + dart tool/build.dart -owindows -aia32 + dart tool/build.dart -owindows -ax64 + + - name: Upload artifacts + uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 + with: + name: ${{ matrix.os }}-host + path: | + pkgs/native_assets_cli/example/build/download_asset/.dart_tool/download_asset/**/*.dll + pkgs/native_assets_cli/example/build/download_asset/.dart_tool/download_asset/**/*.dylib + pkgs/native_assets_cli/example/build/download_asset/.dart_tool/download_asset/**/*.so + if-no-files-found: error + + release: + needs: build + runs-on: ubuntu-latest + + defaults: + run: + working-directory: pkgs/native_assets_cli/example/build/download_asset/ + + steps: + - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 + with: + submodules: true + + - name: Download assets + uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 + with: + merge-multiple: true + path: pkgs/native_assets_cli/example/build/download_asset/.dart_tool/download_asset/ + + - name: Display structure of downloaded assets + run: ls -R .dart_tool/download_asset/ + + - name: Release + uses: softprops/action-gh-release@e7a8f85e1c67a31e6ed99a94b41bd0b71bbee6b8 + if: startsWith(github.ref, 'refs/tags/download_asset-prebuild-assets') + with: + files: 'pkgs/native_assets_cli/example/build/download_asset/.dart_tool/download_asset/**' + fail_on_unmatched_files: true diff --git a/.github/workflows/native.yaml b/.github/workflows/native.yaml index afcbe00ddf..123e92aa1e 100644 --- a/.github/workflows/native.yaml +++ b/.github/workflows/native.yaml @@ -105,6 +105,9 @@ jobs: - run: dart pub get -C test_data/native_dynamic_linking/ if: ${{ matrix.package == 'native_assets_builder' }} + - run: dart pub get -C example/build/download_asset/ + if: ${{ matrix.package == 'native_assets_cli' }} + - run: dart pub get -C example/build/native_dynamic_linking/ if: ${{ matrix.package == 'native_assets_cli' }} diff --git a/pkgs/native_assets_cli/example/build/download_asset/README.md b/pkgs/native_assets_cli/example/build/download_asset/README.md new file mode 100644 index 0000000000..0652769d94 --- /dev/null +++ b/pkgs/native_assets_cli/example/build/download_asset/README.md @@ -0,0 +1,15 @@ +An example of a library depending on prebuilt assets which are downloaded in +the build hook. + +## Usage + +Run tests with `dart --enable-experiment=native-assets test`. + +## Code organization + +A typical layout of a package which downloads assets: + +* `tool/build.dart` prebuilts assets and is exercised from a GitHub workflow. +* A [github workflow](../../../../../.github/workflows/native.yaml) that builds assets. +* `hook/build.dart` downloads the prebuilt assets. +* `lib/` contains Dart code which uses the assets. diff --git a/pkgs/native_assets_cli/example/build/download_asset/ffigen.yaml b/pkgs/native_assets_cli/example/build/download_asset/ffigen.yaml new file mode 100644 index 0000000000..f54281512b --- /dev/null +++ b/pkgs/native_assets_cli/example/build/download_asset/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/native_add.dart' +headers: + entry-points: + - 'src/native_add.h' + include-directives: + - 'src/native_add.h' +preamble: | + // Copyright (c) 2025, 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/pkgs/native_assets_cli/example/build/download_asset/hook/build.dart b/pkgs/native_assets_cli/example/build/download_asset/hook/build.dart new file mode 100644 index 0000000000..f4a501f5b6 --- /dev/null +++ b/pkgs/native_assets_cli/example/build/download_asset/hook/build.dart @@ -0,0 +1,53 @@ +// Copyright (c) 2025, 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:download_asset/src/hook_helpers/c_build.dart'; +import 'package:download_asset/src/hook_helpers/download.dart'; +import 'package:download_asset/src/hook_helpers/hashes.dart'; +import 'package:native_assets_cli/code_assets_builder.dart'; +import 'package:native_assets_cli/native_assets_cli.dart'; + +void main(List args) async { + // TODO(https://github.com/dart-lang/native/issues/39): Use user-defines to + // control this instead. + const localBuild = false; + + await build(args, (input, output) async { + // ignore: dead_code + if (localBuild) { + await runBuild(input, output); + } else { + final targetOS = input.config.code.targetOS; + final targetArchitecture = input.config.code.targetArchitecture; + final iOSSdk = + targetOS == OS.iOS ? input.config.code.iOS.targetSdk : null; + final outputDirectory = Directory.fromUri(input.outputDirectory); + final file = await downloadAsset( + targetOS, + targetArchitecture, + iOSSdk, + outputDirectory, + ); + final fileHash = await hashAsset(file); + final expectedHash = assetHashes[createTargetName( + targetOS.name, + targetArchitecture.name, + iOSSdk?.type, + )]; + if (fileHash != expectedHash) { + throw Exception('File $file was not downloaded correctly.'); + } + output.assets.code.add(CodeAsset( + package: input.packageName, + name: 'native_add.dart', + linkMode: DynamicLoadingBundled(), + os: targetOS, + architecture: targetArchitecture, + file: file.uri, + )); + } + }); +} diff --git a/pkgs/native_assets_cli/example/build/download_asset/lib/native_add.dart b/pkgs/native_assets_cli/example/build/download_asset/lib/native_add.dart new file mode 100644 index 0000000000..f9f8e1e7b5 --- /dev/null +++ b/pkgs/native_assets_cli/example/build/download_asset/lib/native_add.dart @@ -0,0 +1,15 @@ +// Copyright (c) 2025, 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.Native(symbol: 'add') +external int add( + int a, + int b, +); diff --git a/pkgs/native_assets_cli/example/build/download_asset/lib/src/hook_helpers/c_build.dart b/pkgs/native_assets_cli/example/build/download_asset/lib/src/hook_helpers/c_build.dart new file mode 100644 index 0000000000..65783eed83 --- /dev/null +++ b/pkgs/native_assets_cli/example/build/download_asset/lib/src/hook_helpers/c_build.dart @@ -0,0 +1,39 @@ +// Copyright (c) 2025, 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:logging/logging.dart'; +import 'package:native_assets_cli/code_assets_builder.dart'; +import 'package:native_toolchain_c/native_toolchain_c.dart'; + +Future runBuild(BuildInput input, BuildOutputBuilder output) async { + final name = createTargetName( + input.config.code.targetOS.name, + input.config.code.targetArchitecture.name, + input.config.code.targetOS == OS.iOS + ? input.config.code.iOS.targetSdk.type + : null, + ); + final cbuilder = CBuilder.library( + name: name, + assetName: 'native_add.dart', + sources: [ + 'src/native_add.c', + ], + ); + await cbuilder.run( + input: input, + output: output, + logger: Logger('') + ..level = Level.ALL + ..onRecord.listen((record) => print(record.message)), + ); +} + +String createTargetName(String osString, String architecture, String? iOSSdk) { + var targetName = 'native_add_${osString}_$architecture'; + if (iOSSdk != null) { + targetName += '_$iOSSdk'; + } + return targetName; +} diff --git a/pkgs/native_assets_cli/example/build/download_asset/lib/src/hook_helpers/download.dart b/pkgs/native_assets_cli/example/build/download_asset/lib/src/hook_helpers/download.dart new file mode 100644 index 0000000000..1a7bb4cae7 --- /dev/null +++ b/pkgs/native_assets_cli/example/build/download_asset/lib/src/hook_helpers/download.dart @@ -0,0 +1,46 @@ +// Copyright (c) 2025, 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:crypto/crypto.dart'; +import 'package:native_assets_cli/code_assets_builder.dart'; + +import 'c_build.dart'; +import 'version.dart'; + +Uri downloadUri(String target) => Uri.parse( + 'https://github.com/dart-lang/native/releases/download/$version/$target'); + +Future downloadAsset( + OS targetOS, + Architecture targetArchitecture, + IOSSdk? iOSSdk, + Directory outputDirectory, +) async { + final targetName = targetOS.dylibFileName(createTargetName( + targetOS.name, + targetArchitecture.name, + iOSSdk?.type, + )); + final uri = downloadUri(targetName); + final request = await HttpClient().getUrl(uri); + final response = await request.close(); + if (response.statusCode != 200) { + throw ArgumentError('The request to $uri failed.'); + } + final library = File.fromUri(outputDirectory.uri.resolve(targetName)); + await library.create(); + await response.pipe(library.openWrite()); + return library; +} + +Future hashAsset(File assetFile) async { + // TODO(dcharkes): Should this be a strong hash to not only check for download + // integrity but also safeguard against tampering? This would protected + // against the case where the binary hoster is compromised but pub is not + // compromised. + final fileHash = md5.convert(await assetFile.readAsBytes()).toString(); + return fileHash; +} diff --git a/pkgs/native_assets_cli/example/build/download_asset/lib/src/hook_helpers/hashes.dart b/pkgs/native_assets_cli/example/build/download_asset/lib/src/hook_helpers/hashes.dart new file mode 100644 index 0000000000..bdcdbb3ad7 --- /dev/null +++ b/pkgs/native_assets_cli/example/build/download_asset/lib/src/hook_helpers/hashes.dart @@ -0,0 +1,31 @@ +// Copyright (c) 2025, 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. + +// THIS FILE IS AUTOGENERATED. TO UPDATE, RUN +// +// dart --enable-experiment=native-assets tool/generate_asset_hashes.dart +// + +const assetHashes = { + 'libnative_add_android_arm.so': '2c38f3edc805a399dad866d619f9157d', + 'libnative_add_android_arm64.so': 'c4f0d8c4c50d1e83592e499e7434b967', + 'libnative_add_android_ia32.so': 'e3277144d97bd2c54beee581ed7e6665', + 'libnative_add_android_riscv64.so': '8c2576cbe75c9a23f2532ff895f94f76', + 'libnative_add_android_x64.so': '9a7bec53e1591091669ecd2bd20911d1', + 'libnative_add_ios_arm64_iphoneos.dylib': '1bf1473cacb7fd2778fc5bb28f0b61a2', + 'libnative_add_ios_arm64_iphonesimulator.dylib': + 'cdddffc0787e6a3a846affcb05fac3a8', + 'libnative_add_ios_x64_iphonesimulator.dylib': + '4aea6d631350540d50452ac2bdd1a422', + 'libnative_add_linux_arm.so': '1a5b9e4b459e13ee85c148582b9b2252', + 'libnative_add_linux_arm64.so': '2b3d736e0c0ac1e1537bc43bd9b82cad', + 'libnative_add_linux_ia32.so': 'ed6e130e53fa18eab5572ed106cdaab1', + 'libnative_add_linux_riscv64.so': '7fa82325ba7803a0443ca27e3300e7f9', + 'libnative_add_linux_x64.so': 'f7af8d1547cdfb150a73d513a7957999', + 'libnative_add_macos_arm64.dylib': 'f0804ff4b55126996c180114f25ca5bd', + 'libnative_add_macos_x64.dylib': 'b62263803dceb3c23508cb909b5a5583', + 'native_add_windows_arm64.dll': '7aa6f5e0275ba1b94cd5b7356f89ef04', + 'native_add_windows_ia32.dll': 'ab57b5504d92b5b5dc09732d990fd5e7', + 'native_add_windows_x64.dll': 'be9ba2125800aa2e3481a759b1845a50', +}; diff --git a/pkgs/native_assets_cli/example/build/download_asset/lib/src/hook_helpers/target_versions.dart b/pkgs/native_assets_cli/example/build/download_asset/lib/src/hook_helpers/target_versions.dart new file mode 100644 index 0000000000..bb62fd9686 --- /dev/null +++ b/pkgs/native_assets_cli/example/build/download_asset/lib/src/hook_helpers/target_versions.dart @@ -0,0 +1,7 @@ +// Copyright (c) 2025, 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. + +const androidTargetNdkApi = 30; +const int macOSTargetVersion = 13; +const iOSTargetVersion = 16; diff --git a/pkgs/native_assets_cli/example/build/download_asset/lib/src/hook_helpers/targets.dart b/pkgs/native_assets_cli/example/build/download_asset/lib/src/hook_helpers/targets.dart new file mode 100644 index 0000000000..cce15005f5 --- /dev/null +++ b/pkgs/native_assets_cli/example/build/download_asset/lib/src/hook_helpers/targets.dart @@ -0,0 +1,31 @@ +// Copyright (c, null) 2025, 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. + +// THIS FILE IS AUTOGENERATED. TO UPDATE, RUN +// +// dart --enable-experiment=native-assets tool/generate_asset_hashes.dart +// + +import 'package:native_assets_cli/code_assets_builder.dart'; + +const supportedTargets = [ + (OS.android, Architecture.arm, null), + (OS.android, Architecture.arm64, null), + (OS.android, Architecture.ia32, null), + (OS.android, Architecture.riscv64, null), + (OS.android, Architecture.x64, null), + (OS.iOS, Architecture.arm64, IOSSdk.iPhoneOS), + (OS.iOS, Architecture.arm64, IOSSdk.iPhoneSimulator), + (OS.iOS, Architecture.x64, IOSSdk.iPhoneSimulator), + (OS.linux, Architecture.arm, null), + (OS.linux, Architecture.arm64, null), + (OS.linux, Architecture.ia32, null), + (OS.linux, Architecture.riscv64, null), + (OS.linux, Architecture.x64, null), + (OS.macOS, Architecture.arm64, null), + (OS.macOS, Architecture.x64, null), + (OS.windows, Architecture.arm64, null), + (OS.windows, Architecture.ia32, null), + (OS.windows, Architecture.x64, null), +]; diff --git a/pkgs/native_assets_cli/example/build/download_asset/lib/src/hook_helpers/version.dart b/pkgs/native_assets_cli/example/build/download_asset/lib/src/hook_helpers/version.dart new file mode 100644 index 0000000000..08d37a1151 --- /dev/null +++ b/pkgs/native_assets_cli/example/build/download_asset/lib/src/hook_helpers/version.dart @@ -0,0 +1,13 @@ +// Copyright (c) 2025, 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 'download.dart'; +import 'hashes.dart'; + +/// The GitHub release to use for downloading assets. +/// +/// Assets are downloaded from [downloadUri]. +/// +/// After changing [assetHashes] must be updated. +const version = 'download_asset-prebuild-assets-v0.1.0-try-3'; diff --git a/pkgs/native_assets_cli/example/build/download_asset/pubspec.yaml b/pkgs/native_assets_cli/example/build/download_asset/pubspec.yaml new file mode 100644 index 0000000000..924cd0bc28 --- /dev/null +++ b/pkgs/native_assets_cli/example/build/download_asset/pubspec.yaml @@ -0,0 +1,25 @@ +publish_to: none + +name: download_asset +description: Sums two numbers with native code, prebuilt assets. +version: 0.1.0 +repository: https://github.com/dart-lang/native/tree/main/pkgs/native_assets_cli/example/build/download_asset + +environment: + sdk: '>=3.3.0 <4.0.0' + +dependencies: + crypto: ^3.0.3 + logging: ^1.1.1 + # native_assets_cli: ^0.10.0 + native_assets_cli: + path: ../../../../native_assets_cli/ + # native_toolchain_c: ^0.7.0 + native_toolchain_c: + path: ../../../../native_toolchain_c/ + +dev_dependencies: + args: ^2.6.0 + ffigen: ^8.0.2 + lints: ^3.0.0 + test: ^1.21.0 diff --git a/pkgs/native_assets_cli/example/build/download_asset/src/native_add.c b/pkgs/native_assets_cli/example/build/download_asset/src/native_add.c new file mode 100644 index 0000000000..91926c2a54 --- /dev/null +++ b/pkgs/native_assets_cli/example/build/download_asset/src/native_add.c @@ -0,0 +1,7 @@ +// Copyright (c) 2025, 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/pkgs/native_assets_cli/example/build/download_asset/src/native_add.h b/pkgs/native_assets_cli/example/build/download_asset/src/native_add.h new file mode 100644 index 0000000000..196445bb56 --- /dev/null +++ b/pkgs/native_assets_cli/example/build/download_asset/src/native_add.h @@ -0,0 +1,13 @@ +// Copyright (c) 2025, 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 + +#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/pkgs/native_assets_cli/example/build/download_asset/test/native_add_test.dart b/pkgs/native_assets_cli/example/build/download_asset/test/native_add_test.dart new file mode 100644 index 0000000000..ef7e40c9d3 --- /dev/null +++ b/pkgs/native_assets_cli/example/build/download_asset/test/native_add_test.dart @@ -0,0 +1,12 @@ +// Copyright (c) 2025, 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:download_asset/native_add.dart'; +import 'package:test/test.dart'; + +void main() { + test('invoke native function', () { + expect(add(24, 18), 42); + }); +} diff --git a/pkgs/native_assets_cli/example/build/download_asset/tool/build.dart b/pkgs/native_assets_cli/example/build/download_asset/tool/build.dart new file mode 100644 index 0000000000..59c2f80fb2 --- /dev/null +++ b/pkgs/native_assets_cli/example/build/download_asset/tool/build.dart @@ -0,0 +1,102 @@ +// Copyright (c) 2025, 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:args/args.dart'; +import 'package:download_asset/src/hook_helpers/c_build.dart'; +import 'package:download_asset/src/hook_helpers/target_versions.dart'; +import 'package:native_assets_cli/code_assets_builder.dart'; + +void main(List args) async { + final ( + os: os, + architecture: architecture, + iOSSdk: iOSSdk, + ) = parseArguments(args); + final input = createBuildInput(os, architecture, iOSSdk); + final output = BuildOutputBuilder(); + await runBuild(input, output); +} + +({String architecture, String os, String? iOSSdk}) parseArguments( + List args) { + final parser = ArgParser() + ..addOption( + 'architecture', + abbr: 'a', + allowed: Architecture.values.map((a) => a.name), + mandatory: true, + ) + ..addOption( + 'os', + abbr: 'o', + allowed: OS.values.map((a) => a.name), + mandatory: true, + ) + ..addOption( + 'iossdk', + abbr: 'i', + allowed: IOSSdk.values.map((a) => a.type), + help: 'Required if OS is iOS.', + ); + final argResults = parser.parse(args); + + final os = argResults.option('os'); + final architecture = argResults.option('architecture'); + final iOSSdk = argResults.option('iossdk'); + if (os == null || + architecture == null || + (os == OS.iOS.name && iOSSdk == null)) { + print(parser.usage); + exit(1); + } + return ( + os: os, + architecture: architecture, + iOSSdk: iOSSdk, + ); +} + +BuildInput createBuildInput( + String osString, + String architecture, + String? iOSSdk, +) { + final packageRoot = Platform.script.resolve('..'); + final targetName = createTargetName(osString, architecture, iOSSdk); + final outputDirectory = + packageRoot.resolve('.dart_tool/download_asset/$targetName/'); + final outputDirectoryShared = + packageRoot.resolve('.dart_tool/download_asset/shared/'); + + final os = OS.fromString(osString); + final inputBuilder = BuildInputBuilder() + ..setupShared( + packageRoot: packageRoot, + packageName: 'download_asset', + outputDirectory: outputDirectory, + outputDirectoryShared: outputDirectoryShared) + ..config.setupShared( + buildAssetTypes: [CodeAsset.type], + ) + ..config.setupBuild(dryRun: false, linkingEnabled: false) + ..config.setupCode( + targetArchitecture: Architecture.fromString(architecture), + targetOS: os, + linkModePreference: LinkModePreference.dynamic, + android: os != OS.android + ? null + : AndroidConfig( + targetNdkApi: androidTargetNdkApi, + ), + iOS: os != OS.iOS + ? null + : IOSConfig( + targetSdk: IOSSdk.fromString(iOSSdk!), + targetVersion: iOSTargetVersion, + ), + macOS: MacOSConfig(targetVersion: macOSTargetVersion)); + return BuildInput(inputBuilder.json); +} diff --git a/pkgs/native_assets_cli/example/build/download_asset/tool/generate_asset_hashes.dart b/pkgs/native_assets_cli/example/build/download_asset/tool/generate_asset_hashes.dart new file mode 100644 index 0000000000..bd3df512be --- /dev/null +++ b/pkgs/native_assets_cli/example/build/download_asset/tool/generate_asset_hashes.dart @@ -0,0 +1,61 @@ +// Copyright (c) 2025, 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:download_asset/src/hook_helpers/download.dart'; +import 'package:download_asset/src/hook_helpers/hashes.dart'; +import 'package:download_asset/src/hook_helpers/targets.dart'; + +/// Regenerates [assetHashes]. +Future main(List args) async { + final assetsDir = Directory.fromUri( + Platform.script.resolve('../.dart_tool/download_asset/')); + await assetsDir.delete(recursive: true); + await assetsDir.create(recursive: true); + await Future.wait([ + for (final (targetOS, targetArchitecture, iOSSdk) in supportedTargets) + downloadAsset(targetOS, targetArchitecture, iOSSdk, assetsDir), + ]); + final assetFiles = assetsDir + .listSync(recursive: true) + .whereType() + .toList() + ..sort((f1, f2) => f1.path.compareTo(f2.path)); + final assetHashes = {}; + for (final assetFile in assetFiles) { + final fileHash = await hashAsset(assetFile); + final target = assetFile.uri.pathSegments.lastWhere((e) => e.isNotEmpty); + assetHashes[target] = fileHash; + } + + await writeHashesFile(assetHashes); +} + +Future writeHashesFile(Map assetHashes) async { + final hashesFile = File.fromUri( + Platform.script.resolve('../lib/src/hook_helpers/hashes.dart')); + await hashesFile.create(recursive: true); + final buffer = StringBuffer(); + buffer.write(''' +// Copyright (c) 2025, 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. + +// THIS FILE IS AUTOGENERATED. TO UPDATE, RUN +// +// dart --enable-experiment=native-assets tool/generate_asset_hashes.dart +// + +const assetHashes = { +'''); + for (final hash in assetHashes.entries) { + buffer.write(" '${hash.key}': '${hash.value}',\n"); + } + buffer.write(''' +}; +'''); + await hashesFile.writeAsString(buffer.toString()); + await Process.run(Platform.executable, ['format', hashesFile.path]); +} diff --git a/pkgs/native_assets_cli/example/build/native_add_library/README.md b/pkgs/native_assets_cli/example/build/native_add_library/README.md index 180cb02959..5a00ac4e67 100644 --- a/pkgs/native_assets_cli/example/build/native_add_library/README.md +++ b/pkgs/native_assets_cli/example/build/native_add_library/README.md @@ -9,8 +9,8 @@ Run tests with `dart --enable-experiment=native-assets test`. A typical layout of a package with native code is: +* `hook/build.dart` implements the CLI that communicates which native assets to + build/bundle with the Dart/Flutter SDK. * `lib/` contains Dart code which uses [`dart:ffi`] and [`package:ffigen`] to call into native code. * `src/` contains C code which is built and then invoked through `dart:ffi`. -* `build.dart` implements the CLI that communicates which native assets - to build/bundle with the Dart/Flutter SDK. From 16d4e3383de19261a633504ebf8fa991e2c92008 Mon Sep 17 00:00:00 2001 From: Daco Harkes Date: Thu, 9 Jan 2025 11:05:06 +0100 Subject: [PATCH 2/2] address comments --- .../{download_asset.yaml => package_download_asset.yaml} | 6 ++++-- .../example/build/download_asset/README.md | 2 +- .../example/build/download_asset/hook/build.dart | 3 ++- 3 files changed, 7 insertions(+), 4 deletions(-) rename .github/workflows/{download_asset.yaml => package_download_asset.yaml} (95%) diff --git a/.github/workflows/download_asset.yaml b/.github/workflows/package_download_asset.yaml similarity index 95% rename from .github/workflows/download_asset.yaml rename to .github/workflows/package_download_asset.yaml index f3391ba92d..96415865d0 100644 --- a/.github/workflows/download_asset.yaml +++ b/.github/workflows/package_download_asset.yaml @@ -1,4 +1,6 @@ -name: download_asset +# A workflow that goes together with the example package:download_asset inside +# package:native_assets_cli. +name: package_download_asset permissions: contents: write @@ -7,7 +9,7 @@ on: pull_request: branches: [ main ] paths: - - .github/workflows/download_asset.yaml + - .github/workflows/package_download_asset.yaml - pkgs/native_assets_cli/example/build/download_asset/ push: tags: diff --git a/pkgs/native_assets_cli/example/build/download_asset/README.md b/pkgs/native_assets_cli/example/build/download_asset/README.md index 0652769d94..d9d31c8eec 100644 --- a/pkgs/native_assets_cli/example/build/download_asset/README.md +++ b/pkgs/native_assets_cli/example/build/download_asset/README.md @@ -10,6 +10,6 @@ Run tests with `dart --enable-experiment=native-assets test`. A typical layout of a package which downloads assets: * `tool/build.dart` prebuilts assets and is exercised from a GitHub workflow. -* A [github workflow](../../../../../.github/workflows/native.yaml) that builds assets. +* A [GitHub workflow](../../../../../.github/workflows/package_download_asset.yaml) that builds assets. * `hook/build.dart` downloads the prebuilt assets. * `lib/` contains Dart code which uses the assets. diff --git a/pkgs/native_assets_cli/example/build/download_asset/hook/build.dart b/pkgs/native_assets_cli/example/build/download_asset/hook/build.dart index f4a501f5b6..21d8719ec0 100644 --- a/pkgs/native_assets_cli/example/build/download_asset/hook/build.dart +++ b/pkgs/native_assets_cli/example/build/download_asset/hook/build.dart @@ -38,7 +38,8 @@ void main(List args) async { iOSSdk?.type, )]; if (fileHash != expectedHash) { - throw Exception('File $file was not downloaded correctly.'); + throw Exception('File $file was not downloaded correctly. ' + 'Found hash $fileHash, expected $expectedHash.'); } output.assets.code.add(CodeAsset( package: input.packageName,