Skip to content

Commit dc30f3c

Browse files
MichealReedrainyl
andauthored
Implements addFoundCodeAssets to find and add outputs by name. (#11)
* find_assets * rename to add_assets * comment data assets not available yet * comment in example * needs architecture * update test names * fix unix problem with addDirectories * rename: `addLibraries -> addCodeAssets`, `addAssets -> addDataAssets`, use `patternMap` for `addCodeAssets` * fix pattern for windows * use string list for code asset name * fix comments * fix comment * refactor to map library names to dart, removes data asset helpers * update changelog * cleanup data assets from cmake * cleanup data assets from cmake tests * support optional regexp matching --------- Co-authored-by: rainyl <[email protected]>
1 parent 4b56fe6 commit dc30f3c

File tree

8 files changed

+216
-23
lines changed

8 files changed

+216
-23
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
- new: add `CMakeBuilder.fromGit` constructor to create a builder from a remote repository.
66
- new: add `buildLocal` optional parameter to build out of `.dart_tool`.
7+
- new: add `AddFoundCodeAssets`, `BuildOutputBuilder.findAndAddCodeAssets` to find and add code assets.
78
- breaking change: move android and `ios.toolchain.cmake` related args of `CMakeBuilder` to separate `AndroidBuilderArgs` and `AppleBuilderArgs`.
89

910
## 0.0.2

analysis_options.yaml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,6 @@ linter:
218218
- unnecessary_this
219219
- unnecessary_to_list_in_spreads
220220
- unrelated_type_equality_checks
221-
- unsafe_html
222221
- use_build_context_synchronously
223222
- use_colored_box
224223
- use_decorated_box

example/add/hook/build.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
// ignore_for_file: dead_code
12
import 'dart:io';
23

34
import 'package:add/src/hook_helpers/hook_helpers.dart';

example/add/lib/src/hook_helpers/hook_helpers.dart

Lines changed: 20 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ Future<void> runBuild(
1111
BuildOutputBuilder output,
1212
Uri sourceDir,
1313
) async {
14+
final _logger = Logger('')
15+
..level = Level.ALL
16+
// temp fwd to stderr until process logs pass to stdout
17+
..onRecord.listen((record) => stderr.writeln(record));
1418
final builder = CMakeBuilder.create(
1519
name: name,
1620
sourceDir: sourceDir,
@@ -22,32 +26,25 @@ Future<void> runBuild(
2226
'install',
2327
],
2428
buildLocal: true,
25-
logger: Logger('')
26-
..level = Level.ALL
27-
// temp fwd to stderr until process logs pass to stdout
28-
..onRecord.listen((record) => stderr.writeln(record)),
29+
logger: _logger,
2930
);
3031

31-
await builder.run(input: input, output: output);
32+
await builder.run(input: input, output: output, logger: _logger);
3233

33-
final libPath = switch (input.config.code.targetOS) {
34-
OS.linux => "install/lib/libadd.so",
35-
OS.macOS => "install/lib/libadd.dylib",
36-
OS.windows => "install/lib/add.dll",
37-
OS.android => "install/lib/libadd.so",
38-
OS.iOS => "install/lib/libadd.dylib",
39-
_ => throw UnsupportedError("Unsupported OS")
40-
};
41-
output.assets.code.add(
42-
CodeAsset(
43-
package: name,
44-
name: '$name.dart',
45-
linkMode: DynamicLoadingBundled(),
46-
os: input.config.code.targetOS,
47-
file: input.outputDirectory.resolve(libPath),
48-
architecture: input.config.code.targetArchitecture,
49-
),
34+
final buildjson = input.config.json;
35+
_logger.info('Build output: $buildjson');
36+
37+
// automatically search and add libraries
38+
final outLibs = await output.findAndAddCodeAssets(
39+
input,
40+
names: {r'(lib)?add\.(dll|so|dylib)': 'add.dart'},
41+
outDir: input.outputDirectory.resolve('install'),
42+
logger: _logger,
43+
regExp: true,
5044
);
45+
46+
// Do something else with outLibs uris
47+
_logger.info('Found libs: $outLibs');
5148
}
5249

5350
Future<void> runBuildGit(
@@ -80,6 +77,7 @@ Future<void> runBuildGit(
8077
logger: logger,
8178
);
8279

80+
// manually add assets
8381
final libPath = switch (input.config.code.targetOS) {
8482
OS.linux => "install/lib/libadd.so",
8583
OS.macOS => "install/lib/libadd.dylib",

lib/native_toolchain_cmake.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,6 @@ export 'src/builder/builder.dart' show CMakeBuilder;
99
export 'src/builder/builder_args.dart';
1010
export 'src/builder/generator.dart';
1111
export 'src/builder/log_level.dart';
12+
export 'src/utils/add_assets.dart';
1213
export 'src/utils/env_from_bat.dart';
1314
export 'src/utils/package_config_parser.dart';

lib/src/utils/add_assets.dart

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
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+
}

pubspec.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ dependencies:
1919
logging: ^1.1.1
2020
meta: ^1.9.1
2121
native_assets_cli: ^0.11.0
22+
path: ^1.9.1
2223
pub_semver: ^2.1.3
2324

2425
dev_dependencies:

test/utils/add_assets_test.dart

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import 'dart:io';
2+
3+
import 'package:logging/logging.dart';
4+
import 'package:native_toolchain_cmake/native_toolchain_cmake.dart';
5+
import 'package:native_toolchain_cmake/src/utils/add_assets.dart';
6+
import 'package:test/test.dart';
7+
8+
import '../helpers.dart';
9+
10+
void main() {
11+
final logger = Logger('AddAssetsTest');
12+
13+
group('addLibraries', () {
14+
test('finds and adds library files matching expected names', () async {
15+
// Create temporary directories as URIs.
16+
final baseDir = await tempDirForTest();
17+
final sharedDir = await tempDirForTest();
18+
19+
// Create a "lib" subdirectory.
20+
final libDir = Directory.fromUri(baseDir.resolve('lib'));
21+
await libDir.create();
22+
23+
// Based on the current OS, create a dummy dynamic library file for the "add" project.
24+
final libFileName = OS.current.dylibFileName('add'); // e.g. add.dll, libadd.so, or add.dylib.
25+
final libFile = File.fromUri(baseDir.resolve('lib').resolve(libFileName));
26+
await libFile.writeAsString('dummy library content');
27+
28+
final buildInputBuilder = BuildInputBuilder()
29+
..setupShared(
30+
packageName: 'add',
31+
packageRoot: baseDir,
32+
outputFile: baseDir.resolve('output.json'),
33+
outputDirectory: baseDir,
34+
outputDirectoryShared: sharedDir,
35+
)
36+
..config.setupBuild(linkingEnabled: true, dryRun: false)
37+
..config.setupShared(buildAssetTypes: [CodeAsset.type])
38+
..config.setupCode(
39+
targetOS: OS.current,
40+
linkModePreference: LinkModePreference.dynamic,
41+
targetArchitecture: Architecture.current,
42+
);
43+
44+
final buildInput = BuildInput(buildInputBuilder.json);
45+
final buildOutput = BuildOutputBuilder();
46+
47+
{
48+
// Call addLibraries with the provided dynamic library names.
49+
final found = await addFoundCodeAssets(
50+
buildInput,
51+
buildOutput,
52+
outDir: baseDir,
53+
names: {'add': 'add.dart'},
54+
logger: logger,
55+
);
56+
57+
// Validate that one library file was found.
58+
expect(found.length, equals(1));
59+
// The file should reside in a "lib" subdirectory.
60+
expect(found.first.toFilePath(), equals(libFile.absolute.path));
61+
}
62+
63+
{
64+
// Call addLibraries with the provided dynamic library names.
65+
final found = await buildOutput.findAndAddCodeAssets(
66+
buildInput,
67+
outDir: baseDir,
68+
names: {r'(lib)?add\.(dll|so|dylib)': 'add.dart'},
69+
logger: logger,
70+
regExp: true,
71+
);
72+
73+
// Validate that one library file was found.
74+
expect(found.length, equals(1));
75+
// The file should reside in a "lib" subdirectory.
76+
expect(found.first.toFilePath(), equals(libFile.absolute.path));
77+
}
78+
});
79+
});
80+
}

0 commit comments

Comments
 (0)