Skip to content

[pigeon] Support running as a standalone AOT executable - #13253

Open
sigurdm wants to merge 4 commits into
flutter:mainfrom
sigurdm:pigeon-aot
Open

sigurdm wants to merge 4 commits into
flutter:mainfrom
sigurdm:pigeon-aot

Conversation

@sigurdm

@sigurdm sigurdm commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

Declares pigeon under executables in pubspec.yaml and resolves the Dart SDK and dart executable via package:cli_util (cli_util: ^0.6.0).

Previously, pigeon could not be compiled to and run as a standalone AOT executable (such as via dart install pigeon or dart run pigeon@) because:

  • Pigeon.parseFile relied on AnalysisContextCollection's default sdkPath discovery (FolderBasedDartSdk.defaultSdkDirectory, which assumes Platform.resolvedExecutable is <sdk>/bin/dart rather than the compiled pigeon binary), and runWithOptions used Platform.resolvedExecutable directly when invoking dart run for FFIgen/JNIgen.
  • Removing the remaining unused dart:mirrors imports ( also required for AOT compilation) has been split out into [pigeon] Remove remaining dart:mirrors usage #13524 (fixing [pigeon] Remove remaining dart:mirrors usage flutter#190228).

Part of dart-lang/sdk#63210

Pre-Review Checklist

Footnotes

  1. Regular contributors who have demonstrated familiarity with the repository guidelines only need to comment if the PR is not auto-exempted by repo tooling. ↩ ↩2

Declare pigeon under executables in pubspec.yaml, remove unused dart:mirrors imports, and resolve the Dart SDK and executable via package:cli_util.
@flutter-dashboard flutter-dashboard Bot added the CICD Run CI/CD label Oct 14, 2026

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request updates the pigeon package to support running as a standalone AOT-compiled executable. It removes unused dart:mirrors imports, registers pigeon under executables in pubspec.yaml, adds a dependency on cli_util, and resolves the Dart SDK path using cli_util.sdkPath. A test is also added to verify SDK resolution. Feedback points out that cli_util does not expose a dartExecutable property, which will cause a compilation error, and suggests constructing the path manually using cli_util.sdkPath.

Comment thread packages/pigeon/lib/src/pigeon_lib.dart Outdated
final bool useJni = internalOptions.kotlinOptions?.useJni ?? false;

final String dartExecutable = Platform.resolvedExecutable;
final String dartExecutable = cli_util.dartExecutable ?? Platform.resolvedExecutable;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

The cli_util package does not expose a dartExecutable property or getter. Accessing cli_util.dartExecutable will result in a compilation error. Instead, you should construct the path to the dart executable using cli_util.sdkPath and path.join.

    final String dartExecutable = path.join(cli_util.sdkPath, 'bin', Platform.isWindows ? 'dart.exe' : 'dart');

@Piinks
Piinks requested a review from tarrinneal October 20, 2026 20:59
@davidmorgan

Copy link
Copy Markdown

@tarrinneal any chance you could take a look at this one please? I'm looking at removing dart:mirror use in google3 and this would, uh, do some of that :)

dependencies:
analyzer: ">=13.0.0 <15.0.0"
args: ^2.5.0
cli_util: ">=0.4.2 <0.7.0"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cli_util only added AOT SDK discovery (and nullable String? get sdkPath) in 0.6.0; in 0.4.2–0.5.2, cli_util.sdkPath unconditionally returns non-null path.dirname(path.dirname(Platform.resolvedExecutable)). Because dev_dependencies here has swiftgen: 0.2.0 -> ffigen: ^22.0.0 -> cli_util: ^0.4.2, packages/pigeon (and any project using swiftgen: ^0.2.0) resolves cli_util to 0.4.2:

  1. Under <0.6.0, AOT SDK lookup doesn't happen, and sdkPath ?? cli_util.sdkPath in parseFile is never null—overriding analyzer's getSdkPath() candidate search (such as third_party/dart_lang/v2/sdk in google3 runfiles and <exeDir>/dart-sdk) with an unvalidated parent directory.
  2. Also note the PR description still mentions cli_util: ^0.6.0.

