Function is introduced
The previous project business was very large, divided into many modules, each module is made into a library upload warehouse. That way, every time you develop a new business, it’s exhausting to switch branches.
So the big guy in the group made some scripts, some shell, some Python, but it was a headache to learn more than one language, so I thought I could use Gradle to write a plug-in. Well, I spent a lot of time studying Gradle to get out of shell and Python. Lol.
The main function of this plug-in is to pull multiple repositories from different remote locations to the current project. When switching branches later, just change the branch name. The specific usage is as follows
gitClone {
// Many projects have more than one remote repository. Here you can set the domain name of the repository
gitStore("https://gitee.com") {
// Configure the items in the repository
gitProject(
// Project link
url = "jaso_chen.com/camera-study".// Change the project branch
branch = "master".// Save path
saveDir = buildDir.absolutePath + "/testDir".// Whether to rename the folder
rename = "CameraStudy")
gitProject(
url = "jaso_chen.com/camera-study",
branch = "testBranch",
saveDir = buildDir.absolutePath + "/testDir",
rename = "CameraStudyTest")}}Copy the code
Implementation steps
- To create a
Java-Module
, the package,buildSrc
Only this name can be recognized as a plug-in
I also do not know why must this name, also be baidu arrive, the official also is with this for example, at that time toss about for a long time all fast collapse
- configuration
build.gradle.kts
Because it is usedkotlin
Written plug-in, so convert tokts
The important thing to note here is that you must addimplementation(gradleApi())
Otherwise, Gradle classes will not be called and the rest of the configuration should be the same as here
plugins {
java
kotlin("jvm") version ("1.6.10")
}
group = "org.example"
version = "1.0 the SNAPSHOT"
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
java {
targetCompatibility = JavaVersion.VERSION_1_8
sourceCompatibility = JavaVersion.VERSION_1_8
}
dependencies {
// Add gradle-related apis, otherwise you cannot customize plugins and tasks
implementation(gradleApi())
implementation(kotlin("stdlib"))
implementation(kotlin("stdlib-jdk8"))
implementation("Org. Jetbrains. Kotlinx: kotlinx coroutines -- core: 1.5.1." ")}Copy the code
- Find our class and start writing code
If you didn’t modify the main class when you created the project, you don’t have it. You can create one yourself
- To carry on
Plugin
This class, let me rewrite itapply()
methods
class GitPlugin : Plugin<Project> {
override fun apply(target :Project) {
// This is the entrance to the plugin. When Gradle introduces the plugin, this method is executed during the configuration phase}}Copy the code
-
We need to do a plugin to pull multiple repositories at the same time, but no matter what plugin, the first step is to collect data
But how do we collect data when our code is written in this class? Gradle’s Project provides us with a list of extended properties to which we can add methods and variables that we provide for Gradle calls
class GitPlugin : Plugin<Project> {
override fun apply(target :Project) {
// Extend gradle with a method that can be passed to us in the build.gradle file
// My idea is to provide a DSL with regular arguments, so I pass in a class so we can write a DSL
// Note here that if an object is passed in, the object must be open decorated, otherwise Gradle cannot construct it
target.extensions.create("gitClone", GitScope::class.java)
}
}
Copy the code
After the extension is defined above, it can be called in build.gradle
//build.gradle
gitClone { //this = GitScope
}
Copy the code
- You need to define
DSL
To collect data, first define the relevant classes
// Used to define a DSL
open class GitScope {
internal val stores = arrayListOf<GitStore>()
}
// Used to define a DSL
data class GitStore(val host: String) {
internal val projects = arrayListOf<GitProject>()
}
// Used to receive data
data class GitProject(
// Project link
val url: String,
// Change the project branch
val branch: String = "".// Save path
val saveDir: String,
// Whether to rename the folder
val rename: String = ""
)
Copy the code
- Define some
DSL
andgit
Execute the command, due to more functions, here only part of the code, details can be seen in the project
fun scope(scope: GitScope. () - >Unit) {
val gitScope = GitScope()
gitScope.scope()
}
fun GitScope.gitStore(host: String, scope: GitStore. () - >Unit) {
val store = GitStore(host)
store.scope()
this.stores.add(store)
}
fun GitStore.gitProject(url: String, branch: String = "", saveDir: String, rename: String = "") {
val project = GitProject(url, branch, saveDir, rename)
this.projects.add(project)
}
/ / clone warehouse
internal fun gitClone(repoUrl: String, dir: String) {
"git clone $repoUrl $dir".exeCommand()
}
// Switch branches
internal fun gitCheckout(branch: String, dir: String) {
"git checkout -b $branch origin/$branch".exeCommand(dir)
}
Copy the code
-
Build. Gradle = build.gradle = build.gradle = build.gradle = build.gradle = build.gradle = build.gradle
In fact, it is very simple, how to provide out, how to get back. Through the project. Extensions. GetByName (” gitClone “) can get to the us for to preach the object.
// Some code is not complete
private fun gitCloneTask(task: Task) = runBlocking {
// Coroutines are used here to allow multiple repositories to pull at the same time, and more importantly to wait for all tasks to complete
// And we define a single, independent task that exits almost instantaneously, so we need coroutines, also for learning coroutines
coroutineScope.launch {
// Get the data we collected through extensions
val gitScope = task.project.extensions.getByName("gitClone") as GitScope
// Walk through each warehouse
for (store in gitScope.stores) {
cloneStoreTask(store)
}
}.join()
}
private fun CoroutineScope.cloneStoreTask(store: GitStore) {
// Walk through each item in the warehouse
for (project in store.projects) {
async {
// The slashes used in the command are either double slashes \\ or backslashes /
gitClone("${store.host}/${project.url}", project.branch,
"${project.saveDir.replace("\ \"."/")}/${project.rename}")}}}Copy the code
-
Now that the feature is written, when will it be implemented? My idea is to provide a Task and wait for the developer to click on it, or to enter a list of commands to execute it. Then you need to define a Task for the Gradle that currently introduces the plugin
The Apply () method takes the Project object and creates a task directly in the Tasks list
override fun apply(target: Project) {
// Configure git extensions
target.extensions.create("gitClone", GitScope::class.java)
// Define the gitClone task
target.tasks.create("gitClone").apply {
// Define grouping, easy to find
group = "git"
// Describe the functionality appropriately
description = "For managing multiple warehouses initializing, switching branches"
// Execute our function after the task is executed
doLast(this@GitPlugin::gitCloneTask)
}
}
Copy the code
By default, tasks are run in the configuration phase, i.e. “clean”, “sync gradle” tasks are executed, to avoid triggering each time, we write the code to run separately
- The function of the plug-in has been written, how to use other modules?
implementation
Well, of course not. Plugins have plugin-dependent ways, which we’ve all seen inplugins{}
But how? This requires us to make a registration declaration for the plug-in we write
META-INF/gradle-plugin
implementation-class=com.chenchen.plugin.git.GitPlugin
- Now that our plugin is ready, how do we import and configure it? Open any one
build.gradle
I picked it hereapp/build.gradle
Since our plugin is written in Kotlin, it contains some Kotlin features that I don’t know how to use in Groovy, so I changed build.gradle. KTS
//app/build.gradle.kts
plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
// Add a plug-in
id("GitPlugin")}/ /... Omit a large chunk of code
// when KTS calls the class, it needs to guide the package
import com.chenchen.plugin.git.*
// This is the extension method that we provide in the plugin, and the specific way to use it is as follows
gitClone {
// Many projects have more than one remote repository. Here you can set the domain name of the repository
gitStore("https://gitee.com") {
// Configure the items in the repository
gitProject(
// Project link
url = "jaso_chen.com/camera-study".// Change the project branch
branch = "master".// Save path
saveDir = buildDir.absolutePath + "/testDir".// Whether to rename the folder
rename = "CameraStudy")
gitProject(
url = "jaso_chen.com/camera-study",
branch = "testBranch",
saveDir = buildDir.absolutePath + "/testDir",
rename = "CameraStudyTest")}}Copy the code
- After you have written the configuration, click
sync project with gradle files
Sync it up and we’ll be on the rightgradle
I found it on the task list. I was inapp/build.gradle.kts
Introducing plug-ins. That’s inapp
You can find it in the modulegit/gitClone
This task
This is the end of the custom plug-in approach. This step is quite elementary, there is still a lot to improve, but the beginner is enough, it seems very simple, in fact, I spent more than 20 hours to complete, encountered various compilation failed, dependency problems, Gradle error can not understand, and so on
In a word, Gradle is very complex, but also very useful, learn a little fur can save a lot of time, more fish is not good!!
The above features are not satisfactory:
- Can’t give
Settings.gradle
Dependencies, I want to automatically help these libraries while pulling codeinclude
Get in there. Take a long time. Feel like I can’t do it.
- Can only give
build.gradle
This level of imported plug-ins, although not much configuration content, is too bloated to be placed in a separate file to configure
If there is a big boss understand, or later strength rose, there is a solution to continue to improve.