Skip to content

Commit 903eea6

Browse files
blaugoldCommit Queue
authored and
Commit Queue
committed
[vm,dartdev] Support dynamic linking between libraries
The same bundling that is used for `dart build` is now also used for `dart test` and `dart run`, except that the output directory is `.dart_tool/native_assets`. This way all native code assets are placed next to each other in the `lib` directory, and loaded from there instead of loading them in place from where the build/link hooks placed them. By standardizing on this layout the different modes of running dart code that support native assets can use the same mechanisms to support dynamic linking between libraries. On macOS, install names of dylibs are rewritten to support dynamic linking, similar to the changes in flutter/flutter#153054. On Windows, loading of DLLs is altered so that the directory of the DLL that is being loaded is considered when loading dependent DLLs. Tests are added to verify that dynamic linking works as expected. TEST=pkg/dartdev/test/native_assets/{build,run,test}_test.dart [email protected] Related: dart-lang/native#190 Fixes: #56459 Change-Id: Ie4a41e5b7382ab1cea39e93d29d085bf9986828b Cq-Include-Trybots: luci.dart.try:pkg-linux-debug-try,pkg-linux-release-arm64-try,pkg-linux-release-try,pkg-mac-release-arm64-try,pkg-mac-release-try,pkg-win-release-arm64-try,pkg-win-release-try Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/381580 Reviewed-by: Moritz Sümmermann <[email protected]> Commit-Queue: Daco Harkes <[email protected]> Reviewed-by: Daco Harkes <[email protected]>
1 parent 94b080e commit 903eea6

12 files changed

+491
-183
lines changed

pkg/dartdev/lib/src/commands/build.dart

Lines changed: 15 additions & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import 'dart:io';
88
import 'package:dart2native/generate.dart';
99
import 'package:dartdev/src/commands/compile.dart';
1010
import 'package:dartdev/src/experiments.dart';
11+
import 'package:dartdev/src/native_assets_bundling.dart';
1112
import 'package:dartdev/src/sdk.dart';
1213
import 'package:dartdev/src/utils.dart';
1314
import 'package:front_end/src/api_prototype/compiler_options.dart'
@@ -21,9 +22,6 @@ import 'package:vm/target_os.dart'; // For possible --target-os values.
2122
import '../core.dart';
2223
import '../native_assets.dart';
2324