If >=0.4.2 <0.7.0 is needed until swiftgen supports ffigen >=23.0.0, could pigeon validate that cli_util.sdkPath actually contains SDK markers before passing it to AnalysisContextCollection (or use parseString(..., throwIfDiagnostics: false) from package:analyzer/dart/analysis/utilities.dart when sdkPath == null, since parseFile only needs syntactic parsing and wouldn't require a host Dart SDK at all)?

Comment on lines +803 to +808
static String _resolveDartExecutable(String? sdkPath) {
if (sdkPath != null) {
return path.join(sdkPath, 'bin', Platform.isWindows ? 'dart.exe' : 'dart');
}
return Platform.resolvedExecutable;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two issues with resolving dartExecutable for FFIgen/JNIgen:

  1. Line 903 passes cli_util.sdkPath instead of sdkPath ?? cli_util.sdkPath, ignoring the sdkPath parameter passed into runWithOptions.
  2. When pigeon is compiled to a standalone AOT binary, Platform.resolvedExecutable is the pigeon executable itself (not dart). If sdkPath is null (e.g. cli_util.sdkPath can't find an SDK), falling back to Platform.resolvedExecutable on line 807 causes _runFfigen / _runJnigen to run pigeon run <configFile> instead of dart run <configFile>—which prints usage and exits 0 (L851), silently skipping binding generation.

If sdkPath is null and Platform.resolvedExecutable isn't dart, should this fail with a clear error (or fall back to 'dart' on PATH) instead?

Comment on lines +3341 to +3359
test('parseFile locates Dart SDK via cli_util', () {
String requireSdkPath(String? sdkPath) => sdkPath!;
final String currentSdk = requireSdkPath(cli_util.sdkPath);
final ParseResults parseResult = runZoned(
() => parseSource('class Message { String? text; }'),
zoneValues: <Object, Object>{
#environmentOverrides: <String, String>{
'_DART_RESOLVED_EXECUTABLE': '/nonexistent/bin/pigeon',
'DART_SDK': currentSdk,
},
#_environmentOverrides: <String, String>{
'_DART_RESOLVED_EXECUTABLE': '/nonexistent/bin/pigeon',
'DART_SDK': currentSdk,
},
},
);
expect(parseResult.errors, isEmpty);
expect(parseResult.root.classes.single.name, 'Message');
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test passes even if 'DART_SDK' is '/nonexistent' or sdkPath ?? cli_util.sdkPath is reverted:

  1. In Dart, #_environmentOverrides is a library-private Symbol scoped to pigeon_lib_test.dart, so it doesn't match cli_util's #_environmentOverrides (cli_util.environmentOverridesKey in 0.6.0)—under 0.6.0, Zone.current[environmentOverridesKey] is null and cli_util.sdkPath returns _cachedSdkPath from line 3343.
  2. In packages/pigeon, cli_util currently resolves to 0.4.2 (via ffigen: 22.0.0), where cli_util.sdkPath ignores Zone.current altogether and reads the JIT test runner's Platform.resolvedExecutable.

Comment on lines +1 to +5
## 29.1.1

* Adds `pigeon` to `executables` in `pubspec.yaml` and resolves the Dart SDK and
executable via `package:cli_util` so `pigeon` can run as a standalone
AOT-compiled executable (such as via `dart install` or `dart run pigeon@`).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since lib/src/pigeon_lib.dart (_executeConfigurePigeon) and lib/src/generator_tools.dart still import dart:mirrors until #13524 lands, dart install pigeon / dart run pigeon@ will fail to AOT-compile if this merges and auto-publishes first. Can we land #13524 first and rebase this PR onto main (now at 30.0.2, which also already added cli_util to .repo_tool_config.yaml)?

Comment on lines +495 to +498
final collection = AnalysisContextCollection(
includedPaths: includedPaths,
sdkPath: sdkPath ?? cli_util.sdkPath,
);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If parseFile keeps AnalysisContextCollection, note that AnalysisContextCollection throws ArgumentError when sdkPath isn't normalized (path.normalize(p) == p), and cli_util 0.6.0 calls path.absolute without path.normalize—so a trailing slash like DART_SDK=/path/to/dart-sdk/ will throw unless normalized with path.normalize(path.absolute(...)).

Comment on lines 31 to 32
flutter:
uses-material-design: true

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Please revert the unrelated trailing-newline changes here and in platform_tests/test_plugin/example/pubspec.yaml.

Comment on lines +5 to +11
version: 29.1.1 # This must match the version in lib/src/generator_tools.dart

environment:
sdk: ^3.11.0

executables:
pigeon:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since adding pigeon under executables: exposes a new CLI entrypoint for dart install / dart pub global activate, should this be a minor bump (30.1.0 once rebased onto main) rather than a patch bump?

Comment on lines 492 to 494
/// [AnalysisContextCollection].
ParseResults parseFile(String inputPath, {String? sdkPath}) {
final includedPaths = <String>[path.absolute(path.normalize(inputPath))];

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: While touching parseFile, could we clean up its doc comment just above (L489-L492—[path] -> [inputPath] and drop the nonexistent [types] reference)?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants