Skip to content

[native_assets_builder] Support pub workspaces 2 #1921

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jan 22, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions pkgs/native_assets_builder/lib/native_assets_builder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@ export 'package:native_assets_builder/src/build_runner/build_runner.dart'
LinkInputValidator,
LinkValidator,
NativeAssetsBuildRunner;
export 'package:native_assets_builder/src/model/build_result.dart';
export 'package:native_assets_builder/src/model/build_result.dart'
show BuildResult;
export 'package:native_assets_builder/src/model/kernel_assets.dart';
export 'package:native_assets_builder/src/model/link_result.dart';
export 'package:native_assets_builder/src/package_layout/package_layout.dart';
export 'package:native_assets_builder/src/model/link_result.dart'
show LinkResult;
export 'package:native_assets_builder/src/package_layout/package_layout.dart'
show PackageLayout;
80 changes: 68 additions & 12 deletions pkgs/native_assets_builder/lib/src/build_runner/build_planner.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,37 @@
import 'dart:convert';
import 'dart:io' show Process;

import 'package:file/file.dart';
import 'package:graphs/graphs.dart' as graphs;
import 'package:logging/logging.dart';
import 'package:meta/meta.dart';
import 'package:native_assets_cli/native_assets_cli_internal.dart';
import 'package:package_config/package_config.dart';

import '../package_layout/package_layout.dart';

