Skip to content

Commit

Permalink
[native_assets_builder] Support pub workspaces 2
Browse files Browse the repository at this point in the history
  • Loading branch information
dcharkes committed Jan 22, 2025
1 parent 3849808 commit 549efc9
Show file tree
Hide file tree
Showing 11 changed files with 191 additions and 122 deletions.
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
38 changes: 29 additions & 9 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 @@ -713,7 +724,7 @@ ${compileResult.stdout}

if (input is BuildInput) {
final packagesWithLink =
(await packageLayout.packagesWithAssets(Hook.link))
(await (await _planner).packagesWithHook(Hook.link))
.map((p) => p.name);
for (final targetPackage
in (output as BuildOutput).assets.encodedAssetsForLinking.keys) {
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

0 comments on commit 549efc9

Please sign in to comment.