如何编写Gradle插件以简化build.gradle文件。

huangapple go评论59阅读模式
英文:

How to write Gradle plugin to cut down build.gradle file

问题

以下是您提供的文本的翻译部分:

A bit of Context first:
首先一些背景信息:
I am working on migrating my companies projects to be built by Gradle.
我正在进行将我的公司的项目迁移到使用Gradle构建的工作。
One thing, that this results in, is having redundancy in my build.gradle files,
其中的一个结果是,在我的build.gradle文件中存在冗余,
as I am configuring the same Skeleton over and over again.
因为我一遍又一遍地配置相同的框架。
This includes:
这包括:

  • Setting the java-,maven-publish- and org.sonarcube-plugin
  • 设置java,maven-publish和org.sonarcube-plugin
  • Configuring the repositories to be mavenCentral and our private Artifactory Repo
  • 配置存储库为mavenCentral和我们的私有Artifactory Repo
  • Configuring the publishing block, that is all the same, except for the artifactId
  • 配置发布块,除了artifactId之外都相同
  • Building a Manifest inside the Jar block (using helper Methods, to correctly build the Manifests classpath)
  • 在Jar块内构建清单(使用辅助方法来正确构建清单的类路径)
  • Helper Methods
  • 辅助方法
  • two Tasks
  • 两个任务
  • two dependsOn statements
  • 两个dependsOn语句

build.gradle file as of now:
截止目前的build.gradle文件:

(接下来是代码部分,无需翻译)

what I want to achieve:
我想要实现的目标:

(接下来是代码部分,无需翻译)

Most of the values are the same.
大多数值都是相同的。
The ones that aren't are passed in via Environment-Variables (Jenkins-JobName,...),
那些不同的值是通过环境变量传递的(例如Jenkins-JobName,...),
or get determined through helper Methods.
或者通过辅助方法确定。
I reckon, that I will most likely not end up with a buildfile like the one above,
我估计,最终我不太可能得到像上面那样的构建文件,
but at least some of the buildfile must be outsourceable.
但至少有些构建文件必须可以外包。

I know as of now, that I can create separate Tasks in a plugin, like comparing two files, that have been passed. What I didn't find a solution to yet:
截止目前,我知道我可以在插件中创建单独的任务,比如比较传递的两个文件。但我还没有找到解决方案的问题:

  • Can I modify the Jar Task of the project applying the plugin, inside the plugin?
  • 我可以在插件内部修改应用插件的项目的Jar任务吗?
  • How do I pass Outputs from other Tasks into my plugin's tasks?
  • 如何将其他任务的输出传递给我的插件任务?
  • How do I access the applying project's data (i.e. the runtimeClasspath)
  • 如何访问应用项目的数据(例如runtimeClasspath)
  • Is a plugin even what I want to do, or is there another way of cutting down the build.gradle file?
  • 插件甚至是否是我想要做的事情,或者还有其他方式来简化build.gradle文件?
    I am relatively unexperienced with Gradle. I have read through quite a bit of the docs and other postings, but chances are I just overlooked some best-practice way of doing certain things. Therefore, feel free to criticize my build file as well as my approach!
    我对Gradle相对不熟悉。我已经阅读了相当多的文档和其他帖子,但很可能我只是忽视了某些最佳实践方法。因此,随时批评我的构建文件以及我的方法!
英文:

A bit of Context first:
I am working on migrating my companies projects to be built by Gradle.
One thing, that this results in, is having redundancy in my build.gradle files,
as I am configuring the same Skeleton over and over again.
This includes:

  • Setting the java-,maven-publish- and org.sonarcube-plugin
  • Configuring the repositories to be mavenCentral and our private Artifactory Repo
  • Configuring the publishing block, that is all the same, except for the artifactId
  • Building a Manifest inside the Jar block (using helper Methods, to correctly build the Manifests classpath)
  • Helper Methods
  • two Tasks
  • two dependsOn statements

build.gradle file as of now:

plugins {
	id 'io.spring.dependency-management' version '1.0.12.RELEASE'
	id "org.sonarqube" version "3.2.0"
	id 'maven-publish'
	id 'java' 
}

