使用Gradle发布工件到Maven仓库

颜博达
2023-12-01

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发布协议依赖库
协议依赖库
httporg.apache.maven.wagon:wagon-http:2.2
sshorg.apache.maven.wagon:wagon-ssh:2.2
ssh-externalorg.apache.maven.wagon:wagon-ssh-external:2.2
ftporg.apache.maven.wagon:wagon-ftp:2.2
webdavorg.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元素到他的父元素,可以使用闭包。(在上面的例子中,repositoryauthentication就是添加到父元素的bean元素),下标列出了可用的bean】元素及只想相应类的javadoc的链接。

  • MavenDeployer可用的配置元素(Configuration elements of the MavenDeployer)
元素Javadoc链接(只想Gradle官网)
rootMavenDeployer
repositoryorg.apache.maven.artifact.ant.RemoteRepository
authenticationorg.apache.maven.artifact.ant.Authentication
releasesorg.apache.maven.artifact.ant.RepositoryPolicy
snapshotsorg.apache.maven.artifact.ant.RepositoryPolicy
proxyorg.apache.maven.artifact.ant.Proxy
snapshotRepositoryorg.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文件包含groupIdartifactIdversionpackagingdependency元素(参考下表),dependency元素通过项目的依赖定义创建。

  • Maven POM文件元素的默认值(Default Values for Maven POM generation)
Maven元素默认值备注
groupIdproject.group组id
artifactIduploadTask.repositories.mavenDeployer.pom.artifactId (if set) or archiveTask.baseName.构件id
versionproject.version发布版本
packagingarchiveTask.extension文件打包后缀,如jar,aar

在这里,uploadTaskarchiveTask任务是分别用来上传和生成归档文件的(比如:uploadArchivesjar)。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'
                    }
                }
            }
        }
    }
}

注意:groupIdartifactIdversionpackaging应始终在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来发布。该组件目前支持的出版物包括:

名称内容提供者工件依赖
javaJava Plugin生成jar文件依赖来自runtime配置
webWar Plugin生成war文件没有依赖

说明:因为java插件与com.android.librarycom.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 依赖类型
implementationruntime
apicompile
providedprovided
runtimeOnlyruntime
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 处理依赖传递问题

 类似资料: