diff --git a/pkgs/native_assets_builder/CHANGELOG.md b/pkgs/native_assets_builder/CHANGELOG.md index 5700d5d27..dec17b8f4 100644 --- a/pkgs/native_assets_builder/CHANGELOG.md +++ b/pkgs/native_assets_builder/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.3.1-wip + +- Add support for `runPackageName` to avoid native assets for packages that + the root package depends on but the package being run doesn't. + ## 0.3.0 - Bump `package:native_assets_cli` to 0.3.0 diff --git a/pkgs/native_assets_builder/lib/src/build_runner/build_planner.dart b/pkgs/native_assets_builder/lib/src/build_runner/build_planner.dart index 24c533a84..81d050ddb 100644 --- a/pkgs/native_assets_builder/lib/src/build_runner/build_planner.dart +++ b/pkgs/native_assets_builder/lib/src/build_runner/build_planner.dart @@ -47,7 +47,15 @@ class NativeAssetsBuildPlanner { ); } - (List packages, bool success) plan() { + (List packages, bool success) plan({ + String? runPackageName, + }) { + final PackageGraph packageGraph; + if (runPackageName != null) { + packageGraph = this.packageGraph.subGraph(runPackageName); + } else { + packageGraph = this.packageGraph; + } final packageMap = { for (final package in packagesWithNativeAssets) package.name: package }; @@ -105,4 +113,19 @@ class PackageGraph { List> computeStrongComponents() => graphs.stronglyConnectedComponents(vertices, neighborsOf); + + PackageGraph subGraph(String rootPackageName) { + final subgraphVertices = [ + ...graphs.transitiveClosure(vertices, neighborsOf)[rootPackageName]!, + rootPackageName, + ]; + return PackageGraph({ + for (final vertex in map.keys) + if (subgraphVertices.contains(vertex)) + vertex: [ + for (final neighbor in map[vertex]!) + if (subgraphVertices.contains(neighbor)) neighbor, + ] + }); + } } diff --git a/pkgs/native_assets_builder/lib/src/build_runner/build_runner.dart b/pkgs/native_assets_builder/lib/src/build_runner/build_runner.dart index 579855c94..7dec85012 100644 --- a/pkgs/native_assets_builder/lib/src/build_runner/build_runner.dart +++ b/pkgs/native_assets_builder/lib/src/build_runner/build_runner.dart @@ -33,7 +33,8 @@ class NativeAssetsBuildRunner { /// This method is invoked by launchers such as dartdev (for `dart run`) and /// flutter_tools (for `flutter run` and `flutter build`). /// - /// Completes the future with an error if the build fails. + /// If provided, only native assets of all transitive dependencies of + /// [runPackageName] are built. Future build({ required LinkModePreference linkModePreference, required Target target, @@ -44,13 +45,14 @@ class NativeAssetsBuildRunner { int? targetAndroidNdkApi, required bool includeParentEnvironment, PackageLayout? packageLayout, + String? runPackageName, }) async { packageLayout ??= await PackageLayout.fromRootPackageRoot(workingDirectory); final packagesWithNativeAssets = await packageLayout.packagesWithNativeAssets; final List buildPlan; final PackageGraph packageGraph; - if (packagesWithNativeAssets.length <= 1) { + if (packagesWithNativeAssets.length <= 1 && runPackageName == null) { buildPlan = packagesWithNativeAssets; packageGraph = PackageGraph({ for (final p in packagesWithNativeAssets) p.name: [], @@ -62,7 +64,9 @@ class NativeAssetsBuildRunner { dartExecutable: Uri.file(Platform.resolvedExecutable), logger: logger, ); - final (plan, planSuccess) = planner.plan(); + final (plan, planSuccess) = planner.plan( + runPackageName: runPackageName, + ); if (!planSuccess) { return _BuildResultImpl( assets: [], @@ -125,19 +129,21 @@ class NativeAssetsBuildRunner { /// This method is invoked by launchers such as dartdev (for `dart run`) and /// flutter_tools (for `flutter run` and `flutter build`). /// - /// Completes the future with an error if the build fails. + /// If provided, only native assets of all transitive dependencies of + /// [runPackageName] are built. Future dryRun({ required LinkModePreference linkModePreference, required OS targetOs, required Uri workingDirectory, required bool includeParentEnvironment, PackageLayout? packageLayout, + String? runPackageName, }) async { packageLayout ??= await PackageLayout.fromRootPackageRoot(workingDirectory); final packagesWithNativeAssets = await packageLayout.packagesWithNativeAssets; final List buildPlan; - if (packagesWithNativeAssets.length <= 1) { + if (packagesWithNativeAssets.length <= 1 && runPackageName == null) { buildPlan = packagesWithNativeAssets; } else { final planner = await NativeAssetsBuildPlanner.fromRootPackageRoot( @@ -146,7 +152,9 @@ class NativeAssetsBuildRunner { dartExecutable: Uri.file(Platform.resolvedExecutable), logger: logger, ); - final (plan, planSuccess) = planner.plan(); + final (plan, planSuccess) = planner.plan( + runPackageName: runPackageName, + ); if (!planSuccess) { return _DryRunResultImpl( assets: [], diff --git a/pkgs/native_assets_builder/pubspec.yaml b/pkgs/native_assets_builder/pubspec.yaml index af8c48e30..5a48564f2 100644 --- a/pkgs/native_assets_builder/pubspec.yaml +++ b/pkgs/native_assets_builder/pubspec.yaml @@ -1,7 +1,7 @@ name: native_assets_builder description: >- This package is the backend that invokes top-level `build.dart` scripts. -version: 0.3.0 +version: 0.3.1-wip repository: https://github.com/dart-lang/native/tree/main/pkgs/native_assets_builder environment: diff --git a/pkgs/native_assets_builder/test/build_runner/build_planner_test.dart b/pkgs/native_assets_builder/test/build_runner/build_planner_test.dart index 48daae368..37af2a37d 100644 --- a/pkgs/native_assets_builder/test/build_runner/build_planner_test.dart +++ b/pkgs/native_assets_builder/test/build_runner/build_planner_test.dart @@ -50,6 +50,7 @@ void main() async { expect(buildPlan.single.name, 'native_add'); }); }); + test('build dependency graph fromPackageRoot', () async { await inTempDir((tempUri) async { await copyTestProjects(targetUri: tempUri); @@ -74,4 +75,30 @@ void main() async { expect(buildPlan.single.name, 'native_add'); }); }); + + test('runPackageName', () async { + await inTempDir((tempUri) async { + await copyTestProjects(targetUri: tempUri); + final nativeAddUri = tempUri.resolve('native_add/'); + + // First, run `pub get`, we need pub to resolve our dependencies. + await runPubGet(workingDirectory: nativeAddUri, logger: logger); + + final packageLayout = + await PackageLayout.fromRootPackageRoot(nativeAddUri); + final packagesWithNativeAssets = + await packageLayout.packagesWithNativeAssets; + final nativeAssetsBuildPlanner = + await NativeAssetsBuildPlanner.fromRootPackageRoot( + rootPackageRoot: nativeAddUri, + packagesWithNativeAssets: packagesWithNativeAssets, + dartExecutable: Uri.file(Platform.resolvedExecutable), + logger: logger, + ); + final (buildPlan, _) = nativeAssetsBuildPlanner.plan( + runPackageName: 'ffigen', + ); + expect(buildPlan.length, 0); + }); + }); } diff --git a/pkgs/native_assets_builder/test/build_runner/build_runner_non_root_package_test.dart b/pkgs/native_assets_builder/test/build_runner/build_runner_non_root_package_test.dart new file mode 100644 index 000000000..80f6c8d37 --- /dev/null +++ b/pkgs/native_assets_builder/test/build_runner/build_runner_non_root_package_test.dart @@ -0,0 +1,62 @@ +// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'dart:io'; + +import 'package:test/test.dart'; + +import '../helpers.dart'; +import 'helpers.dart'; + +const Timeout longTimeout = Timeout(Duration(minutes: 5)); + +void main() async { + test('run ffigen first', timeout: longTimeout, () async { + await inTempDir((tempUri) async { + await copyTestProjects(targetUri: tempUri); + final packageUri = tempUri.resolve('native_add/'); + + await runPubGet( + workingDirectory: packageUri, + logger: logger, + ); + + { + final logMessages = []; + final result = await build( + packageUri, + logger, + dartExecutable, + capturedLogs: logMessages, + runPackageName: 'ffigen', + ); + expect(result.assets, isEmpty); + expect(result.dependencies, isEmpty); + } + + { + final logMessages = []; + final result = await build( + packageUri, + logger, + dartExecutable, + capturedLogs: logMessages, + runPackageName: 'native_add', + ); + expect(result.assets, isNotEmpty); + expect( + result.dependencies, + [ + packageUri.resolve('build.dart'), + packageUri.resolve('src/native_add.c'), + ], + ); + expect( + logMessages.join('\n'), + contains('native_add${Platform.pathSeparator}build.dart'), + ); + } + }); + }); +} diff --git a/pkgs/native_assets_builder/test/build_runner/helpers.dart b/pkgs/native_assets_builder/test/build_runner/helpers.dart index 9609c1c55..82f37da02 100644 --- a/pkgs/native_assets_builder/test/build_runner/helpers.dart +++ b/pkgs/native_assets_builder/test/build_runner/helpers.dart @@ -39,6 +39,7 @@ Future build( bool includeParentEnvironment = true, List? capturedLogs, PackageLayout? packageLayout, + String? runPackageName, }) async { StreamSubscription? subscription; if (capturedLogs != null) { @@ -57,6 +58,7 @@ Future build( cCompilerConfig: cCompilerConfig, includeParentEnvironment: includeParentEnvironment, packageLayout: packageLayout, + runPackageName: runPackageName, ); if (result.success) { await expectAssetsExist(result.assets);