group = 'group'
version = 'version'
sourceCompatibility = '11'
ext.artifactName = 'ProjectName'

// Where to look for dependencies:
repositories {
	mavenCentral()
	maven{
		credentials{
			username = "${artifactory_user}"
			password = "${artifactory_password}"
		}
		url "${artifactory_contextUrl}"
		allowInsecureProtocol = true
	}
}
// Where to publish what Artifacts to:
publishing {
	publications {
        maven(MavenPublication) {
            groupId = 'group'
            artifactId = 'ProjectName'
			String buildEnvVar = System.env.BUILDENVIRONMENT
			if(buildEnvVar == null){
				version = 'LOCAL BUILD'
			}else{
				version = 'version'
			}
            from components.java
        }
    }
    repositories {
        maven {
            // change to point to your repo, e.g. http://my.org/repo
            name = "gradle-dev"
            url = "${artifactory_contextUrl}"
            allowInsecureProtocol = true
            credentials{
            	username = "${artifactory_user}"
            	password = "${artifactory_password}"
            }
        }
    }
}

dependencies {...}

jar { 
	// configuration of variables
	String dateString = new Date().format("yyyy-MM-dd HH:mm:ss ")
	String localBuild = "LOCAL BUILD by " + System.getProperty("user.name") + " on " + dateString
	String buildEnvVar = System.env.BUILDENVIRONMENT
	String buildEnvironment
	String classpath = createCP()
	if(buildEnvVar == null){
		buildEnvironment = localBuild 
		archiveName = "ProjectName"
	}else{
		buildEnvironment = buildEnvVar
		archiveFileName= "ProjectName_" + version + ".jar"
		
		delete fileTree("build/libs") {
			include('*')
		}
	}
	manifest {
		attributes (
			"Main-Class": "org.example.foo",
			"Specification-Title" : "ProjectName",
			"Specification-Vendor" : "blab",
			"Specification-Version" : "Spec-version", 
			"Implementation-Title" : "$System.env.JOB_NAME",
			"Implementation-Version" : "Impl-version",
			"Implementation-Vendor" : "blub",
			"Implementation-Vendor-Id" : "blob",
			"Implementation-Url" : "bleb",
			"Build-By" : buildEnvironment,
			'Class-Path': classpath
		)
	}
}

String createCP () {
	// super secret can't share
}

// will suffix the jars with release or debug, depending on it being compiled with or without debug-information
project.gradle.taskGraph.whenReady{
	boolean isDebug = project.gradle.taskGraph.getAllTasks().join(' ').contains('debugJar')
	compileJava.options.debug = isDebug
	String suffix = isDebug? "debug" : "release"
	String fullJarName = "$artifactName-$suffix" + ".jar"
	
	jar.setProperty('archiveName', fullJarName)
}

tasks.named('test') {
	useJUnitPlatform()
}

task debugJar() {}
debugJar.dependsOn(jar)

//Downloads all Jars the project depends on, and saves them in buildDirectory/output/libs if the gradle build command is executed.

 task copyToLib(type: Copy) {
    into "${buildDir}/output/libs"
    from configurations.runtimeClasspath
}

build.dependsOn(copyToLib)

what I want to achive:

plugins {
    id 'io.spring.dependency-management' version '1.0.12.RELEASE'
    id "org.sonarqube" version "3.2.0"
    id 'maven-publish'
    id 'java'
    id 'mySuperPlugin'
}

// Configure mySuperPlugin
mySuperPlugin {
    artifactId = 'xyz'
    mainClass = 'org.example.foo'
    version = 'version'
    stuffFromOtherTasks = ...
}

// Where to look for dependencies:
repositories {
	mavenCentral()
	maven{
		credentials{
			username = "${artifactory_user}"
			password = "${artifactory_password}"
		}
		url "${artifactory_contextUrl}"
		allowInsecureProtocol = true
	}
}

dependencies {...}

Most of the values are the same.
The ones that aren't are passed in via Environment-Variables (Jenkins-JobName,...),
or get determined through helper Methods.
I reckon, that i will most likely not end up with a buildfile like the one above,
but atleast some of the buildfile must be outsourceable.