@internal
class NativeAssetsBuildPlanner {
final PackageGraph packageGraph;
final List<Package> packagesWithNativeAssets;
final Uri dartExecutable;
final Logger logger;
final PackageLayout packageLayout;
final FileSystem fileSystem;

NativeAssetsBuildPlanner({
NativeAssetsBuildPlanner._({
required this.packageGraph,
required this.packagesWithNativeAssets,
required this.dartExecutable,
required this.logger,
required this.packageLayout,
required this.fileSystem,
});

static Future<NativeAssetsBuildPlanner> fromPackageConfigUri({
required Uri packageConfigUri,
required List<Package> packagesWithNativeAssets,
required Uri dartExecutable,
required Logger logger,
required PackageLayout packageLayout,
required FileSystem fileSystem,
}) async {
final workingDirectory = packageConfigUri.resolve('../');
final result = await Process.run(
Expand All @@ -40,21 +49,67 @@ class NativeAssetsBuildPlanner {
);
final packageGraph =
PackageGraph.fromPubDepsJsonString(result.stdout as String);
return NativeAssetsBuildPlanner(
packageGraph: packageGraph,
packagesWithNativeAssets: packagesWithNativeAssets,
final packageGraphFromRunPackage =
packageGraph.subGraph(packageLayout.runPackageName);
return NativeAssetsBuildPlanner._(
packageGraph: packageGraphFromRunPackage,
dartExecutable: dartExecutable,
logger: logger,
packageLayout: packageLayout,
fileSystem: fileSystem,
);
}

/// All packages in [PackageLayout.packageConfig] with native assets.
///
/// Whether a package has native assets is defined by whether it contains
/// a `hook/build.dart` or `hook/link.dart`.
///
/// For backwards compatibility, a toplevel `build.dart` is also supported.
// TODO(https://github.com/dart-lang/native/issues/823): Remove fallback when
// everyone has migrated. (Probably once we stop backwards compatibility of
// the protocol version pre 1.2.0 on some future version.)
Future<List<Package>> packagesWithHook(Hook hook) async => switch (hook) {
Hook.build => _packagesWithBuildHook ??=
await _runPackagesWithHook(hook),
Hook.link => _packagesWithLinkHook ??= await _runPackagesWithHook(hook),
};

List<Package>? _packagesWithBuildHook;
List<Package>? _packagesWithLinkHook;

Future<List<Package>> _runPackagesWithHook(Hook hook) async {
final packageNamesInDependencies = packageGraph.vertices.toSet();
final result = <Package>[];
for (final package in packageLayout.packageConfig.packages) {
if (!packageNamesInDependencies.contains(package.name)) {
continue;
}
final packageRoot = package.root;
if (packageRoot.scheme == 'file') {
if (await fileSystem
.file(packageRoot.resolve('hook/').resolve(hook.scriptName))
.exists() ||
await fileSystem
.file(packageRoot.resolve(hook.scriptName))
.exists()) {
result.add(package);
}
}
}
return result;
}

List<Package>? _buildHookPlan;

/// Plans in what order to run build hooks.
///
/// [runPackageName] provides the entry-point in the graph. The hooks of
/// packages not in the transitive dependencies of [runPackageName] will not
/// be run.
List<Package>? plan(String runPackageName) {
final packageGraph = this.packageGraph.subGraph(runPackageName);
/// [PackageLayout.runPackageName] provides the entry-point in the graph. The
/// hooks of packages not in the transitive dependencies of
/// [PackageLayout.runPackageName] will not be run.
Future<List<Package>?> makeBuildHookPlan() async {
if (_buildHookPlan != null) return _buildHookPlan;
final packagesWithNativeAssets = await packagesWithHook(Hook.build);
final packageMap = {
for (final package in packagesWithNativeAssets) package.name: package
};
Expand All @@ -77,6 +132,7 @@ class NativeAssetsBuildPlanner {
packageMap[stronglyConnectedComponentWithNativeAssets.single]!);
}
}
_buildHookPlan = result;
return result;
}
}
Expand Down
40 changes: 30 additions & 10 deletions pkgs/native_assets_builder/lib/src/build_runner/build_runner.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import 'dart:io' show Platform;

import 'package:file/file.dart';
import 'package:logging/logging.dart';
import 'package:meta/meta.dart';
import 'package:native_assets_cli/native_assets_cli_internal.dart';
import 'package:package_config/package_config.dart';

Expand Down Expand Up @@ -82,6 +83,16 @@ class NativeAssetsBuildRunner {
hookEnvironment = hookEnvironment ??
filteredEnvironment(hookEnvironmentVariablesFilter);

/// Checks whether any hooks need to be run.
///
/// This method is invoked by launchers such as dartdev (for `dart run`) and
/// flutter_tools (for `flutter run` and `flutter build`).
Future<bool> hasBuildHooks() async {
final planner = await _planner;
final packagesWithHook = await planner.packagesWithHook(Hook.build);
return packagesWithHook.isNotEmpty;
}

/// This method is invoked by launchers such as dartdev (for `dart run`) and
/// flutter_tools (for `flutter run` and `flutter build`).
///
Expand Down Expand Up @@ -712,9 +723,9 @@ ${compileResult.stdout}
errors.addAll(await validator(input, output));

if (input is BuildInput) {
final planner = await _planner;
final packagesWithLink =
(await packageLayout.packagesWithAssets(Hook.link))
.map((p) => p.name);
(await planner.packagesWithHook(Hook.link)).map((p) => p.name);
for (final targetPackage
in (output as BuildOutput).assets.encodedAssetsForLinking.keys) {
if (!packagesWithLink.contains(targetPackage)) {
Expand All @@ -731,30 +742,37 @@ ${compileResult.stdout}
return errors;
}

late final _planner = () async {
final planner = await NativeAssetsBuildPlanner.fromPackageConfigUri(
packageConfigUri: packageLayout.packageConfigUri,
dartExecutable: Uri.file(Platform.resolvedExecutable),
logger: logger,
packageLayout: packageLayout,
fileSystem: _fileSystem,
);
return planner;
}();

Future<(List<Package>? plan, PackageGraph? dependencyGraph)> _makePlan({
required Hook hook,
// TODO(dacoharkes): How to share these two? Make them extend each other?
BuildResult? buildResult,
}) async {
final packagesWithHook = await packageLayout.packagesWithAssets(hook);
final List<Package> buildPlan;
final PackageGraph? packageGraph;
switch (hook) {
case Hook.build:
final planner = await NativeAssetsBuildPlanner.fromPackageConfigUri(
packageConfigUri: packageLayout.packageConfigUri,
packagesWithNativeAssets: packagesWithHook,
dartExecutable: Uri.file(Platform.resolvedExecutable),
logger: logger,
);
final plan = planner.plan(packageLayout.runPackageName);
final planner = await _planner;
final plan = await planner.makeBuildHookPlan();
return (plan, planner.packageGraph);
case Hook.link:
// Link hooks are not run in any particular order.
// Link hooks are skipped if no assets for linking are provided.
buildPlan = [];
final skipped = <String>[];
final encodedAssetsForLinking = buildResult!.encodedAssetsForLinking;
final planner = await _planner;
final packagesWithHook = await planner.packagesWithHook(Hook.link);
for (final package in packagesWithHook) {
if (encodedAssetsForLinking[package.name]?.isNotEmpty ?? false) {
buildPlan.add(package);
Expand Down Expand Up @@ -802,6 +820,7 @@ ${compileResult.stdout}
/// return path.replaceAll('\\', '\\\\').replaceAll(' ', '\\ ');
/// }
/// ```
@internal
List<String> parseDepFileInputs(String contents) {
final output = contents.substring(0, contents.indexOf(': '));
contents = contents.substring(output.length + ': '.length).trim();
Expand Down Expand Up @@ -844,6 +863,7 @@ Future<List<Uri>> _readDepFile(File depFile) async {
return dartSources.map(Uri.file).toList();
}

@internal
Map<String, String> filteredEnvironment(Set<String> allowList) => {
for (final entry in Platform.environment.entries)
if (allowList.contains(entry.key.toUpperCase())) entry.key: entry.value,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,25 @@
// BSD-style license that can be found in the LICENSE file.

import 'package:file/file.dart';
import 'package:native_assets_cli/native_assets_cli_internal.dart';
import 'package:package_config/package_config.dart';

import '../../native_assets_builder.dart';

/// Directory layout for dealing with native assets.
///
/// Build hooks for native assets will be run from the context of another root
/// package.
/// For the [NativeAssetsBuildRunner] to correctly run hooks, multiple pieces of
/// information are required:
/// * [packageConfig] to know the list of all packages that may contain hooks.
/// * [packageConfigUri] to be able to get a dependency graph with `pub` and to
/// know where to cache/share asset builds.
/// * [runPackageName] to know which package build hooks to invoke and ignore.
/// Only dependencies of the "run package" are built.
///
/// The directory layout follows pub's convention for caching:
/// The [NativeAssetsBuildRunner] builds assets in
/// `.dart_tool/native_assets_builder`. The directory layout follows pub's
/// convention for caching:
/// https://dart.dev/tools/pub/package-layout#project-specific-caching-for-tools
class PackageLayout {
final FileSystem _fileSystem;

/// Package config containing the information of where to foot the root [Uri]s
/// of other packages.
///
Expand All @@ -29,7 +35,6 @@ class PackageLayout {
final String runPackageName;

PackageLayout._(
this._fileSystem,
this.packageConfig,
this.packageConfigUri,
this.runPackageName,
Expand All @@ -44,7 +49,6 @@ class PackageLayout {
assert(fileSystem.file(packageConfigUri).existsSync());
packageConfigUri = packageConfigUri.normalizePath();
return PackageLayout._(
fileSystem,
packageConfig,
packageConfigUri,
runPackageName,
Expand All @@ -62,7 +66,6 @@ class PackageLayout {
assert(await fileSystem.file(packageConfigUri).exists());
final packageConfig = await loadPackageConfigUri(packageConfigUri!);
return PackageLayout._(
fileSystem,
packageConfig,
packageConfigUri,
runPackgeName,
Expand Down Expand Up @@ -121,40 +124,4 @@ class PackageLayout {
}
return package.root;
}

/// All packages in [packageConfig] with native assets.
///
/// Whether a package has native assets is defined by whether it contains
/// a `hook/build.dart` or `hook/link.dart`.
///
/// For backwards compatibility, a toplevel `build.dart` is also supported.
// TODO(https://github.com/dart-lang/native/issues/823): Remove fallback when
// everyone has migrated. (Probably once we stop backwards compatibility of
// the protocol version pre 1.2.0 on some future version.)
Future<List<Package>> packagesWithAssets(Hook hook) async => switch (hook) {
Hook.build => _packagesWithBuildAssets ??=
await _packagesWithHook(hook),
Hook.link => _packagesWithLinkAssets ??= await _packagesWithHook(hook),
};

List<Package>? _packagesWithBuildAssets;
List<Package>? _packagesWithLinkAssets;

Future<List<Package>> _packagesWithHook(Hook hook) async {
final result = <Package>[];
for (final package in packageConfig.packages) {
final packageRoot = package.root;
if (packageRoot.scheme == 'file') {
if (await _fileSystem
.file(packageRoot.resolve('hook/').resolve(hook.scriptName))
.exists() ||
await _fileSystem
.file(packageRoot.resolve(hook.scriptName))
.exists()) {
result.add(package);
}
}
}
return result;
}
}
1 change: 1 addition & 0 deletions pkgs/native_assets_builder/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ dependencies:
file: ^7.0.1
graphs: ^2.3.1
logging: ^1.2.0
meta: ^1.16.0
# native_assets_cli: ^0.10.0
native_assets_cli:
path: ../native_assets_cli/
Expand Down
Loading
Loading