1. 程式人生 > >釋出到Maven倉庫

釋出到Maven倉庫

原文地址: ,基於Gradle Build Tool 3.4.1的,後期可能會修改。文章最後面寫了一下自己的總結。

譯文

本章描述的是通過”maven-publish”外掛來支援釋出到Maven功能。最終這種新的釋出方式會替換掉通過Upload task的釋出方式。

本章描述怎樣釋出構建的內容(artifacts)到Apache Maven倉庫。一個模組釋出到Maven倉庫以後可以供MavenGradle以及其它支援Maven倉庫格式的工具使用。

36.1. “maven-publish” 外掛

“maven-publish” 外掛提供了釋出Maven格式的功能。

“publishing” 外掛建立一個型別為PublishingExtension

名稱為publishing的引數,這個引數裡面提供了publications容器和repositories容器。”maven-publish” 依賴的是MavenPublicationMavenArtifactRepository。(意思是”maven-publish”是在”publishing”上再一次的封裝)

應用”maven-publish”外掛的例子。

// build.gradle
apply plugin: 'maven-publish'

應用”Maven Plugin”需要做如下事情:

  • 使用”publishing”外掛 (沒看明白,覺得意思就是需要apply plugin: 'maven-publish'
    )
  • 建立一個規則自動為每一個MavenPublication建立GenerateMavenPom任務
  • 建立一個規則自動的為每一個MavenPublicationMavenArtifactRepository的組合建立一個PublishToMavenRepository任務
  • 建立一個規則自動的為每一個MavenPublication建立PublishToMavenLocal任務

PS:這裡說的似乎有點繞,MavenPublication相當於是需要釋出的內容,而MavenArtifactRepository則相當於是釋出的倉庫。”maven-pluign”主要是做三件事:GenerateMavenPom

可以用來生成pom檔案;PublishToMavenRepository可以用來發布到指定倉庫,本地某個路徑或者遠端的伺服器;PublishToMavenLocal則是用來發布到本地的.m2倉庫(例如我自己電腦的地址是/Users/Egos/.m2)

36.2. Publications

Publication物件描述的是一次釋出的結構和配置。Publications通過任務釋出到倉庫,Publication物件的配置則精確地確定哪些內容需要釋出。所有的釋出配置資訊定義在PublishingExtension.getPublications()容器中,在一個專案中每一個釋出到都有一個唯一的名字。

對於”maven-publish”外掛有哪些影響,需要建立一個MavenPublication,這個釋出決定了哪些內容需要釋出以及包含在POM檔案中的詳細資訊。釋出時可以通過增加模組、自定義構建的內容以及修改生成的POM檔案來配置。

36.2.1. 釋出一個軟體模組

最簡單的釋出一個Gradle專案到Maven倉庫的方法就是指定一個軟體模版去釋出。目前已經支援的模版有。

Name Provided By Artifacts Dependencies
java The Java Plugin Generated jar file Dependencies from ‘runtime’ configuration
web The War Plugin Generated war file No dependencies

下面的例子,構建內容和執行的依賴都來自在Java外掛中定義的java資訊。

// build.gradle
publishing {
    publications {
        mavenJava(MavenPublication) {
            from components.java
        }
    }
}

PS:什麼意思呢?上面一句from components.java代表了使用預設的配置,從上表可以看出會生成jar檔案,同時需要執行時的一些依賴。

36.2.2. 釋出一個自定義構建內容

可以通過配置artifact明確的指定需要生成的內容。通常會提供原始的檔案或者AbstractArchiveTask對應的例項(例如:Jar,Zip等等)。

對於每一個自定義的構建內容,可以指定副檔名和分類資訊(classifier)。注意只有一個釋出的構建內容可以擁有空的分類資訊(classifier),並且所有的構建資訊必須擁有唯一的分類資訊(classifier)和副檔名的組合。

如下配置自定義的構建內容:

// build.gradle
task sourceJar(type: Jar) {
    from sourceSets.main.allJava
}

publishing {
    publications {
        mavenJava(MavenPublication) {
            from components.java

            artifact sourceJar {
                classifier "sources"
            }
        }
    }
}

PS:什麼意思呢? 外掛自身可以支援釋出出去生成的檔案是jarwar,但是這並不能滿足所有的情況,所以可以自定義構建內容,比如:aar等等。怎麼自定義呢?建立相應的任務以及在publications中使用artifact就可以了。

36.2.3. 在生成的POM中區別值

生成的POM檔案的值包含了如下的屬性:

  • groupId Project.getGroup()
  • artifactId Project.getName()
  • version Project.getVersion()

覆蓋預設的標識屬性非常的簡單:只需要在MavenPublication配置時指定groupId,artifactId,version就行。