I know as of now, that i can create seperate Tasks in a plugin, like comparing two files, that have been passed. What I didn't find a solution to yet:

  • Can I modify the Jar Task of the project applying the plugin, inside the plugin?
  • How do I pass Outputs from other Tasks into my plugins tasks?
  • How do I access the applying projects data (i.e. the runtimeClasspath)
  • Is a plugin even what i want to do, or is there another way of cutting down the build.gradle file?

I am relatively unexperienced with gradle. I have read through quite a bit of the docs and other postings, but chances are i just overlooked some best-practice way of doing certain things.
Therefore, feel free to criticize my buildfile aswell as my approach!

答案1

得分: 0

您可以以几种方式完成这个任务。这取决于您的项目是否由多个子项目组成,或者它们是独立的项目。对于独立的项目,您可以在您的settings.gradle文件中执行以下操作:

includeBuild("../common-project/build.gradle")

通用项目将只包含通用的build.gradle文件,并且您可以在其中声明要共享的所有项目。它看起来像一个普通的build.gradle文件。

这将要求每个项目共享另一个项目的通用配置,但不需要检出任何额外的项目,只需检出通用项目和项目本身。有关更多详情,请参见:

https://docs.gradle.org/current/userguide/composite_builds.html

如果您有更多的多项目构建,比如由多个微服务或子项目组成的较大项目,那么可以使用多项目设置,并在根build.gradle中的allprojects闭包中声明通用部分:

allprojects {
    plugin: java
    plugin: web

    repositories {
      ....
    }

    dependencies {
       ....
    }
}

在多项目的情况下,您需要检出顶层项目和所有子项目。您的文件夹布局可能如下所示:

- my-project
  - service-1
      src
      build.gradle
  - service-2
      src
      build.gradle
  - service-3
      src
      build.gradle
    build.gradle
    settings.gradle

在多项目设置中,service1、service2 和 service3 将在 settings.gradle 文件中使用 include 进行声明,如下所示:

rootProject.name = 'my-project'
include 'service1'
include 'service2'
include 'service3'

在多项目设置中,通常将其存储在单个源代码仓库中,而不是使用includeBuild,其中每个项目将属于单独的源代码仓库。前一种方式会强制您在多项目的情况下检出适当数量的项目。但在includeBuild的情况下,开发人员必须知道要检出至少 2 个项目。

英文:

You can do this in a couple of ways. And this comes down to if your project is composed of multiple sub-projects or if they are stand alone projects. For stand alone projects you can do the following in your settings.gradle file:

includeBuild("../common-project/build.gradle")

The common project would just house the common build.gradle file, and you'd declare all of the items you want to share in there. It would look like a normal build.gradle file.

That would require that each project share the common configuration from another project, but wouldn't require any additional projects be checked out. Just the common project and the project itself. For more details see:

https://docs.gradle.org/current/userguide/composite_builds.html

If you have more of a multi-project build like say many micro-services or subprojects that make up a larger project then using multi-project setup and declare the common pieces in the allprojects closure in the root build.gradle:

allprojects {
    plugin: java
    plugin: web

    repositories {
      ....
    }

    dependencies {
       ....
    }
}

In the multi-project case you'd have to check out the top level project and all subprojects. Your folder layout might look like this:

- my-project
  - service-1
      src
      build.gradle
  - service-2
      src
      build.gradle
  - service-3
      src
      build.gradle
    build.gradle
    settings.gradle

In the multi-project setup service1, service2, and service3 would be declared in the settings.gradle file using include like so:

rootProject.name = 'my-project'
include `service1`
include `service2`
include `service3`

In a multi-project setup you'd typically house that in a single source repository as oppose to using includeBuild where each project would belong to a separate source code repo. The former way forces you to checkout the appropriate number of projects in the multi-project case. But, in the includeBuild case the developer would have to know to check out minimum of 2 projects.

huangapple
  • 本文由 发表于 2023年2月9日 02:04:32
  • 转载请务必保留本文链接:https://go.coder-hub.com/75389963.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定