Gradle发布工件(Publishing Artifacts)
此文章基于Gradle 4.6编写。
1. Maven Plugin(旧版)
使用Maven Plugin发布Artifcat是旧的插件,新版Gradle有新的插件Maven Publish Plugin发布Artifact,需要注意的是,如果项目使用Gradle 7.0及更新版本,maven插件已被移除,请使用Maven Publish Plugin(新版)替代
Maven Plugin
是添加支持发布artifact
到Maven仓库的插件。
1.1 使用
需要使用Maven plugin
插件,首先需要在项目的构建脚本build.gradle
上添加插件引用声明:
apply plugin: 'maven'
1.2 任务
Maven Plugin
定义了以下任务
任务名称 | 依赖于 | 类型 | 描述 |
---|---|---|---|
install | 构建关联归档的所有任务。 | Upload | 将关联的工件安装到本地Maven缓存,包括Maven元数据生成。默认情况下,安装任务与archives配置相关联。默认情况下,此配置仅将默认jar作为元素。要了解有关安装到本地存储库的更多信息,请参阅:“安装到本地存储库”一节 |
1.3 依赖管理
Maven Plugin
没有定义任何依赖配置。
1.4 与Maven仓库交互
1.4.1 介绍
使用Gradle,您可以部署到远程Maven存储库或安装到本地Maven存储库。这包括所有Maven元数据操作,也适用于Maven快照。事实上,Gradle的部署与Maven是100%兼容,因为使用了本机Maven Ant任务。
如果没有POM文件,Gradle可以根据它具有的依赖性信息为您生成一个POM文件。
1.4.2 部署到Maven存储仓库
如果项目只生成默认的jar文件,要将此jar文件部署到远程Maven存储库,在build.gradle
添加以下代码:
apply plugin: 'maven' // 启用Maven Plugin
uploadArchives {
repositories {
mavenDeployer {
repository(url: "file://localhost/tmp/myRepo/") // url的值为存储仓库地址
}
}
}
执行uplaodArchives
任务,Gradle会生成一个POM文件,并将jar文件上传发布到存储仓库中。
如果需要支持除file
以外的协议,还有更多工作要做。在这种情况下,Maven代码需要额外的库。具体需要哪些库取决于所使用的协议。
- Maven发布协议依赖库
协议 | 依赖库 |
---|---|
http | org.apache.maven.wagon:wagon-http:2.2 |
ssh | org.apache.maven.wagon:wagon-ssh:2.2 |
ssh-external | org.apache.maven.wagon:wagon-ssh-external:2.2 |
ftp | org.apache.maven.wagon:wagon-ftp:2.2 |
webdav | org.apache.maven.wagon:wagon-webdav:1.0-beta-2 |
file | - |
加入需要使用ssh
协议,需要在build.gradle
这么做:
// 声明一个configuration
configurations {
deployerJars
}
repositories {
mavenCentral()
}
dependencies {
// deployJar是前面configuration声明的
deployerJars "org.apache.maven.wagon:wagon-ssh:2.2" // ssh依赖库
}
uploadArchives {
repositories.mavenDeployer {
configuration = configurations.deployerJars
repository(url: "scp://repos.mycompany.com/releases") {
authentication(userName: "me", password: "myPassword") // 这里是仓库账号认证设置,用户名和密码
}
}
}
Maven发布有许多配置项,这些配置项是依赖Groovy构建器完成的。配置项的所有元素都是Java bean。配置简单的属性,可以直接映射到bean的元素。如果添加bean元素到他的父元素,可以使用闭包。(在上面的例子中,repository
和authentication
就是添加到父元素的bean元素),下标列出了可用的bean】元素及只想相应类的javadoc的链接。
- MavenDeployer可用的配置元素(Configuration elements of the MavenDeployer)
元素 | Javadoc链接(只想Gradle官网) |
---|---|
root | MavenDeployer |
repository | org.apache.maven.artifact.ant.RemoteRepository |
authentication | org.apache.maven.artifact.ant.Authentication |
releases | org.apache.maven.artifact.ant.RepositoryPolicy |
snapshots | org.apache.maven.artifact.ant.RepositoryPolicy |
proxy | org.apache.maven.artifact.ant.Proxy |
snapshotRepository | org.apache.maven.artifact.ant.RemoteRepository |
在Maven中,可以定义存储仓库和可选的快照存储仓库。如果没有定义快照存储仓库,会将版本和快照部署到这个repository
元素配置的存储仓库,如果配置了快照,快照将部署到snapshortRepository
元素配置的存储仓库中。
1.4.3 安装到本地存储库(Installing to the local repository)
Maven Plugin
会往项目中添加install
任务。这个任务依赖于所有archives
配置的归档任务。它将安装这些归档文件到本地仓库存储中(如果在Maven在string.xml中重新配置了默认仓库的位置,这个任务将会根据改配置指向新的存储位置)。
1.4.4 Maven POM文件的生成(Maven POM generation)
当发布构件到Maven仓库时,Gradle会自动生成一个POM文件。POM文件包含groupId
、artifactId
、version
、packaging
、dependency
元素(参考下表),dependency
元素通过项目的依赖定义创建。
- Maven POM文件元素的默认值(Default Values for Maven POM generation)
Maven元素 | 默认值 | 备注 |
---|---|---|
groupId | project.group | 组id |
artifactId | uploadTask.repositories.mavenDeployer.pom.artifactId (if set) or archiveTask.baseName. | 构件id |
version | project.version | 发布版本 |
packaging | archiveTask.extension | 文件打包后缀,如jar,aar |
在这里,uploadTask
和archiveTask
任务是分别用来上传和生成归档文件的(比如:uploadArchives
和jar
)。archiveTask.basename
默认是archiveTask.archivesBasename
的值,而archiveTask.archivesBasename
的默认值是project.name
的值。
当你设置
arhiveTask.baseName
属性值代替默认值是,还必须设置uploadtask.prepositories.mavenDeployer.pom.artifactId
为相同值,否则,在同一个构建中的不同项目里,可能会在生成POM文件时引用错误artifaceId。
生成的POM文件可以在<buildDir>/poms
目录下找到,他们可以通过MavenPom
API进行自定义。例如,你可以将部署到Maven存储仓库的工件具有与Gradle编译生成的工件有不同的版本或者名称。要做到这些,可以根据以下方法:
- build.gradle
uploadArchives {
repositories {
mavenDeployer {
repository(url: "file://localhost/tmp/myRepo/")
pom.version = '1.0Maven' // 发布到仓库的构建版本
pom.artifactId = 'myMavenName' // 发布到仓库的构建id
}
}
}
添加额外的内容到POP文件,可用pom.project
构建器,使用这个而构建起,所有在Maven POM引用中列出的元素都可以添加到里面。
- build.gradle
uploadArchives {
repositories {
mavenDeployer {
repository(url: "file://localhost/tmp/myRepo/")
pom.project {
licenses {
license {
name 'The Apache Software License, Version 2.0'
url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
distribution 'repo'
}
}
}
}
}
}
注意:
groupId
,artifactId
,version
和packaging
应始终在pom
对象中进行设置。
Maven插件还支持同时发布多个工件,这时需要为要发布的每个工件声明一个过滤器。此过滤器定义了一个布尔表达式,它接受Gradle工件。每个过滤器都有一个与之关联的POM,您可以配置它。要了解更多相关信息,请查看PomFilterContainer及其相关课程。
1.5 依赖关系映射(Dependency mapping)
通过Java和War插件添加的Gradle配置与Maven作用域之间,Maven插件配置了默认映射。大多数情况下,您可以跳过这部分而不需要关注它。映射的工作原理是:您可以将配置映射到一个且仅一个作用域,可以将不同的配置映射到一个或不同的作用域,您还可以为特定的配置到作用域映射分配优先级。看看Conf2ScopeMappingContainer了解更多。
1.6 在AndroidStudio中发布到远程仓库
在AndroidStudio中使用Maven插件发布工件到远程仓库存储,在需要发布的模块下面的build.gradle添加以下配置
apply plugin: maven
uploadArchives {
repositories {
mavenDeployer {
// 仓库地址
repository(url: repositoryUrl) {
// 用户校验 用户名/密码
authentication(userName: uname, password: pwd)
}
// 快照仓库地址(可选)
snapshotRepository(url: repositorySnapshotUrl) {
// 用户校验 用户名/密码
authentication(userName: uname, password: pwd)
}
pom.project {
name artifactName // artifact名称
version versionName // artifact版本
artifactId _artifactId // artifact id
groupId _groupId // artifact所属Group Id
packaging packagingType // 文件格式,例如jar、aar
description _description // 描述
}
}
}
}
配置好后,打开Gradle面板,双击模块名称/Tasks/upload/uploadArchives
任务或者在命令行输入gradle 模块名称:uploadArchives
执行命令发布到远程仓库,如果出现BUILD SUCCESS
字样说明发布成功。
2 Maven Publish Plugin(新版)
2.1 使用
Maven Publish Plugin通过项目中以publishing
命名,PublishingExtension类型的扩展来实现。该扩展提供了一个称为publications
的容器和一个称为repositories
的容器,Maven Publish Plugin适用于MavenPublication发布和MavenArtifactRepository仓库。
使用Maven Publish Plugin,需要在构建脚本中添加代码启用以下插件
- build.gradle
apply plugin: 'maven-publish'
2.2 出版物(Publications)
出版物对象描述创建的出版物的结构/配置,出版物通过任务发布到仓库存储,出版物配置明确发布内容。项目中的出版物都在 PublishingExtension.getPublications()容器中定义。
要使Maven Publish Plugin生效,必须将MavenPublication添加到出版物集合中,此出版物确定发布的工件以及关联的POM文中包含的详细信息。出版物可以通过添加组件、自定义工件以及修改生成的POM文件来配置发布。
2.3 发布程序组件(Publishing a Software Component)
发布Gradle项目到Maven仓库最简单的方式,就是通过SoftwareComponent来发布。该组件目前支持的出版物包括:
名称 | 内容提供者 | 工件 | 依赖 |
---|---|---|---|
java | Java Plugin | 生成jar文件 | 依赖来自runtime 配置 |
web | War Plugin | 生成war文件 | 没有依赖 |
说明:因为
java
插件与com.android.library
和com.android.application
不兼容,所以在AndroidStudio中,使用maven-plugin
插件时不可同时使用java
插件。
2.4 在AndroidStudio中发布到远程仓库
2.4.1 手动指定发布工件和使用pom
文件关联依赖
旧版的 maven
插件无需自己定义发布内容和 pom
,插件会直接将编译输出的 aar
作为发布内容,并且根据 build.gradle
声明的依赖生成 pom
文件。然而新的 maven-publish
插件需要手动指定发布内容和 pom
文件内容。可以在声明出版物的时候,通过 artifact
进行指定(同时发布多个工件使用 artifacts
,更多详情请参考 MavenPublication 文档说明),手动配置 pmo
文件可以在出版物声明时使用 pom.withXml{}
声明即可。如下示例所示(在AndroidStudio中使用):
apply plugin: 'maven-publish'
publishing {
repositories {
maven {
credentials {
username 'username' // 仓库发布用户名
password 'password' // 仓库发布用户密码
}
url 'trunk_url' // 仓库地址
}
}
publications {
maven(MavenPublication) {
groupId 'group_id' // groupId
artifactId 'artifact_id' // artifactId
version '1.0.0' // 发布版本
description 'This is a liberary to test v1.0.0' // 说明描述
artifact "${project.buildDir}/outputs/aar/${project.name}-release.aar"
pom.withXml{
def dependenciesNode = asNode().appendNode("dependencies")
// 添加一个依赖
def dependencyNode = dependenciesNode.appendNode('dependency')
dependencyNode.appendNode('groupId', dependency.group)
dependencyNode.appendNode('artifactId', dependency.name)
dependencyNode.appendNode('version', dependency.version)
// 逐一添加第三方依赖。。。
}
// 如果依赖比较多,有个更简洁的方式就是读取配置的依赖配置,逐一添加。这种方法在依赖变更之后,也会自动识别,如下所示
// pom.withXml{
// def dependenciesNode = asNode().appendNode("dependencies")
// configurations.implementation.allDependencies.forEach(){
// Dependency dependency ->
// if (dependency.version != "unspecified" && dependency.name != "unspecified"){
// def dependencyNode = dependenciesNode.appendNode('dependency')
// dependencyNode.appendNode('groupId', dependency.group)
// dependencyNode.appendNode('artifactId', dependency.name)
// dependencyNode.appendNode('version', dependency.version)
// }
// }
// }
}
}
}
2.4.2 添加配置自动获取发布工件和生成 pom
文件关联依赖
Android Gradle插件提供了快速配置 maven-plugin
的方法,只需要添加一行配置,就可以跟旧版本的 maven
插件一样自动获取发布工件并生成 pom
文件关联依赖
publishing {
repositories {
maven {
credentials {
username 'username' // 仓库发布用户名
password 'password' // 仓库发布用户密码
}
url 'trunk_url' // 仓库地址
}
}
}
afterEvaluate {
publishing {
publications {
release(MavenPublication) {
// 添加以下配置可以自动从编译容器中获取release版本内容(使用debug可以获取debug版本内容),并生成pom文件
// 注意:发布物声明必须在 afterEvaluate 内部,因为 components 在 afterEvaluate 阶段才生成完成
from components.release
groupId 'group_id' // groupId
artifactId 'artifact_id' // artifactId
version '1.0.0' // 发布版本
description 'This is a liberary to test v1.0.0' // 说明描述
}
}
}
}
说明:通过
from components.release
可以自动从编译容器中获取release版本内容(使用debug可以获取debug版本内容),并生成pom文件。但是需要注意的是:使用from components.release
必须将配置代码放在afterEvaluate {}
中,因为编译容器中的内容只有在afterEvaluate
阶段才生成完成。
配置好后,打开Gradle面板,双击模块名称/Tasks/publishing/publishMavenPublicationToMavenRepository
任务或者在命令行输入gradle 模块名称:publishMavenPublicationToMavenRepository
执行命令发布到远程仓库,如果出现BUILD SUCCESS
字样说明发布成功。
2.4.3 处理依赖传递问题
依赖传递是使用仓库的一个便利之处,就是我们只需要引入某个库的依赖,这个库所依赖的其他依赖项,都会自动依赖到项目中,完成这一个功能主要依靠 pom.xml
文件的配置信息。 pom.xml
文件中声明了关联的所有依赖,其中 <scope>
节点定义了依赖的传递类型,其中包括以下几种:
compile
:依赖在编译时起作用,具有传递性;provided
:依赖在编译或者测试时起作用,跟compile
类似,但是无传递性;runtime
:依赖在运行时或者测试时起作用,编译时不起作用,不具备传递性;test
:依赖在测试时起作用,运行时不起作用;system
:跟provided
非常类似,主要不同之处是system
需要我们在系统中直接指定一个特定的jar,需要配合<systemPath>
使用,这个 scope 已经废弃。import
:Maven 2.0.9 之后新增,仅在依赖类型为pom
时(由<type>
节点指定)有用。表示此项依赖将会被pom
文件中所有有效的依赖替代。
其实我们常用的是上面的四种,对于手动生成 pom
文件,可以根据需求进行手动指定映射,但是对于自动生成 pom
文件的情况,我们就必须清楚在 AndroidStudio 的依赖声明中,依赖类型默认映射是怎样的,才能准确实现依赖传递。下面是常用的部分
AndroidStudio依赖类型 | Maven 依赖类型 |
---|---|
implementation | runtime |
api | compile |
provided | provided |
runtimeOnly | runtime |
compileOnly | - |
testImplementation | - |
从前面的介绍我们知道,Maven 中只有 compile
依赖才具有传递性,因此,在AndroidStudio中使用 maven-pulbish
插件发布工件,如果使用自动生成 pom
文件,那么就需要在 build.gradle
文件中声明依赖是,对需要传递的依赖使用 api
来声明。
__注意事项:旧版的
maven
插件的映射有所不同,其中implementation
映射为compile
,因此如果你的项目从旧版迁移到新版插件,切记一定要修改依赖声明,否则会出现需要传递的依赖无法传递的问题。<scope>
节点未指定时,默认为compile
,如果手动生成pom
文件,没有指定<scope>
将采用默认值,但是建议显式指定<scope>
值。 __
2.5 错误及解决方案
2.5.1 发布出现Read timeout
网络异常
报错信息似以下的例子
Upload http://192.168.33.110:8081/repository/RastarGameSdkOversea/com/test/maven/plugin/rastar-sdk-line/1.0.0/rastar-sdk-line-1.0.0.pom
Could not transfer artifact com.test.maven.plugin:rastar-sdk-line:pom:1.0.0 from/to remote (http://192.168.33.110:8081/repository/RastarGameSdkOversea/): Could not write to resource 'com/test/maven/plugin/rastar-sdk-line/1.0.0/rastar-sdk-line-1.0.0.pom'
:rsdk_line:publishAarPublicationToMavenRepository FAILED
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':rsdk_line:publishAarPublicationToMavenRepository'.
> Failed to publish publication 'aar' to repository 'maven'
> Could not write to resource 'http://192.168.33.110:8081/repository/RastarGameSdkOversea/com/test/maven/plugin/rastar-sdk-line/1.0.0/rastar-sdk-line-1.0.0.pom'.
> Read timed out
* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.
* Get more help at https://help.gradle.org
BUILD FAILED in 44s
根据报错信息,是设置了代理,检查AnroidStudio是否设置了代理,如果没有,打开Gradle用户目录(一般是在C:\Users\用户名\.gradle
),打开gradle.properties
文件,看看是否设置了代理,如果设置了,将其去掉。
- 在Gradle用户目录下
gradle.properties
设置代理的配置如下。
systemProp.http.proxyHost=127.0.0.1
systemProp.http.proxyPort=1080
2.5.2 ERROR: Cannot configure the 'publishing' extension after it has been accessed.
配置好后,刷新Gradle的时候,如果出现ERROR: Cannot configure the 'publishing' extension after it has been accessed.
错误,可以将以上的配置修改一下
apply plugin: 'maven-publish'
publishing {
repositories {
maven {
credentials {
username 'username' // 仓库发布用户名
password 'password' // 仓库发布用户密码
}
url 'trunk_url' // 仓库地址
}
}
publications {
maven(MavenPublication) {
groupId 'group_id' // groupId
artifactId 'artifact_id' // artifactId
version '1.0.0' // 发布版本
description 'This is a liberary to test v1.0.0' // 说明描述
}
}
}
将多级闭包改为少一级,将publishing
跟闭包下面的使用.
链接在一起,如下:
apply plugin: 'maven-publish'
publishing.repositories {
maven {
credentials {
username 'username' // 仓库发布用户名
password 'password' // 仓库发布用户密码
}
url 'trunk_url' // 仓库地址
}
}
publishing.publications {
maven(MavenPublication) {
groupId 'group_id' // groupId
artifactId 'artifact_id' // artifactId
version '1.0.0' // 发布版本
description 'This is a liberary to test v1.0.0' // 说明描述
}
}
2.5.3 使用新版插件后依赖传递失效了
发布时,当前库依赖的一些第三方库,切实远程仓库依赖,需要传递给当前库的引用者,但是发现依赖声明的那些依赖都不会传递给引用者了,此时可以查看下仓库中的 pom
文件,看看 <scope>
节点的值是否为 compile
,如果不是,就需要检查你发布时生成 pom
的文件,如果是使用自动生成 pom
文件,则需要检查项目依赖声明,将所有的 implementation
改为 api
即可(新版本的 maven-publish
插件默认将 implementation
映射成 runtime
),详情参考:2.4.3 处理依赖传递问题