24-
const _libOutputDirectory = 'lib';
25-
const _dataOutputDirectory = 'assets';
26-
2725
class BuildCommand extends DartdevCommand {
2826
static const String cmdName = 'build';
2927
static const String outputOptionName = 'output';
@@ -240,60 +238,34 @@ class BuildCommand extends DartdevCommand {
240238
return 255;
241239
}
242240

243-
final tempUri = tempDir.uri;
244-
Uri? assetsDartUri;
245241
final allAssets = linkResult.encodedAssets;
246-
final dataAssets = allAssets
247-
.where((e) => e.type == DataAsset.type)
248-
.map(DataAsset.fromEncoded)
249-
.toList();
250-
final codeAssets = allAssets
242+
243+
final staticAssets = allAssets
251244
.where((e) => e.type == CodeAsset.type)
252245
.map(CodeAsset.fromEncoded)
253-
.toList();
254-
255-
final staticAssets =
256-
codeAssets.where((e) => e.linkMode == StaticLinking());
246+
.where((e) => e.linkMode == StaticLinking());
257247
if (staticAssets.isNotEmpty) {
258248
stderr.write(
259249
"""'dart build' does not yet support CodeAssets with static linking.
260250
Use linkMode as dynamic library instead.""");
261251
return 255;
262252
}
263-
if (allAssets.isNotEmpty) {
264-
final kernelAssets = <KernelAsset>[];
265-
final filesToCopy = <(String id, Uri, KernelAssetRelativePath)>[];
266253

267-
for (final asset in codeAssets) {
268-
final kernelAsset = asset.targetLocation(target);
269-
kernelAssets.add(kernelAsset);
270-
final targetPath = kernelAsset.path;
271-
if (targetPath is KernelAssetRelativePath) {
272-
filesToCopy.add((asset.id, asset.file!, targetPath));
273-
}
274-
}
275-
for (final asset in dataAssets) {
276-
final kernelAsset = asset.targetLocation(target);
277-
kernelAssets.add(kernelAsset);
278-
final targetPath = kernelAsset.path;
279-
if (targetPath is KernelAssetRelativePath) {
280-
filesToCopy.add((asset.id, asset.file, targetPath));
281-
}
282-
}
283-
assetsDartUri = await _writeAssetsYaml(
284-
kernelAssets,
285-
assetsDartUri,
286-
tempUri,
254+
Uri? nativeAssetsYamlUri;
255+
if (allAssets.isNotEmpty) {
256+
final kernelAssets = await bundleNativeAssets(
257+
allAssets,
258+
target,
259+
outputUri,
260+
relocatable: true,
261+
verbose: true,
287262
);
288-
if (allAssets.isNotEmpty) {
289-
stdout.writeln(
290-
'Copying ${filesToCopy.length} build assets: ${filesToCopy.map((e) => e.$1)}');
291-
_copyAssets(filesToCopy, outputUri);
292-
}
263+
nativeAssetsYamlUri =
264+
await writeNativeAssetsYaml(kernelAssets, tempDir.uri);
293265
}
294266

295267
await snapshotGenerator.generate(
296-
nativeAssets: assetsDartUri?.toFilePath(),
268+
nativeAssets: nativeAssetsYamlUri?.toFilePath(),
297269
);
298270

299271
// End linking here.
@@ -302,29 +274,6 @@ Use linkMode as dynamic library instead.""");
302274
}
303275
return 0;
304276
}
305-
306-
void _copyAssets(
307-
List<(String id, Uri, KernelAssetRelativePath)> assetTargetLocations,
308-
Uri output,
309-
) {
310-
for (final (_, file, targetPath) in assetTargetLocations) {
311-
file.copyTo(targetPath, output);
312-
}
313-
}
314-
315-
Future<Uri> _writeAssetsYaml(
316-
List<KernelAsset> assetTargetLocations,
317-
Uri? nativeAssetsDartUri,
318-
Uri tempUri,
319-
) async {
320-
stdout.writeln('Writing native_assets.yaml.');
321-
nativeAssetsDartUri = tempUri.resolve('native_assets.yaml');
322-
final assetsContent =
323-
KernelAssets(assetTargetLocations).toNativeAssetsFile();
324-
await Directory.fromUri(nativeAssetsDartUri.resolve('.')).create();
325-
await File(nativeAssetsDartUri.toFilePath()).writeAsString(assetsContent);
326-
return nativeAssetsDartUri;
327-
}
328277
}
329278

330279
extension on String {
@@ -333,57 +282,6 @@ extension on String {
333282
String removeDotDart() => replaceFirst(RegExp(r'\.dart$'), '');
334283
}
335284

336-
extension on Uri {
337-
void copyTo(KernelAssetRelativePath target, Uri outputUri) {
338-
if (this != target.uri) {
339-
final targetUri = outputUri.resolveUri(target.uri);
340-
File.fromUri(targetUri).createSync(
341-
recursive: true,
342-
exclusive: true,
343-
);
344-
File.fromUri(this).copySync(targetUri.toFilePath());
345-
}
346-
}
347-
}
348-
349-
extension on CodeAsset {
350-
KernelAsset targetLocation(Target target) {
351-
final KernelAssetPath kernelAssetPath;
352-
switch (linkMode) {
353-
case DynamicLoadingSystem dynamicLoading:
354-
kernelAssetPath = KernelAssetSystemPath(dynamicLoading.uri);
355-
case LookupInExecutable _:
356-
kernelAssetPath = KernelAssetInExecutable();
357-
case LookupInProcess _:
358-
kernelAssetPath = KernelAssetInProcess();
359-
case DynamicLoadingBundled _:
360-
kernelAssetPath = KernelAssetRelativePath(
361-
Uri(path: path.join(_libOutputDirectory, file!.pathSegments.last)),
362-
);
363-
default:
364-
throw Exception(
365-
'Unsupported CodeAsset linkMode ${linkMode.runtimeType} in asset $this',
366-
);
367-
}
368-
return KernelAsset(
369-
id: id,
370-
target: target,
371-
path: kernelAssetPath,
372-
);
373-
}
374-
}
375-
376-
extension on DataAsset {
377-
KernelAsset targetLocation(Target target) {
378-
return KernelAsset(
379-
id: id,
380-
target: target,
381-
path: KernelAssetRelativePath(
382-
Uri(path: path.join(_dataOutputDirectory, file.pathSegments.last))),
383-
);
384-
}
385-
}
386-
387285
// TODO(https://github.com/dart-lang/package_config/issues/126): Expose this
388286
// logic in package:package_config.
389287
Future<Uri?> packageConfigUri(Uri uri) async {

pkg/dartdev/lib/src/native_assets.dart

Lines changed: 15 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import 'dart:async';
66
import 'dart:io';
77

8+
import 'package:dartdev/src/native_assets_bundling.dart';
89
import 'package:dartdev/src/sdk.dart';
910
import 'package:dartdev/src/utils.dart';
1011
import 'package:logging/logging.dart';
@@ -100,62 +101,24 @@ Future<Uri?> compileNativeAssetsJitYamlFile({
100101
runPackageName: runPackageName,
101102
);
102103
if (assets == null) return null;
103-
final codeAssets = assets
104-
.where((e) => e.type == CodeAsset.type)
105-
.map(CodeAsset.fromEncoded)
106-
.toList();
107-
final dataAssets = assets
108-
.where((e) => e.type == DataAsset.type)
109-
.map(DataAsset.fromEncoded)
110-
.toList();
111-
final kernelAssets = KernelAssets([
112-
...[
113-
for (final asset in codeAssets) _targetLocation(asset),
114-
],
115-
...[
116-
for (final asset in dataAssets) _dataTargetLocation(asset),
117-
]
118-
]);
119104

120-
final workingDirectory = Directory.current.uri;
121-
final assetsUri = workingDirectory.resolve('.dart_tool/native_assets.yaml');
122-
final nativeAssetsYaml = '''# Native assets mapping for host OS in JIT mode.
123-
# Generated by dartdev and package:native_assets_builder.
124-
${kernelAssets.toNativeAssetsFile()}''';
125-
final assetFile = File(assetsUri.toFilePath());
126-
await assetFile.writeAsString(nativeAssetsYaml);
127-
return assetsUri;
128-
}
105+
final dartToolUri = Directory.current.uri.resolve('.dart_tool/');
106+
final outputUri = dartToolUri.resolve('native_assets/');
107+
await Directory.fromUri(outputUri).create(recursive: true);
129108

130-
KernelAsset _targetLocation(CodeAsset asset) {
131-
final linkMode = asset.linkMode;
132-
final KernelAssetPath kernelAssetPath;
133-
switch (linkMode) {
134-
case DynamicLoadingSystem _:
135-
kernelAssetPath = KernelAssetSystemPath(linkMode.uri);
136-
case LookupInExecutable _:
137-
kernelAssetPath = KernelAssetInExecutable();
138-
case LookupInProcess _:
139-
kernelAssetPath = KernelAssetInProcess();
140-
case DynamicLoadingBundled _:
141-
kernelAssetPath = KernelAssetAbsolutePath(asset.file!);
142-
default:
143-
throw Exception(
144-
'Unsupported CodeAsset linkMode ${linkMode.runtimeType} in asset $asset',
145-
);
146-
}
147-
return KernelAsset(
148-
id: asset.id,
149-
target: Target.fromArchitectureAndOS(asset.architecture!, asset.os),
150-
path: kernelAssetPath,
109+
final kernelAssets = await bundleNativeAssets(
110+
assets,
111+
Target.current,
112+
outputUri,
113+
relocatable: false,
151114
);
152-
}
153115

154-
KernelAsset _dataTargetLocation(DataAsset asset) {
155-
return KernelAsset(
156-
id: asset.id,
157-
target: Target.current,
158-
path: KernelAssetAbsolutePath(asset.file),
116+
return await writeNativeAssetsYaml(
117+
kernelAssets,
118+
dartToolUri,
119+
header: '''# Native assets mapping for host OS in JIT mode.
120+
# Generated by dartdev and package:native_assets_builder.
121+
''',
159122
);
160123
}
161124

0 commit comments

Comments
 (0)