Skip to content

flutter config --explicit-package-dependencies breaks flutter build apk #160407

@matanlurey

Description

@matanlurey

Looking at the failures still being flagged in #160289, I see a trend where flutter build apk (which implicitly is release mode) causes the integration_test package itself to fail to be built (example); I'm guessing that when we short-circuit the build, we're still leaving artifacts that integration_test is depended on, but not in a way that compilation succeeds.

Reproduces at HEAD:

# Opt-in to the feature
flutter config --explicit-package-dependencies

# Create an Android plugin
flutter create plugin_a --template=plugin --platforms=android

# Try to build the example app for the plugin
cd plugin_a/example
flutter build apk

By opting-out again (flutter config --no-explicit-package-dependencies) the build succeeds.

Interestingly, the integration test for this feature does do a flutter build apk --release, so I'm guessing this has something to do with how integration_test imports and uses the Android embedder (i.e. import io.flutter.embedding.android.FlutterActivity and like), and the default (empty) plugin template does not - we should add a regression test as part of fixing this.


Some potentially useful bits:

With the flag enabled, .flutter-plugins-dependencies looks like this
{
  "info": "This is a generated file; do not edit or check into version control.",
  "plugins": {
    "ios": [
      {
        "name": "integration_test",
        "path": "/Users/matanl/Developer/flutter/packages/integration_test/",
        "native_build": true,
        "dependencies": [],
        "dev_dependency": true
      }
    ],
    "android": [
      {
        "name": "integration_test",
        "path": "/Users/matanl/Developer/flutter/packages/integration_test/",
        "native_build": true,
        "dependencies": [],
        "dev_dependency": true
      },
      {
        "name": "plugin_a",
        "path": "/private/tmp/flutter.tmp/plugin_a/",
        "native_build": true,
        "dependencies": [],
        "dev_dependency": false
      }
    ],
    "macos": [],
    "linux": [],
    "windows": [],
    "web": []
  },
  "dependencyGraph": [
    {
      "name": "integration_test",
      "dependencies": []
    },
    {
      "name": "plugin_a",
      "dependencies": []
    }
  ],
  "date_created": "2024-12-16 20:52:06.884223",
  "version": "3.28.0-1.0.pre.157",
  "swift_package_manager_enabled": {
    "ios": false,
    "macos": false
  }
}
Here is what plugin_a/pubspec.yaml looks like
dependencies:
  flutter:
    sdk: flutter

  plugin_a:
    # When depending on this package from a real application you should use:
    #   plugin_a: ^x.y.z
    # See https://dart.dev/tools/pub/dependencies#version-constraints
    # The example app is bundled with the plugin so we use a path dependency on
    # the parent directory to use the current plugin's version.
    path: ../

  # The following adds the Cupertino Icons font to your application.
  # Use with the CupertinoIcons class for iOS style icons.
  cupertino_icons: ^1.0.8

dev_dependencies:
  integration_test:
    sdk: flutter
  flutter_test:
    sdk: flutter

  # The "flutter_lints" package below contains a set of recommended lints to
  # encourage good coding practices. The lint set provided by the package is
  # activated in the `analysis_options.yaml` file located at the root of your
  # package. See that file for information about deactivating specific lint
  # rules and activating additional ones.
  flutter_lints: ^5.0.0

I assume it's further work required in flutter.groovy:

private void configurePluginProject(Map<String, Object> pluginObject) {
assert(pluginObject.name instanceof String)
Project pluginProject = project.rootProject.findProject(":${pluginObject.name}")
if (pluginProject == null) {
return
}
// Apply the "flutter" Gradle extension to plugins so that they can use it's vended
// compile/target/min sdk values.
pluginProject.extensions.create("flutter", FlutterExtension)
// Add plugin dependency to the app project.
project.android.buildTypes.each { buildType ->
String flutterBuildMode = buildModeFor(buildType)
if (flutterBuildMode != "release" || !pluginObject.dev_dependency) {
// Only add dependency on dev dependencies in non-release builds.
project.dependencies {
api(pluginProject)
}
}
}
Closure addEmbeddingDependencyToPlugin = { buildType ->
String flutterBuildMode = buildModeFor(buildType)
// In AGP 3.5, the embedding must be added as an API implementation,
// so java8 features are desugared against the runtime classpath.
// For more, see https://github.com/flutter/flutter/issues/40126
if (!supportsBuildMode(flutterBuildMode)) {
return
}
if (!pluginProject.hasProperty("android")) {
return
}
if (flutterBuildMode == "release" && pluginObject.dev_dependency) {
// This plugin is a dev dependency and will not be included in
// the release build, so no need to add the embedding
// dependency to it.
return
}
// Copy build types from the app to the plugin.
// This allows to build apps with plugins and custom build types or flavors.
pluginProject.android.buildTypes {
"${buildType.name}" {}
}
// The embedding is API dependency of the plugin, so the AGP is able to desugar
// default method implementations when the interface is implemented by a plugin.
//
// See https://issuetracker.google.com/139821726, and
// https://github.com/flutter/flutter/issues/72185 for more details.
addApiDependencies(
pluginProject,
buildType.name,
"io.flutter:flutter_embedding_$flutterBuildMode:$engineVersion"
)
}
// Wait until the Android plugin loaded.
pluginProject.afterEvaluate {
// Checks if there is a mismatch between the plugin compileSdkVersion and the project compileSdkVersion.
if (pluginProject.android.compileSdkVersion > project.android.compileSdkVersion) {
project.logger.quiet("Warning: The plugin ${pluginObject.name} requires Android SDK version ${getCompileSdkFromProject(pluginProject)} or higher.")
project.logger.quiet("For more information about build configuration, see $kWebsiteDeploymentAndroidBuildConfig.")
}
project.android.buildTypes.all(addEmbeddingDependencyToPlugin)
}
}

Metadata

Metadata

Assignees

Labels

P1High-priority issues at the top of the work listf: integration_testThe flutter/packages/integration_test pluginplatform-androidAndroid applications specificallyteam-androidOwned by Android platform team

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions