Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
53b817e
Dart transpiler: throw expressions, async*/await for, mixin applicati…
shai-almog Oct 19, 2026
c04a59a
Dart transpiler: generator and generic closures, redirecting factorie…
shai-almog Oct 19, 2026
a04c182
Dart transpiler: dart:convert, dynamic dispatch, arrow factories, mix…
shai-almog Oct 19, 2026
d570fe5
Dart runtime: dart:io subset and fuller Streams
shai-almog Oct 19, 2026
d9e6909
Flutter: plugin stub packs, pub package import, and a pet-adoption ac…
shai-almog Oct 19, 2026
f0322cc
Flutter: plugin stub packs, PopScope/StreamBuilder, and an acceptance…
shai-almog Oct 19, 2026
281d800
Flutter: address review findings and fix the flutter-runtime test com…
shai-almog Oct 20, 2026
6091c28
Flutter: address second-round review findings
shai-almog Oct 20, 2026
3a717df
Flutter: address third-round review findings
shai-almog Oct 20, 2026
f78e349
Flutter: fix native benchmark builds, push opt-in and an iterator race
shai-almog Oct 20, 2026
f848db1
Flutter: fourth-round review fixes, a 20% native size regression, and…
shai-almog Oct 20, 2026
9ffb01f
Merge remote-tracking branch 'origin/master' into flutter-language-co…
shai-almog Oct 20, 2026
a54e3b9
Flutter: fifth-round review fixes and the Linux size baseline
shai-almog Oct 20, 2026
406d722
Flutter: sixth-round review fixes
shai-almog Oct 20, 2026
1288ada
Flutter: seventh-round review fixes
shai-almog Oct 21, 2026
4e8dd3b
Flutter: type WidgetsBinding.platformDispatcher so the native gallery…
shai-almog Oct 21, 2026
633b07a
Flutter: eighth-round review fixes
shai-almog Oct 21, 2026
29c36b2
Merge remote-tracking branch 'origin/master' into flutter-language-co…
shai-almog Oct 21, 2026
1f5d6c0
Flutter: ninth-round review fixes
shai-almog Oct 21, 2026
c6f3112
Merge remote-tracking branch 'origin/master' into flutter-language-co…
shai-almog Oct 21, 2026
a582b00
Flutter: tenth-round review fixes
shai-almog Oct 21, 2026
91ff218
Android bench: prune unused Flutter plugin packs from the build
shai-almog Oct 21, 2026
03a5de8
Flutter: eleventh-round review fixes
shai-almog Oct 21, 2026
6b69e0e
Merge remote-tracking branch 'origin/master' into flutter-language-co…
shai-almog Oct 21, 2026
eda2b41
Android bench: prune Flutter plugin packs in the Android builder too
shai-almog Oct 21, 2026
f608da0
Flutter bench: re-base #5976's JavaScript rebaseline on the folded row
shai-almog Oct 21, 2026
b69b91a
Flutter bench: report an unreadable baseline without a KeyError
shai-almog Oct 21, 2026
126f01d
Firebase: keep push out of auth-only apps; users act through their ow…
shai-almog Oct 21, 2026
a7bd966
Merge remote-tracking branch 'origin/master' into flutter-language-co…
shai-almog Oct 22, 2026
3f711ab
Baselines: drop #5976's stale ParparVM perf overlay, as #6059 does
shai-almog Oct 22, 2026
4a74ae6
Flutter: adversarial-review fixes
shai-almog Oct 22, 2026
a1de154
Flutter: adversarial round 2 fixes
shai-almog Oct 22, 2026
86eaec1
Flutter: adversarial round 3 fixes
shai-almog Oct 22, 2026
fdd52f5
Flutter: adversarial round 4 fixes
shai-almog Oct 22, 2026
cf627bd
Merge origin/master into flutter-language-completeness
shai-almog Oct 22, 2026
787c73a
Flutter: select plugin packs at transpile time, not in the builders
shai-almog Oct 22, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
82 changes: 82 additions & 0 deletions .github/workflows/flutter-acceptance.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
name: Flutter app acceptance

