|
| 1 | +// ignore_for_file: comment_references |
| 2 | +import 'dart:io'; |
| 3 | + |
| 4 | +import 'package:logging/logging.dart'; |
| 5 | +import 'package:native_assets_cli/code_assets_builder.dart'; |
| 6 | +import 'package:path/path.dart' as p; |
| 7 | + |
| 8 | +import '../builder/linkmode.dart'; |
| 9 | + |
| 10 | +/// Searches recursively through the provided [outDir] (or [input.outputDirectory] |
| 11 | +/// if not provided) for native library files that match the given [names] |
| 12 | +/// and adds them to [output.assets.code]. |
| 13 | +/// |
| 14 | +/// [names] are used to map a found library file to package URI. |
| 15 | +/// e.g., for `foo_windows_x64.dll` -> `package:my_pkg/native_foo.dart`, |
| 16 | +/// should provide `{'foo_windows_x64': 'native_foo.dart'}`. |
| 17 | +/// |
| 18 | +/// If [regExp] is true, keys in [names] are treated as regular expression patterns to match library filenames. |
| 19 | +/// Use regExp if the built libraries are something like `libadd-1.dll`. |
| 20 | +/// e.g., |
| 21 | +/// ```dart |
| 22 | +/// {r'(lib)?add-\d\.(dll|so|dylib)': 'native_foo.dart'}. |
| 23 | +/// ``` |
| 24 | +/// |
| 25 | +/// The expected filename is computed using the current operating system's naming conventions |
| 26 | +/// combined with a concrete [LinkMode] derived from [input.config.code.linkModePreference]. |
| 27 | +/// For each file that ends with the computed library filename for one of the provided [names], |
| 28 | +/// a [CodeAsset] is created and added to [output.assets.code] if it hasn't already been added. |
| 29 | +/// |
| 30 | +/// Duplicate assets are avoided by tracking previously added file paths internally |
| 31 | +/// as [output.assets] is not iterable. |
| 32 | +/// |
| 33 | +/// Returns a list of URIs corresponding to all the added code assets. |
| 34 | +/// |
| 35 | +/// Example: |
| 36 | +/// ```dart |
| 37 | +/// final foundUris = await addCodeAssets( |
| 38 | +/// input, |
| 39 | +/// output, |
| 40 | +/// outDir: myOutputUri, |
| 41 | +/// names: {'add': 'add.dart'}, |
| 42 | +/// ); |
| 43 | +/// ``` |
| 44 | +/// |
| 45 | +/// ```dart |
| 46 | +/// final foundUris = await addCodeAssets( |
| 47 | +/// input, |
| 48 | +/// output, |
| 49 | +/// outDir: myOutputUri, |
| 50 | +/// names: { r'(lib)?add\.(dll|so|dylib)': 'add.dart' }, |
| 51 | +/// regExp=true, |
| 52 | +/// ); |
| 53 | +/// ``` |
| 54 | +Future<List<Uri>> addFoundCodeAssets( |
| 55 | + BuildInput input, |
| 56 | + BuildOutputBuilder output, { |
| 57 | + required Map<String, String> names, // key: search library name, value: dart ffi name |
| 58 | + Uri? outDir, |
| 59 | + Logger? logger, |
| 60 | + bool regExp = false, |
| 61 | +}) async { |
| 62 | + final preferredMode = input.config.code.linkModePreference; |
| 63 | + final searchDir = Directory.fromUri(outDir ?? input.outputDirectory); |
| 64 | + final linkMode = getLinkMode(preferredMode); |
| 65 | + final List<Uri> foundFiles = []; |
| 66 | + |
| 67 | + logger?.info('Searching for libraries in ${searchDir.path}'); |
| 68 | + logger?.info('Preferred link mode: $preferredMode'); |
| 69 | + |
| 70 | + await for (final entity in searchDir.list(recursive: true, followLinks: false)) { |
| 71 | + if (entity is! File) continue; |
| 72 | + for (final MapEntry(:key, value: name) in names.entries) { |
| 73 | + // even on windows, library name may be `libadd.dll` instead of `add.dll` sometimes, |
| 74 | + // e.g., when using mingw64, so allow using RegExp matching if regExp=true. |
| 75 | + final found = regExp |
| 76 | + ? RegExp(key).hasMatch(p.basename(entity.path)) |
| 77 | + : entity.path.endsWith(OS.current.libraryFileName(key, linkMode)); |
| 78 | + if (!found) continue; |
| 79 | + logger?.info('Found library file: ${entity.path}'); |
| 80 | + output.addCodeAsset( |
| 81 | + CodeAsset( |
| 82 | + package: input.packageName, |
| 83 | + name: name, |
| 84 | + linkMode: linkMode, |
| 85 | + os: input.config.code.targetOS, |
| 86 | + file: entity.uri, |
| 87 | + architecture: input.config.code.targetArchitecture, |
| 88 | + ), |
| 89 | + ); |
| 90 | + foundFiles.add(entity.uri); |
| 91 | + break; // only add one file per name |
| 92 | + } |
| 93 | + } |
| 94 | + return foundFiles; |
| 95 | +} |
| 96 | + |
| 97 | +extension BuildOutputBuilderCodeAssets on BuildOutputBuilder { |
| 98 | + /// short for [assets.code.add] |
| 99 | + void addCodeAsset(CodeAsset codeAsset) { |
| 100 | + assets.code.add(codeAsset); |
| 101 | + } |
| 102 | + |
| 103 | + /// extension method for [addFoundCodeAssets] |
| 104 | + Future<List<Uri>> findAndAddCodeAssets( |
| 105 | + BuildInput input, { |
| 106 | + required Map<String, String> names, |
| 107 | + Uri? outDir, |
| 108 | + Logger? logger, |
| 109 | + bool regExp = false, |
| 110 | + }) async => |
| 111 | + addFoundCodeAssets(input, this, names: names, outDir: outDir, logger: logger, regExp: regExp); |
| 112 | +} |
0 commit comments