// build.gradle
publishing {
    publications {
        maven(MavenPublication) {
            groupId 'org.gradle.sample'
            artifactId 'project1-sample'
            version '1.1'

            from components.java
        }
    }
}

Maven限制了groupIdartifactId在一個有限的字符集([A-Za-z0-9_\\-.]+),並且Gradle也遵循這樣的限制。對於version(以及構建內容中的extensionclassifier),只需要是有效的Unicode字元就行。唯一明確禁止的Unicode字元是”\”,”/”和ISO控制字元。會在publication之前對字元進行校驗。

36.2.4. 修改生成的POM檔案

生成的POM檔案可能在釋出之前需要修改。”maven-publish”外掛提供了一個hook來允許這樣的修改。

// build.gradle
publications {
    mavenCustom(MavenPublication) {
        pom.withXml {
            asNode().appendNode('description',
                                'A demonstration of maven POM customization')
        }
    }
}

上面例子在生成的POM檔案中增加了一個description元素。使用hook可以修改POM中任意的元素。例如:你可以將依賴關係的版本範圍替換成生成構建的實際版本。

可以修改POM中的任意元素,這也意味著可以將POM修改成非法的POM,所以需要小心的使用這個功能。

釋出模組的唯一標識屬性(groupId,artifactId,version)是一個例外,這個元素不能通過withXMLhook來修改。

36.2.5. 釋出多個模組

有時候使用Gradle不建立多個子專案釋出多個模組是非常有用的。

下面的例子是在一個專案中使用Gradle釋出多個模組。

task apiJar(type: Jar) {
    baseName "publishing-api"
    from sourceSets.main.output
    exclude '**/impl/**'
}

publishing {
    publications {
        impl(MavenPublication) {
            groupId 'org.gradle.sample.impl'
            artifactId 'project2-impl'
            version '2.3'

            from components.java
        }
        api(MavenPublication) {
            groupId 'org.gradle.sample'
            artifactId 'project2-api'
            version '2'

            artifact apiJar
        }
    }
}

如果一個工程定義了多個釋出資訊,Gradle會將每一個釋出資訊釋出到指定的倉庫。每一個釋出需要給定一個唯一的標示。

36.3. Repositories

釋出資訊(Publications)最終會發布到倉庫,釋出的倉庫資訊定義在PublishingExtension.getRepositories()中。

下面是定義一個釋出的倉庫

// build.gradle
publishing {
    repositories {
        maven {
            // change to point to your repo, e.g. http://my.org/repo
            url "$buildDir/repo"
        }
    }
}

The DSL used to declare repositories for publication is the same DSL that is used to declare repositories to consume dependencies from, RepositoryHandler. However, in the context of Maven publication only MavenArtifactRepository repositories can be used for publication.

上面一句是對於Repositories的描述,理解不了。自己理解的意思是使用Maven publication只能釋出在Maven倉庫中。

36.4. 釋出到maven倉庫的一個例子

“maven-publish”外掛會自動的為每一個各自宣告在publishing.publicationsMavenPublication和宣告在publishing.repositoriesMavenArtifactRepository的組合建立一個PublishToMavenRepository任務。

建立的任務的名字格式是publish«PUBNAME»PublicationTo«REPONAME»Repository

// build.gradle
apply plugin: 'java'
apply plugin: 'maven-publish'

group = 'org.gradle.sample'
version = '1.0'

publishing {
    publications {
        mavenJava(MavenPublication) {
            from components.java
        }
    }
}
publishing {
    repositories {
        maven {
            // change to point to your repo, e.g. http://my.org/repo
            url "$buildDir/repo"
        }
    }
}
// Output of gradle publish
> gradle publish
:generatePomFileForMavenJavaPublication
:compileJava
:processResources NO-SOURCE
:classes
:jar
:publishMavenJavaPublicationToMavenRepository
:publish

BUILD SUCCESSFUL

Total time: 1 secs

在這個例子中,一個型別是PublishToMavenRepository,名稱是publishMavenJavaPublicationToMavenRepository的任務會被建立,這個任務會被連線到釋出任務的生命週期中。執行gradle publish會生成POM檔案以及生成要釋出的所有的構建內容,並將生成的構建內容傳輸到相應的倉庫。

36.5. 釋出到本地maven倉庫

為了於本地maven整合,有時候將模組釋出在本地的.m2倉庫是非常有用的。Maven稱之為“安裝”模組。”maven-publish”外掛會自動的為每一個宣告在publishing.publications的內容建立一個PublishToMavenLocal任務。每一個這樣的任務都會被連線到publishToMavenLocal的生命週期中。不需要在publishing.repositories中寫mavenLocal