# Imports, transpiles and compiles the "ordinary" Flutter apps under
# scripts/flutter-apps with no edits (scripts/flutter-apps/run-acceptance.sh).
# The unit and behavior tests (dart-flutter-tests.yml) check constructs one at a
# time; this checks that a whole app built from pub packages and plugins gets
# through the importer, the transpiler and javac together.
#
# `flutter pub get` is real: the importer transpiles pure-Dart dependencies
# (dio, ...) from the pub cache, so the Flutter SDK is needed for resolution,
# never to build anything.

on:
pull_request:
paths:
- 'maven/dart-transpiler/**'
- 'maven/dart-runtime/**'
- 'maven/flutter-runtime/**'
- 'maven/build-engine/src/main/java/com/codename1/maven/FlutterProjectImporter.java'
- 'scripts/flutter-apps/**'
- '.github/workflows/flutter-acceptance.yml'
push:
branches:
- master
paths:
- 'maven/dart-transpiler/**'
- 'maven/dart-runtime/**'
- 'maven/flutter-runtime/**'
- 'maven/build-engine/src/main/java/com/codename1/maven/FlutterProjectImporter.java'
- 'scripts/flutter-apps/**'
- '.github/workflows/flutter-acceptance.yml'
workflow_dispatch:

permissions:
contents: read

concurrency:
group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

jobs:
acceptance:
runs-on: ubuntu-24.04
timeout-minutes: 45
env:
LANG: C.UTF-8
LC_ALL: C.UTF-8
steps:
- uses: actions/checkout@v6

- uses: actions/setup-java@v5
with:
distribution: temurin
java-version: '17'

- name: Cache Maven dependencies
uses: actions/cache@v5
with:
path: ~/.m2
key: ${{ runner.os }}-m2-flutter-acceptance-${{ hashFiles('**/pom.xml') }}
restore-keys: |
${{ runner.os }}-m2-flutter-acceptance-
${{ runner.os }}-m2

- uses: subosito/flutter-action@v2
with:
channel: stable
flutter-version: '3.35.4'

# The runtimes join the reactor only on JDK 17+ (flutter-modules).
- name: Build the transpiler and runtimes
env:
RETRY_ONLY_MATCHING: transient
run: |
cd maven
bash ../scripts/ci/retry.sh mvn -B install \
-pl core,dart-runtime,flutter-runtime,dart-transpiler -am -DskipTests \
-Dmaven.javadoc.skip=true -Dmaven.source.skip=true \
-Dspotbugs.skip=true -Dpmd.skip=true -Dcheckstyle.skip=true -Djacoco.skip=true

- name: Import, transpile and compile each app
run: JAVA17_HOME="$JAVA_HOME" scripts/flutter-apps/run-acceptance.sh

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,8 @@ public void importsLibTreeAssetsAndFontsAndReportsDependencies() throws Exceptio
File root = Files.createTempDirectory("flutter-import").toFile();
File flutterProject = new File(root, "demo");
write(new File(flutterProject, "pubspec.yaml"), PUBSPEC);
write(new File(flutterProject, "lib/main.dart"), "void main() {}\n");
// imported, so it is a real dependency: one lib/ never imports is not reported
write(new File(flutterProject, "lib/main.dart"), "import 'package:some_uncovered_pkg/a.dart';\nvoid main() {}\n");
write(new File(flutterProject, "lib/pages/home.dart"), "class Home {}\n");
write(new File(flutterProject, "assets/images/logo.png"), "PNGDATA");
write(new File(flutterProject, "assets/icons/a.png"), "ICONA");
Expand All @@ -103,7 +104,8 @@ public void importsLibTreeAssetsAndFontsAndReportsDependencies() throws Exceptio
assertEquals("1.2.3+4", r.appVersion);
assertTrue(r.covered.toString(), r.covered.get(0).startsWith("flutter ("));
assertTrue(r.covered.toString(), r.covered.get(1).startsWith("cupertino_icons ("));
assertEquals(java.util.Collections.singletonList("some_uncovered_pkg"), r.uncovered);
assertEquals(r.uncovered.toString(), 1, r.uncovered.size());
assertTrue(r.uncovered.toString(), r.uncovered.get(0).startsWith("some_uncovered_pkg (sources not found"));
assertTrue(r.missingAssets.toString(), r.missingAssets.isEmpty());

