How to build and maven-publish a flutter aar?
Asked Answered
S

2

6

I am working on a flutter project and I am building aar out of it. This aar I am able to integrate into my native android project by following the instructions provided by the flutter. But my problem is that this aar is integrated locally. I want to maven-publish this aar so that it can be accessed easily by team members.

Publishing out from the native library is possible with the help of the publishing task in grade. But for the flutter library, I am not sure what we are supposed to do. So, I need help with this.

Squib answered 9/6, 2020 at 14:1 Comment(0)
D
5

I was trying to figure out the same thing.. I didnt, really, but here's how far I got:

First attempt

It's a bit messy and might get overwritten, but I was able to publish to a local Nexus Repository by adding the maven-publish stuff to the generated project gradle .android/Flutter/build.gradle which seems to be responsible for building libraries.

apply plugin: 'maven-publish'

...

afterEvaluate {
    publishing {
        repositories {
            maven {
                url = "http://10.2.0.210:8081/repository/maven-releases/"
                credentials{
                    username "a"
                    password "b"
                }
            }
        }

        publications {
            // Creates a Maven publication called "release".
            release(MavenPublication) {
                // Applies the component for the release build variant.
                from project.components.release

                // You can then customize attributes of the publication as shown below.
                groupId = 'com.example.myflutmod'
                artifactId = 'something_module'
                version = '0.1.0'
            }
            // Creates a Maven publication called “debug”.
            debug(MavenPublication) {
                // Applies the component for the debug build variant.
                from project.components.debug

                groupId = 'com.example.myflutmod'
                artifactId = 'something_module'
                version = '0.1.0'
            }
        }
    }
}

then I did

flutter build aar
./gradlew -b .android/Flutter/build.gradle publish

and finally in the main android app project adding the same maven repository configuration and just a dependency like implementation "com.example.myflutmod:something_module:0.1.0"

But it didn't include "plugin dependency aars" in the upload, (like in my case url_launcher), so the app didn't build. That might be something easy to add, though I didn't look in to it.

Another option

So instead I went deeper and found that in modifying the flutter tools it was very easy to make it upload all the .aars it built by replacing the filesystem repo with your custom one. So in aar_init_script.gradle under your flutter sdk installation:

diff --git a/packages/flutter_tools/gradle/aar_init_script.gradle b/packages/flutter_tools/gradle/aar_init_script.gradle
index cc2bbe6f98..242ac61fb0 100644
--- a/packages/flutter_tools/gradle/aar_init_script.gradle
+++ b/packages/flutter_tools/gradle/aar_init_script.gradle
@@ -37,7 +37,10 @@ void configureProject(Project project, String outputDir) {
     project.uploadArchives {
         repositories {
             mavenDeployer {
-                repository(url: "file://${outputDir}/outputs/repo")
+                //repository(url: "file://${outputDir}/outputs/repo")
+                repository(url: "http://10.2.0.210:8081/repository/maven-releases/") {
+                    authentication(userName: "a", password: "b")
+                }
             }
         }
     }

It looks like it would be reasonably simple for them to add a some property or switch to the build command to make it use an externally configured repository. Maybe there's a PR in the future.

But now I'm more leaning towards just making a script that uploads whatever is in file://${outputDir}/outputs/repo "manually" after builds instead.

Domitian answered 23/2, 2021 at 9:46 Comment(3)
have you found any other way for this, and can you share where I can get resources to create a script to manually uploadMllly
@Domitian have you tried? can you share with us some hints to upload "file://${outputDir}/outputs/repo" to remote mavenMoonlighting
sorry, afraid not. i left that project behind and have not been doing much flutter since.Domitian
P
0

Add shell script and gradle file to your flutter project and run it whenever you need aar to be updated in maven

upload.sh

#!/usr/bin/env sh

flutter pub get

#cd ..
#./gradlew build

cd .android
./gradlew build

./gradlew \
  -I=../script/flutter_aar_upload.gradle \
  -Pmaven-url=maven-url \
  -Pmaven-user=username \
  -Pmaven-pwd=password \
  -Pis-plugin=false \
  -PbuildNumber=${pom-1.0.0} \
  -Pt

flutter_aar_upload_maven.gradle

// This script is used to initialize the build in a module or plugin project.
// During this phase, the script applies the Maven plugin and configures the
// destination of the local repository.
// The local repository will contain the AAR and POM files.

void configureProject(Project project, String mavenUrl, String mavenUser, String mavenPwd, String version) {
  if (!project.hasProperty("android")) {
    throw new GradleException("Android property not found.")
  }
  if (!project.android.hasProperty("libraryVariants")) {
    throw new GradleException("Can't generate AAR on a non Android library project.")
  }

  project.apply plugin: "maven"

  // Snapshot versions include the timestamp in the artifact name.
  // Therefore, remove the snapshot part, so new runs of `flutter build aar` overrides existing artifacts.
  // This version isn't relevant in Flutter since the pub version is used
  // to resolve dependencies.

  project.version = version
  if (mavenUrl.startsWith("file:")) {
    //本地路径时,原逻辑
    project.version = project.version.replace("-SNAPSHOT", "")
  }


  project.android.libraryVariants.all { variant ->
    addAarTask(project, variant)
  }

  project.uploadArchives {
    repositories {
      mavenDeployer {
        repository(url: mavenUrl) {
          if (mavenUser != null) {
            authentication(userName: mavenUser, password: mavenPwd)
          }
        }
        //默认本地路径:
        //repository(url: "file://${outputDir}/outputs/repo")
      }
    }
  }
  if (!project.property("is-plugin").toBoolean()) {
    return
  }

  if (project.hasProperty('localEngineOut')) {
    // TODO(egarciad): Support local engine.
    // This most likely requires refactoring `flutter.gradle`, so the logic can be reused.
    throw new GradleException(
        "Local engine isn't supported when building the plugins as AAR. " +
            "See: https://github.com/flutter/flutter/issues/40866")
  }

  // This is a Flutter plugin project. Plugin projects don't apply the Flutter Gradle plugin,
  // as a result, add the dependency on the embedding.
  project.repositories {
    maven {
      url "https://storage.googleapis.com/download.flutter.io"
    }
  }
  String engineVersion = flutterEngineVersion()
  project.dependencies {
    // Add the embedding dependency.
    //使用api方式打入aar
    compileOnly("io.flutter:flutter_embedding_release:1.0.0-$engineVersion") {
      //    api("io.flutter:flutter_embedding_release:1.0.0-$engineVersion") {
      // We only need to expose io.flutter.plugin.*
      // No need for the embedding transitive dependencies.
      transitive = true
    }
  }
}

void configurePlugin(Project project, String outputDir, String mavenUser, String mavenPwd) {
  if (!project.hasProperty("android")) {
    // A plugin doesn't support the Android platform when this property isn't defined in the plugin.
    return
  }
  //flutter plugin 版本号
  File pubspecFile = project.file("../pubspec.yaml")
  String versionLine = pubspecFile.readLines().find { line->
    line.startsWith("version:")
  }
  def pluginVersion = versionLine.split(":")[1].trim()
  println("configurePlugin: " + project.toString() + " v" + pluginVersion)
  configureProject(project, outputDir, mavenUser, mavenPwd, pluginVersion)
}

void addAarTask(Project project, variant) {
  String variantName = variant.name.capitalize()
  String taskName = "assembleAar$variantName"
  project.tasks.create(name: taskName) {
    // This check is required to be able to configure the archives before `uploadArchives` runs.
    if (!project.gradle.startParameter.taskNames.contains(taskName)) {
      return
    }
    project.uploadArchives.repositories.mavenDeployer {
      pom {
        artifactId = "${project.name}_${variant.name.toLowerCase()}"
      }
    }
    overrideDefaultPublishConfig(project, variant)
    // Generate the Maven artifacts.
    finalizedBy "uploadArchives"
  }
}

// This method mimics the logic in AGP when `android.defaultPublishConfig` is set in `build.gradle`:
// https://android.googlesource.com/platform/tools/base/+/studio-master-dev/build-system/gradle-core/src/main/java/com/android/build/gradle/internal/variant/VariantHelper.java
//
// Unfortunately, `android.defaultPublishConfig` cannot be overriden at this point since
// AGP already run this code.
void overrideDefaultPublishConfig(Project project, variant) {
  String variantName = variant.name.capitalize()
  Task bundle = project.tasks.findByName("bundle${variantName}Aar")
  // gradle:3.2.0
  if (bundle == null) {
    bundle = project.tasks.findByName("bundle${variantName}") // gradle:3.1.0
  }
  if (bundle == null) {
    throw new GradleException("Can't generate AAR for variant ${variantName}.")
  }
  // Clear the current archive artifacts since the artifacts are based on `android.defaultPublishConfig`.
  project.configurations["archives"].artifacts.clear()
  // Add the artifact that will be published.
  project.artifacts.add("archives", bundle)

  def scopeMappings = project.uploadArchives.repositories.mavenDeployer.pom.scopeMappings
  // Clear the scope mappings added by AGP since they are based on the current `android.defaultPublishConfig`.
  scopeMappings.mappings.clear()
  // Add the new mappings.
  for (Configuration configuration : flattenConfiguration(variant.runtimeConfiguration)) {
    scopeMappings.addMapping(/* priority = */ 300, configuration, "compile")
  }
}

Set<Configuration> flattenConfiguration(Configuration configuration) {
  Set<Configuration> configs = [configuration]
  for (Configuration extend : configuration.extendsFrom) {
    configs.addAll(flattenConfiguration(extend))
  }
  return configs
}

projectsEvaluated {
  assert rootProject.hasProperty("is-plugin")
  if (rootProject.property("is-plugin").toBoolean()) {
    assert rootProject.hasProperty("maven-url")
    // In plugin projects, the root project is the plugin.
    configureProject(rootProject, rootProject.property("maven-url"),
        rootProject.property("maven-user"), rootProject.property("maven-pwd"),
        project.property("buildNumber")
    )
    return
  }
  // The module project is the `:flutter` subproject.
  Project moduleProject = rootProject.subprojects.find { it.name == "flutter" }

  assert moduleProject != null
  assert moduleProject.hasProperty("maven-url")
  configureProject(moduleProject, moduleProject.property("maven-url"),
      moduleProject.property("maven-user"), moduleProject.property("maven-pwd"),
      moduleProject.property("buildNumber")
  )

  // Gets the plugin subprojects.
  Set<Project> modulePlugins = rootProject.subprojects.findAll {
    it.name != "flutter" && it.name != "app"
  }
  // When a module is built as a Maven artifacts, plugins must also be built this way
  // because the module POM's file will include a dependency on the plugin Maven artifact.
  // This is due to the Android Gradle Plugin expecting all library subprojects to be published
  // as Maven artifacts.
  String mavenUrl = moduleProject.property("maven-url")
  String version = moduleProject.property("buildNumber")

  println("Version: $version")
  println("MavenUrl: " + mavenUrl)

  //输出 配置
  String buildMode = moduleProject.gradle.startParameter
      .taskNames.find { it.startsWith("assembleAar") }.substring(11)
  println("BuildMode: $buildMode")

  println("================================================================================")
  //配置插件
  modulePlugins.each { pluginProject ->
    configurePlugin(pluginProject, mavenUrl,
        moduleProject.property("maven-user"), moduleProject.property("maven-pwd"))
    moduleProject.android.libraryVariants.all { variant ->
      // Configure the `assembleAar<variantName>` task for each plugin's projects and make
      // the module's equivalent task depend on the plugin's task.
      String variantName = variant.name.capitalize()
      moduleProject.tasks.findByPath("assembleAar$variantName")
          .dependsOn(pluginProject.tasks.findByPath("assembleAar$variantName"))
    }
  }
  //结束
  println("================================================================================")

  String mUrl = mavenUrl
  if (mavenUrl.startsWith("file://")) {
    mUrl = mavenUrl.substring(7)
  }

  String groupId = moduleProject.group

  println("""
1. 添加maven地址
repositories {
  maven {
      url '${mUrl}'
  }
  maven {
      url 'https://storage.googleapis.com/download.flutter.io'
  }
}
2. 添加依赖
dependencies {
  implementation '${groupId}:flutter_${buildMode.toLowerCase()}:${version}'
}
""")
}
Priscillaprise answered 25/3, 2022 at 6:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.