建立的任務的名字格式是publish«PUBNAME»PublicationToMavenLocal

PS:網上很多的開源外掛有一些沒有釋出到本地的方法,對於一些需要修改程式碼進行除錯等,釋出到本地還是非常方便的。

36.6. 不釋出的情況下生成pom檔案

有時候在不釋出的情況下修改POM檔案是非常有用的,因為POM檔案的生成是一個單獨的任務,所以非常容易實現。

生成POM檔案的任務型別是GenerateMavenPom,名稱是基於釋出任務generatePomFileFor«PUBNAME»Publication。因此在下面的例子中,釋出的名稱是mavenCustom,所以生成POM檔案的名稱是generatePomFileForMavenCustomPublication

// 不釋出的情況下生成POM檔案
model {
    tasks.generatePomFileForMavenCustomPublication {
        destination = file("$buildDir/generated-pom.xml")
    }
}
> gradle generatePomFileForMavenCustomPublication
:generatePomFileForMavenCustomPublication

BUILD SUCCESSFUL

Total time: 1 secs

打包android library到本地的例子

// android-artifacts.gradle
apply plugin: "maven-publish"

task androidJavadocs(type: Javadoc) {
    source = android.sourceSets.main.java.srcDirs
    ext.androidJar = "${android.sdkDirectory}/platforms/${android.compileSdkVersion}/android.jar"
    classpath += files(ext.androidJar)
}

task androidJavadocsJar(type: Jar, dependsOn: androidJavadocs) {
    classifier = 'javadoc'
    from androidJavadocs.destinationDir
}

afterEvaluate { project ->
    tasks.all { Task task ->
        if (task.name.equalsIgnoreCase('publishPatchLibPublicationToMavenLocal')) {
            task.dependsOn tasks.getByName('assemble')
        }
    }
}

publishing {
    publications {
        PatchLib(MavenPublication) {

            artifactId project.getName()
            groupId group
            version version

            // artifact 打成aar
            artifact("$buildDir/outputs/aar/${project.getName()}-release.aar")
            artifact androidJavadocsJar
        }
    }
}
// publish«PUBNAME»PublicationToMavenLocal
task pushlishLibToLocal(dependsOn: ['build', 'publish PatchLibPublicationToMavenLocal']) {
    group = 'patch'
}

使用的時候只需要在需要打包到本地的lib工程的build.gradle目錄中。

apply plugin: 'com.android.library'

...
version "1.0.0" // 需要一個version傳到android-artifacts.gradle,每一個需要打包的可能不一樣
group APP_PACKAGE_NAME  // 同上

apply from: file('../gradle/android-artifacts.gradle')

打包Java library到本地的例子

Java library 就會更簡單一點了,因為有一個模版,上面文章已經有介紹了。

// java-artifacts.gradle

apply plugin: 'maven-publish'

sourceCompatibility = JavaVersion.VERSION_1_7
targetCompatibility = JavaVersion.VERSION_1_7

afterEvaluate { project ->
    tasks.all { Task task ->
        if (task.name.equalsIgnoreCase('publishPatchLibPublicationToMavenLocal')) {
            task.dependsOn tasks.getByName('assemble')
        }
    }
}

publishing {
    publications {
        PatchLib(MavenPublication) {
            from components.java
            groupId = group
            artifactId = project.getName()
            version = VERSION_NAME_DEV
        }
    }
}

// 釋出在本地倉庫
// 會自動建立'publishXXXXXPublicationToMavenLocal'  'XXXXX' 是 publishing->publications-> PatchLib
task pushlishLibToLocal(dependsOn: ['build', 'publishPatchLibPublicationToMavenLocal']) {
    group = 'patch'
}

總結

將專案模組(Android LibraryJava Library以及Groovy Plugins等等)構建、打包發到Maven倉庫(包括本地的Maven倉庫)是一件很有意義的事情。對於公司來說,方便多個小組之間的呼叫、以及維護管理,同時也使程式碼看起來不會顯的過於臃腫,也避免了一些網路不好的情況(公司內網還是相對快一點)。這篇檔案介紹的’maven-publish’外掛是一個已經可以直接使用的外掛,個人覺得目前這個外掛的主要作用是將構建內容釋出在本地(.m2目錄)。這樣對於修改一些開源的框架還是蠻有作用的,以及將專案中的Library模組先發布在本地然後再依賴呼叫,也可以減少打包的時間。

對於需要打包釋出在遠端倉庫的,最好還是使用“maven”外掛“signing”外掛,當然還有可能釋出在ivy倉庫等,需要根據實際的需求來選擇。所有的釋出都可以在官方文件中找到。想要系統的學習Gradle外掛還是得花點時間看看userguide