阅读 563

Flutter项目打包aar上传maven脚本

使用

1. Flutter 模块根目录添加一个文件夹(两个文件)

script
├── flutter_aar_upload.gradle
└── upload.sh
复制代码

2.执行 upload.sh 文件

等待 BUILD SUCCESSFUL

执行输出:

按照图中最后提示配置Host项目。

构建环境:Flutter v1.17.5

原理

flutter build aar 会将构建的aar输出到当前工程 build/host 目录下,此时可以编写脚本使用mvn命令直接上传至远程仓库;

不过既然能输出到本地,这段路径一定是在某个地方配置的。经过 flutter build aar -v 命令的查看执行记录,发现了下面一段命令

im_teacher/.android/gradlew -I=/Users/xxx/Library/Flutter/packages/flutter_tools/gradle/aar_init_script.gradle
-Pflutter-root=/Users/xxx/Library/Flutter -Poutput-dir=/Users/xxx/StudioProjects/CustomerService/im_teacher/build/host -Pis-plugin=false -PbuildNumber=1.0
-Ptarget-platform=android-arm,android-arm64,android-x64 assembleAarDebug
复制代码

也就是 执行了 aar_init_script.gradle,在文件中也发现了配置输出路径的位置

mavenDeployer {
	//outputDir 为命令参数中的 output-dir
	repository(url: "file://${outputDir}/outputs/repo")
}
复制代码

于是可通过修改 aar_init_script.gradle 来达到目的,不过为了不修改flutter环境源码,将 aar_init_script.gradle 复制出以修改。没错,flutter_aar_upload.gradle 就是修改至此文件

修改后可输出打包的plugin列表,不再需要指定-Pflutter-root 路径,可自定义mavenUrl 支持本地与远程上传,支持输出Host项目需要的配置信息。

Jenkins打包配置

由于使用Jenkins打包需要运行Jenkins的服务器需要安装FlutterSDK,不一定实用,此方法仅作为示例。

以CustomerService为例:

  1. 在新建任务后配置Gitlab地址,配置参数化构建

2. 构建方式选择执行shell

# 执行目录定位到模块根目录
cd im_teacher
flutter pub get
cd .android

./gradlew \
  -I=../script/flutter_aar_upload.gradle \
  -Pmaven-url=${MavenUrl} \
  -Pmaven-user=admin \
  -Pmaven-pwd=admin123 \
  -Pis-plugin=false \
  -PbuildNumber=${Version} \
  -Ptarget-platform=android-arm,android-arm64,android-x64 assembleAarRelease
复制代码

3. 构建结束后可在控制台查看结果

脚本源码

#!/usr/bin/env sh

# 执行目录为{flutter_module_dir}/script
cd ..
# 自动生成.android文件夹
flutter pub get
cd .android

./gradlew \
  -I=../script/flutter_aar_upload.gradle \
  -Pmaven-url=http://192.168.43.136:8081/repository/maven-releases/ \
  -Pmaven-user=admin \
  -Pmaven-pwd=admin123 \
  -Pis-plugin=false \
  -PbuildNumber=1.0.0 \
  -Ptarget-platform=android-arm,android-arm64,android-x64 assembleAarRelease

# 参数说明
# -Pmaven-url 可以是本地路径,也可为远程地址
# 示例:
# maven-url=file:///Users/liben/StudioProjects/CustomerService/im_teacher/build/host
# maven-url=http://192.168.43.136:8081/repository/maven-releases/
# maven-user 与 maven-pwd 为maven远程用户及密码,若发布到本地无需设置
# -PbuildNumber=1.0.0 为版本号,可从pubspec.yaml读取
# 最后 assembleAarRelease 为打包类型,assembleAarDebug, assembleAarProfile
# 原命令需要指定 flutter-root=/Users/xxx/Library/Flutter, 现在修改为从PATH中读取flutter sdk位置,需要确保在PATH环境变量中在命令行中添加Flutter/bin



// 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.

import java.nio.file.Paths

void configureProject(Project project, String mavenUrl, String mavenUser, String mavenPwd) {
  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.
  if (mavenUrl.startsWith("file:")) {
    //本地路径时,原逻辑
    project.version = project.version.replace("-SNAPSHOT", "")
  }

  if (project.hasProperty("buildNumber")) {
    project.version = project.property("buildNumber")
  }

  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
  }
  println("configurePlugin: " + project.toString())
  configureProject(project, outputDir, mavenUser, mavenPwd)
}

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"))
    return
  }
  // The module project is the `:flutter` subproject.
  Project moduleProject = rootProject.subprojects.find { it.name == "flutter" }
  println("moduleProject: " + moduleProject)

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

  // 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
  String engineVersion = flutterEngineVersion()

  println("""
1. 添加maven地址
repositories {
  maven {
      url '${mUrl}'
  }
  maven {
      url 'https://storage.googleapis.com/download.flutter.io'
  }
}
2. 添加依赖

dependencies {
  implementation '${groupId}:flutter_${buildMode.toLowerCase()}:${version}'

  //flutter engine version
  def engineVersion = "${engineVersion}"

  implementation "io.flutter:flutter_embedding_release:1.0.0-\$engineVersion"
  implementation "io.flutter:armeabi_v7a_release:1.0.0-\$engineVersion"
  //若app模块只保留armeabi_v7a,可不需要配置arm64_v8a
  //implementation "io.flutter:arm64_v8a_release:1.0.0-\$engineVersion"
}
""")

}

//获取Flutter Engine Version
def flutterEngineVersion() {

  Process process = Runtime.getRuntime().exec(["sh", "-c", "which flutter"] as String[])

  BufferedReader reader = new BufferedReader(new InputStreamReader(process.inputStream))
  process.waitFor()

  String path = reader.readLine()
  println("Flutter Path:" + path)

  String engineVersion = Paths.get(new File(path).parent, "internal", "engine.version")
      .toFile().text.trim()
  println("Flutter Engine Version: " + engineVersion)

  return engineVersion
}
复制代码
文章分类
Android
文章标签