Gradle Customizing publishing
Publish Libraries
发布公共的库的主要步骤
- 定义发布的内容 (what)
- 定义发布的地方 (where)
- 执行发布
设置基本发布
apply plugin: 'java-library'
apply plugin: 'maven-publish'
java {
withJavadocJar()
withSourcesJar()
}
publishing {
publications {
VCamSDK(MavenPublication) {
groupId = 'com.example.demo'
artifactId = 'ApiDemo'
version = '1.0.1.a'
from components.java
}
}
repositories {
maven {
name = 'MyM1'
url = "http://com.example.org/nexus/content/groups/public/"
}
}
}
![](https://img.haomeiwen.com/i11240087/ba9665bdb0d68d6e.png)
元数据
元数据主要是描述需要发布的内容
比较官方的说法,即元数据是序列化Gradle组件模型的一种格式,类似POM文件
添加自定义工件
upload.gradle文件
ext {
aar_version = '1.0.6'
commit_id = ''
}
def outputAARDebugFile
def outputAARReleaseFile
android.libraryVariants.all { variant ->
if (variant.buildType.name == "debug") {
variant.outputs.all { output ->
outputAARDebugFile = output.outputFile
}
}
if (variant.buildType.name == "release") {
variant.outputs.all { output ->
outputAARReleaseFile = output.outputFile
}
}
}
def outputAARDebug = artifacts.add("archives", outputAARDebugFile)
def outputAARRelease = artifacts.add("archives", outputAARReleaseFile)
project.ext.commit_id = "git log -n 1 --pretty=%t".execute().text.trim()
println('project.ext.commit_id ' + project.ext.commit_id)
publishing {
publications {
ToolDebug(MavenPublication) {
groupId = 'com.example'
artifactId = 'tool'
version = project.ext.aar_version + '-' +project.ext.commit_id + '-debug'
artifact outputAARDebug
pom {
name = 'tool'
packaging = "aar"
description = 'utility method for tool usage'
}
}
VCamSDKRelease(MavenPublication) {
groupId = 'com.example'
artifactId = 'tool'
version = project.ext.aar_version + '-' + project.ext.commit_id + '-release'
artifact outputAARRelease
pom {
name = 'tool'
packaging = "aar"
description = 'utility method for tool usage'
}
}
}
repositories {
maven {
name = 'myExample'
url = "http://com.example.org/nexus/content/groups/public/"
}
maven {
name = 'mylocal'
url = "file:///c:/build/repo"
}
}
}
tasks.getByName('publishToolDebugPublicationToMylocalRepository').dependsOn('assembleDebug')
tasks.getByName('publishToolReleasePublicationToMyExampleRepository').dependsOn('assembleRelease')
通过配置上传脚本,我们做到了
-
发布的AAR包内部增加了CommitId信息文件,用于跟踪AAR包对应的代码版本
-
Debug版本只能发布到本地Maven库,不能发布到Vivo正式库
-
Debug的AAR会带上debug的字段的版本号,而Release版本版本号则是不到BuildType信息,防止本地Debug版本和使用服务器正式版本出现覆盖的风险
-
发布到Vivo正式库之前必须执行更新操作,保证本地的代码已经全部提交才能执行发布动作,这样才能保证CommitId是有效的
代码方式
从上面的例子,我们可以会发现,我们通过脚本方式添加了
- ToolDebug
- VCamSDKRelease
这两个出版物,用来区分不同的buildType,分别支持Debug和Release的发布,但是我们使用Android发布aar的时候,除了针对不同的buildType,可能还会有不同的渠道差异,而且支持的渠道会不同的改变,如果有手写的脚本方式,会造成添加渠道,就需要在upload.gradle脚本中也同步手动修改,显然这种方式效率比较低,作为一个合格程序员这是不能接受的,因为需要增加代码配置方式
upload.gradle
import java.util.zip.ZipEntry
import java.util.zip.ZipFile
import java.util.zip.ZipOutputStream
apply plugin: 'maven-publish'
def deleteVersionFile() {
def path = "$buildDir/generated/upload/"
def versionDir = file(path)
File[] files = versionDir.listFiles();
for (File child : files) {
child.delete()
}
versionDir.delete()
}
def createVersionFile() {
project.ext.fullCommitId = "git log -n 1 --pretty=%T".execute().text.trim();
//for test
project.ext.fullCommitId = '1111'
def path = "$buildDir/generated/upload/" + fullCommitId + ".txt"
def versionFile = file(path)
versionFile.getParentFile().mkdirs()
versionFile.createNewFile()
versionFile.append(fullCommitId.getBytes())
}
def copy(InputStream input, OutputStream output) {
int bytesRead;
byte[] BUFFER = new byte[4096 * 1024];
while ((bytesRead = input.read(BUFFER)) != -1) {
output.write(BUFFER, 0, bytesRead);
}
}
def appendZipFile(File sourceFile) {
// read war.zip and write to append.zip
ZipFile sourceZip = new ZipFile(sourceFile);
def tmpZip = new File("$buildDir/generated/upload/tmp.zip")
ZipOutputStream append = new ZipOutputStream(new FileOutputStream(tmpZip));
// first, copy contents from existing war
Enumeration<? extends ZipEntry> entries = sourceZip.entries();
while (entries.hasMoreElements()) {
ZipEntry e = entries.nextElement();
println("copy: " + e.getName());
append.putNextEntry(e);
if (!e.isDirectory()) {
copy(sourceZip.getInputStream(e), append);
}
append.closeEntry();
}
// now append some extra content
ZipEntry e = new ZipEntry("$project.ext.fullCommitId" + ".txt");
println("append: " + e.getName());
append.putNextEntry(e);
append.write(project.ext.fullCommitId.getBytes());
append.closeEntry();
// close
sourceZip.close();
append.close();
sourceFile.delete()
tmpZip.renameTo(sourceFile)
}
def addVersionFile(File aarFile) {
println('addVersionFile aarFile ' + aarFile.getAbsoluteFile())
createVersionFile()
appendZipFile(aarFile)
}
project.afterEvaluate {
def debugVariants = android.libraryVariants.findAll { element ->
//println("element " + element.properties)
element.buildType.name == 'debug'
}
println("debugVariants size " + debugVariants.size())
def ReleaseVariants = android.libraryVariants.findAll { element ->
//println("element " + element.properties)
element.buildType.name == 'release'
}
println("ReleaseVariants size " + ReleaseVariants.size())
def libVariants = debugVariants + ReleaseVariants
def repoCount = project.ext.repository.size()
println("repoCount " + repoCount);
for (int i = 0; i < repoCount; ++i) {
publishing.repositories.maven {
name = project.ext.repository[i]['name']
url = project.ext.repository[i]['url']
println('name ' + name + ' url ' + url)
}
}
def flavors = android.productFlavors
flavors.all { flavor ->
println("flavor " + flavor.name)
}
project.ext.abbreviatedCommitId = "git log -n 1 --pretty=%t".execute().text.trim()
//针对不同的版本添加不同的出版物
def publishingTaskNames = new HashMap<Object, Object>()
def artifactFile = new HashMap<Object, Object>()
for (variant in libVariants) {
def buildType = variant.buildType.name
buildType = buildType.substring(0, 1).toUpperCase() + buildType.substring(1)
def flavorName = variant.flavorName
if (variant.flavorName != null && !variant.flavorName.isEmpty()) {
flavorName = flavorName.substring(0, 1).toUpperCase() + flavorName.substring(1)
}
def publicationName = project.ext.baseName + flavorName + buildType
def taskName = 'bundle' + flavorName + buildType + 'Aar'
artifactFile.put(publicationName, variant.outputs[0].outputFile)
// to support 1.0.+
def publicationVersion = ''
if (buildType == 'Release') {
publicationVersion = flavorName + '_' + project.ext.version
} else {
publicationVersion = project.ext.abbreviatedCommitId + '_' + flavorName + "_" + buildType + "_" + project.ext.version
}
publishing.publications.create(publicationName, MavenPublication) {
groupId = project.ext.groupId
artifactId = project.ext.artifactId
version = publicationVersion
println('version ' + version)
artifact tasks.getByName(taskName)
pom {
name = project.ext.pom['name']
packaging = project.ext.pom['packaging']
description = project.ext.pom['description']
}
}
for (int i = 0; i < repoCount; ++i) {
def publishTaskName = 'publish' + publicationName + 'PublicationTo' + project.ext.repository[i]['name'] + 'Repository'
println("publishTaskName " + publishTaskName)
publishingTaskNames.put(publishTaskName, publicationName)
}
def publishLocalTaskName = 'publish' + publicationName + 'PublicationTo' + 'MavenLocal'
publishingTaskNames.put(publishLocalTaskName, publicationName)
}
for (taskName in publishingTaskNames.keySet()) {
println("taskName:" + taskName)
def aarFile = artifactFile[publishingTaskNames[taskName]]
println("addVersionFile aarFile:" + aarFile)
tasks.getByName(taskName).doFirst {
addVersionFile(aarFile)
}
tasks.getByName(taskName).doLast {
println("deleteVersionFile for task:" + taskName)
deleteVersionFile()
}
}
tasks.withType(PublishToMavenRepository) {
onlyIf {
println("PublishToMavenRepository publication.name " + publication.name)
publication.name.contains('Release')
}
doFirst {
'git pull --rebase'.execute()
def status = 'git status'.execute().text.trim()
if (!status.contains('working tree clean')) {
throw new RuntimeException('check your code is sync with gerrit!')
}
}
}
tasks.withType(PublishToMavenLocal) {
onlyIf {
println("PublishToMavenLocal publication.name " + publication.name)
publication.name.contains('Debug')
}
}
}
通过这种方式我们增加了一些优化
-
通过配置ext信息,简化了使用的难度,通过添加仓库,自动增加发布到不同仓库的任务
-
增加对Android Gradle的ProductFlavor的支持,增加针对不同的ProductFlavor的配置构建发布不同的发布任务
基于一下的android配置
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
flavorDimensions 'platform', 'model'
productFlavors {
x50 {
dimension 'model'
}
x60 {
dimension 'model'
}
mtk {
dimension 'platform'
}
qcom {
dimension 'platform'
}
}
我们可以看到生成了对应的任务,我们通过gradle运行对应的任务就可以执行不同的发布
![](https://img.haomeiwen.com/i11240087/2b4a1736ed54b2e6.png)
添加外部依赖的POM信息
pom.withXml {
// def addNode = { rootNode, list ->
// list.each {
// if (null != it.group) {
// def dependency = rootNode.appendNode('dependency')
// dependency.appendNode('groupId', 'com.vivo.vivo3rdalgointerface')
// dependency.appendNode('artifactId', 'VivoAlgoSDK')
// dependency.appendNode('version', 'common_1.0.1.1')
// dependency.appendNode('scope', 'compile')
// }
// }
// }
// def rootNode = asNode().appendNode('dependencies')
// // 查询所有 implementation, api, compile 依赖
// def implementations = configurations.findByName("implementation").allDependencies
// // 查询所有 variantNameImplementation, api, compile 依赖
// def variantNameImplementations = configurations.findByName("${variantName}Implementation").allDependencies
//
// // 遍历添加依赖节点
// addNode(rootNode, implementations)
// addNode(rootNode, variantNameImplementations)
// 创建根节点
def rootNode = asNode().appendNode('dependencies')
def dependency = rootNode.appendNode('dependency')
dependency.appendNode('groupId', project.ext.algo_groupId)
dependency.appendNode('artifactId', project.ext.algo_artifactId)
dependency.appendNode('version', project.ext.algo_version)
dependency.appendNode('scope', 'compile')
}
分组
考虑发布任务混在一起容易不小心点击出错,我们可以增加分组
//保存任务返回结果
def pubToMyM1Tasks = tasks.withType(PublishToMavenRepository) {
onlyIf {
println("PublishToMavenRepository publication.name " + publication.name)
publication.name.contains('Release')
}
doFirst {
'git pull --rebase'.execute()
def status = 'git status'.execute().text.trim()
if (!status.contains('working tree clean')) {
throw new RuntimeException('check your code is sync with gerrit!')
}
}
}
def pubToLocalTasks = tasks.withType(PublishToMavenLocal) {
onlyIf {
println("PublishToMavenLocal publication.name " + publication.name)
publication.name.contains('Debug')
}
}
pubToVivoTasks.all {
if(publication.name.contains('Release')){
setGroup('publishTo-MyM1')
}
}
pubToLocalTasks.all {
if(publication.name.contains('Debug')){
setGroup('publishTo-Local')
}
}
Classifier
分类
我们依赖配置的信息如下:
configurationName "group:name:version:classifier@extension"
那么我们publish的时候通过配置classifier那么就可以在引用的时候通过classifier@extension
引入,同时在生成的文件也添加了对应的
后缀-[classifier].extension
如下:
artifact tasks.getByName(taskName) {
classifier android
extension project.ext.pom['packaging']
}
pom {
name = project.ext.pom['name']
description = project.ext.pom['description']
}
aar发布
首先aar项目build.gradle引入upload.gradle
apply from: 'upload.gradle'
后续可以把upload.gradle做成在线插件引入
在根目录配置发布信息
ext {
baseName = "XXXLib"
groupId = "com.example"
artifactId = "xxxlib"
version = '1.0.6'
abbreviatedCommitId = ''
fullCommitId = ''
pom = [name : "XXXLib",
packaging : "aar",
description: "XXXLib for all normal android usage"]
repository = [
[name: 'MyM1', url: 'https://com.example.org/maven1/'],
[name: 'MyM2', url: 'https://com.example.org/maven2/'],
]
}
ext字段配置信息是对应于dependencies
的依赖项
即
configurationName "group:name:version:classifier@extension"
例如
implementation 'com.vivo.camera:VCameraSDK:1.0.6@aar'
-
baseName
AAR工程项目名
-
groupId
AAR包组ID
-
artifactId
发布的工件ID
-
version
AAR包版本号
-
pom中的name
AAR包的名字,一般可以配置成和baseName一样
-
repository
需要发布的仓库,添加多个仓库项即可以发布到多个仓库
配置之后,通过
implementation groupId:artifactId:version@extension 引入发布的AAR包
这里我们会发现aar包的名字其实不对定位依赖起作用
如果当前的VCameraSDK分支添加了不同项目的支持,例如添加了pd1969和pd2001,可以在AAR模块的build.gradle
android {
.............
.............
}
扩展中添加
flavorDimensions 'product'
productFlavors {
x88 {
dimension 'product'
}
x99 {
dimension 'product'
}
}
app集成
基于buildType和productFlavor的配置和Gradle的依赖管理,我们就可以在项目集成中做到
-
根据项目依赖不同的sdk
if (project.ext.product.contains('x88')) { implementation 'com.example.xxxLib:x88-1.0.6' } if (project.ext.product.contains('x99')) { implementation 'com.example.xxxLib:x88-1.0.6' }
-
根据是否调试版本依赖本地发布的调试sdk
适用于SDK开发团队自己调试VCameraSDK
project.afterEvaluate { android.applicationVariants.all { variant-> if(variant.buildType.name == 'debug') { project.dependencies { implementation('com.example.xxxLib:x88-1.0.6') } } } }
或者
dependencies { if (project.ext.debug) { implementation('com.example.xxxLib:x88-1.0.6') } }
-
设置sdk的版本范围
例如AAR版本规则为
X.Y.Z
, 那么1.0.+
只取1.0.
最新的版本,这样不会取到1.1.
的版本,进一步降低取最新版本的风险if (project.ext.product.contains('x88')) { implementation 'com.example.xxxLib:x88-1.0.+' } if (project.ext.product.contains('x99')) { implementation 'com.example.xxxLib:x99-1.0.+' }