File flutterDir = new File(common, "src/main/flutter");
Expand Down Expand Up @@ -368,6 +370,37 @@ public void anAssetNeverOverwritesALibSourceAtTheSamePath() throws Exception {

/// lib/ is copied flat, so lib/assets/x.dart shares assets/ with the bundled
/// assets; the lib-sources record lists exactly the Dart files that came from lib/.
/// A pure-Dart package importing an unsupported one cannot compile either: the
/// verdict travels to it, and none of its files reach the build.
@Test
public void aPackageImportingAnUnsupportedOneIsUnsupportedToo() throws Exception {
File root = Files.createTempDirectory("flutter-transitive").toFile();
File flutterProject = new File(root, "demo");
write(new File(flutterProject, "pubspec.yaml"), "name: demo\nversion: 1.0.0\n"
+ "dependencies:\n flutter:\n sdk: flutter\n outer: 1.0.0\n");
write(new File(flutterProject, "lib/main.dart"), "import 'package:outer/outer.dart';\nvoid main() {}\n");
File outer = new File(root, "cache/outer-1.0.0");
write(new File(outer, "lib/outer.dart"), "import 'package:inner/inner.dart';\nint o() => 1;\n");
File inner = new File(root, "cache/inner-1.0.0");
write(new File(inner, "lib/inner.dart"), "import 'dart:mirrors';\nint i() => 1;\n");
write(new File(flutterProject, ".dart_tool/package_config.json"), "{\n \"configVersion\": 2,\n"
+ " \"packages\": [\n"
+ " {\n \"name\": \"outer\",\n \"rootUri\": \"" + outer.toURI()
+ "\",\n \"packageUri\": \"lib/\"\n },\n"
+ " {\n \"name\": \"inner\",\n \"rootUri\": \"" + inner.toURI()
+ "\",\n \"packageUri\": \"lib/\"\n }\n"
+ " ]\n}\n");

File common = new File(root, "common");
FlutterProjectImporter.Result r = new FlutterProjectImporter(new com.codename1.build.SystemStreamLog())
.importProject(flutterProject, common, null, null);

assertTrue(r.unsupportedPackages.toString(), r.unsupportedPackages.containsKey("inner"));
assertTrue(r.unsupportedPackages.toString(),
String.valueOf(r.unsupportedPackages.get("outer")).startsWith("depends on inner"));
assertFalse(new File(common, "src/main/flutter/packages/outer/outer.dart").exists());
}

@Test
public void recordsTheDartSourcesCopiedFromLib() throws Exception {
File root = Files.createTempDirectory("flutter-lib-sources").toFile();
Expand Down Expand Up @@ -661,4 +694,45 @@ private static void write(File f, String content) throws IOException {
private static String read(File f) throws IOException {
return new String(Files.readAllBytes(f.toPath()), "UTF-8");
}

@Test
public void entryPointForwardsPluginCallbacksOnlyWhenUsed() {
String plain = FlutterProjectImporter.entryPointSource("com.example", "Main", "com.example.flutter",
java.util.Arrays.asList("flutter", "dio"));
assertFalse(plain.contains("implements"));
assertFalse(plain.contains("AppLinks"));
assertTrue(plain.contains("com.example.flutter.FlutterRegistry.invokeMain();"));
// naming com.codename1.push makes the Android build require google-services.json
assertFalse(plain.contains("com.codename1.push"));

String all = FlutterProjectImporter.entryPointSource("com.example", "Main", "com.example.flutter",
java.util.Arrays.asList("app_links", "flutter_local_notifications", "firebase_messaging"));
assertTrue(all.contains("implements com.codename1.notifications.LocalNotificationCallback, "
+ "com.codename1.push.PushCallback"));
assertTrue(all.contains("AppLinks.appStarted();\n super.start();"));
assertTrue(all.contains("FlutterLocalNotificationsPlugin.notificationTapped(notificationId);"));
assertTrue(all.contains("FirebaseMessaging.pushReceived(value);"));
assertTrue(all.contains("return com.codename1.push.Push.getPushKey();"));
assertTrue(all.contains("com.codename1.push.Push.subscribeToTopic(topic);"));
}

@Test
public void pluginPacksAreCovered() {
for (String p : new String[] {"share_plus", "geolocator", "image_picker", "firebase_auth",
"firebase_messaging", "google_maps_flutter", "flutter_local_notifications", "app_links"}) {
assertTrue(p, FlutterProjectImporter.COVERED.containsKey(p));
}
}

@Test
public void importLookingTextInTripleQuotedStringsIsNotADirective() {
String src = "import 'package:real/real.dart';\n"
+ "const doc = \"\"\"\nimport 'package:fake/fake.dart';\n\"\"\";\n"
+ "const raw = r'''\nimport 'package:fake2/x.dart';\\\n''';\n"
+ "const re = r'\\d';\nimport 'package:after/after.dart';\n";
String out = FlutterProjectImporter.stripComments(src);
assertTrue(out, out.contains("package:real/real.dart"));
assertTrue(out, out.contains("package:after/after.dart"));
assertFalse(out, out.contains("fake"));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ public class TranscodeFlutterMojo extends AbstractCN1Mojo {
protected void executeImpl() throws MojoExecutionException, MojoFailureException {
if (flutterSourceDir == null || !flutterSourceDir.isDirectory()) {
sweepStaleOutput();
removeDeselectedPackClasses();
// Flutter was removed from the project: its assets go with it.
removeStaleAssets(new HashSet<String>());
return;
Expand All @@ -116,6 +117,15 @@ protected void executeImpl() throws MojoExecutionException, MojoFailureException
req.stubClasspathEntry(a.getFile());
}
}
// The app's own Java can name a plugin pack too (the import-generated entry point
// installs the firebase_messaging and app_links hooks): the transpiler copies
// exactly the packs that are named, so it must see that Java as well.
for (Object root : project.getCompileSourceRoots()) {
File dir = new File(String.valueOf(root));
if (dir.isDirectory() && !dir.getAbsoluteFile().equals(flutterOutputDir.getAbsoluteFile())) {
req.javaSourceRoot(dir);
}
}
TranspileResult result = new DartTranspiler().transpile(req);
for (Diagnostic d : result.getDiagnostics()) {
String msg = "src/main/flutter/" + d.file + ":[" + d.line + "," + d.col + "] "
Expand All @@ -139,10 +149,32 @@ protected void executeImpl() throws MojoExecutionException, MojoFailureException
} else {
getLog().info("Transpiled Flutter sources to " + flutterOutputDir);
}
removeDeselectedPackClasses();
copyAssets();
registerSourceRoot();
}

/**
* The transpiler deletes the source of a plugin pack the app stopped naming, but javac
* does not reliably delete the class compiled from it on an earlier build. Left in
* target/classes, those classes would still ship -- and still imply the permissions
* (location, push, ...) the builders derive from the classes they find.
*/
private void removeDeselectedPackClasses() {
String packs = "com/codename1/flutter/plugins".replace('/', File.separatorChar);
File[] compiled = new File(project.getBuild().getOutputDirectory(), packs).listFiles();
if (compiled == null) {
return;
}
File selected = new File(flutterOutputDir, packs);
for (File pack : compiled) {
if (pack.isDirectory() && !new File(selected, pack.getName()).isDirectory()) {
deleteRecursive(pack);
getLog().debug("Removed classes of unused Flutter plugin pack " + pack.getName());
}
}
}

private void checkJdk() throws MojoFailureException {
String spec = System.getProperty("java.specification.version", "1.8");
int major;
Expand Down
Loading
Loading