我的答案是建立在模糊武器的答案之上的,因为我在看到它之前无法弄清楚,荣誉!
我的答案基于 Gradle DSL 语法。我希望有足够的细节来收集适用于 Kotlin DSL 的解决方案。
您可以通过创建带有项目对象参数的闭包来解决大多数此类问题,而不是将构建逻辑直接放入您应用的脚本中(尽管这显然适用于独立于项目的逻辑)。由于我不清楚的原因,在单独的脚本中创建新函数会导致 Project 对象参数参数始终为空。闭包的语法很接近,与Java lambda语法几乎相同,从调用者的角度来看,函数和闭包之间的语义基本相同(显然与外部脚本内部不同)。
我最近不得不解决一个类似的问题,我想将一些动态任务生成和清理逻辑提取到一个单独的脚本中。我就是这样解决的:
- 将逻辑移动到单独的文件中,在我的例子中,我在我的项目中将其命名为 env-config.gradle
$projectDir/gradle/env-config.gradle
- 将其包装在参数化闭包中。
- 修复您 (OP) 在您的问题中提出的错误。大多数这些问题都可以使用直接导入来解决,就像您在 Java、Kotlin 或直接在 Groovy 中所做的那样。这可能需要构建脚本依赖项,特别是对于二进制插件,您将在下面的完整示例中看到。
- 将闭包引用附加到扩展脚本插件对象。
- 使用项目对象作为参数从您提取逻辑的构建脚本中调用闭包。
这是一个基于我为解决该问题所做的简化示例,我在其中定义任务创建和任务逻辑,然后外部化对闭包的引用,以便可以从应用了该脚本的脚本中调用它:
在$projectDir/gradle/env-config.gradle:
def genTasks = { p ->
def previousServicePropsTask = null
deploymentPlatforms.each {
def deploymentPlatform = it
deploymentEnvironments.each {
def deploymentEnv = it
def propsTaskName = "xslt_${deploymentPlatform}_service_props_$deploymentEnv"
p.logger.info("Creating XSL Transform task: $propsTaskName")
def newServicePropsXslTask = p.tasks.create(
[name: propsTaskName, type: SaxonXsltTask], {
input "${p.projectDir}/dist/config/$deploymentPlatform/config.properties.xml"
stylesheet "${p.projectDir}/transforms/build/apply-env-specific-service-properties.xsl"
output "${p.projectDir}/config/$deploymentPlatform/$deploymentEnv/config.properties"
parameters(envSpecificServicePropsXslTransformParameters[deploymentPlatform][deploymentEnv])
}
)
if (previousServicePropsTask != null) {
newServicePropsXslTask.dependsOn(previousServicePropsTask)
}
previousServicePropsTask = newServicePropsXslTask
}
}
return previousServicePropsTask
}
//... other stuff
ext {
//Add closure to script plugin extension object
generateTransformTasks = genTasks
}
然后在项目构建脚本的适当范围和阶段调用它,在我的例子中,这是在配置阶段的自定义转换任务中:
在$projectDir/build.gradle:
apply from: "$projectDir/gradle/env-config.gradle"
task transformConfigurations() {
def taskDepends = generateTransformTasks(project)
dependsOn(taskDepends)
}
我发现这种分解构建脚本的方法非常灵活,基本上可以处理大多数想将逻辑分解成单独脚本的情况。
这是最终产品单独构建脚本的精简版本(实际上它甚至会在自定义脚本中应用不同的自定义脚本):
buildscript {
repositories {
mavenLocal()
}
dependencies {
classpath 'gradle.plugin.com.github.eerohele:saxon-gradle:0.8.0'
}
}
import com.github.eerohele.SaxonXsltTask
import java.nio.charset.StandardCharsets
import java.nio.file.Files
import java.nio.file.Paths
import java.util.Properties
def deploymentPlatforms = [
'platX'
]
def deploymentEnvironments = [
'dev',
'qa',
'prod'
]
def envSpecificLog4j2XslTransformParameters = [:]
def envSpecificServicePropsXslTransformParameters = [:]
//Have to use fully-qualified class name here, known issue in Gradle
apply plugin: com.github.eerohele.SaxonPlugin
deploymentPlatforms.each {
def plat = it
envSpecificLog4j2XslTransformParameters[plat] = [:]
envSpecificServicePropsXslTransformParameters[plat] = [:]
deploymentEnvironments.each {
Properties log4j2Props = new Properties()
Properties serviceProps = new Properties()
serviceProps.load(
Files.newBufferedReader(
Paths.get("$projectDir/dist/config/$plat/$it/config.properties"),
StandardCharsets.UTF_8
)
)
log4j2Props.load(
Files.newBufferedReader(
Paths.get("$projectDir/dist/config/$plat/$it/log4j2.xml.properties"),
StandardCharsets.UTF_8
)
)
envSpecificServicePropsXslTransformParameters[plat][it] = serviceProps
envSpecificLog4j2XslTransformParameters[plat][it] = log4j2Props
}
}
def genTasks = { p ->
def previousLog4j2Task = null
def previousServicePropsTask = null
deploymentPlatforms.each {
def deploymentPlatform = it
deploymentEnvironments.each {
def deploymentEnv = it
def propsTaskName = "xslt_${deploymentPlatform}_service_props_$deploymentEnv"
p.logger.info("Creating XSL Transform task: $propsTaskName")
def newServicePropsXslTask = p.tasks.create(
[name: propsTaskName, type: SaxonXsltTask], {
input "${p.projectDir}/dist/config/$deploymentPlatform/config.properties.xml"
stylesheet "${p.projectDir}/transforms/build/apply-env-specific-service-properties.xsl"
output "${p.projectDir}/config/$deploymentPlatform/$deploymentEnv/config.properties"
parameters(envSpecificServicePropsXslTransformParameters[deploymentPlatform][deploymentEnv])
}
)
def log4j2XmlTaskName = "xslt_${deploymentPlatform}_service_log4j2_$deploymentEnv"
p.logger.info("Creating XSL Transform task: $log4j2XmlTaskName")
def newLog4j2XslTask = p.tasks.create(
[name: log4j2XmlTaskName, type: SaxonXsltTask], {
input "${p.projectDir}/dist/config/$deploymentPlatform/log4j2.xml"
stylesheet "${p.projectDir}/transforms/build/apply-env-specific-service-logging.xsl"
output "${p.projectDir}/config/$deploymentPlatform/$deploymentEnv/log4j2.xml"
parameters(envSpecificLog4j2XslTransformParameters[deploymentPlatform][deploymentEnv])
}
)
if (previousServicePropsTask != null) {
newServicePropsXslTask.dependsOn(previousServicePropsTask)
}
if (previousLog4j2Task != null) {
newLog4j2XslTask.dependsOn(previousLog4j2Task)
}
previousServicePropsTask = newServicePropsXslTask
previousLog4j2Task = newLog4j2XslTask
}
}
return [
previousServicePropsTask: previousServicePropsTask,
previousLog4j2Task: previousLog4j2Task
]
}
def cleanupCustomTask = { p ->
def configsForRemoval = [:]
def dirsForRemoval = [:]
deploymentPlatforms.each {
def deploymentPlatform = it
def generatedPlatformDir = "${p.projectDir}/config/$deploymentPlatform"
deploymentEnvironments.each {
def deploymentEnv = it
def distributionName = "$deploymentPlatform${deploymentEnv.capitalize()}"
def generatedConfigDir = "$generatedPlatformDir/$deploymentEnv"
def generatedServicePropsConfig = "$generatedConfigDir/config.properties"
def generatedLog4j2Config = "$generatedConfigDir/lo4j2.xml"
def generatedDistNameDir = "${p.projectDir}/src/$distributionName"
def generatedConfigVersionDistDir = "$generatedDistNameDir/dist"
def generatedConfigVersionText = "$generatedConfigVersionDistDir/config-version.txt"
def generatedOldConfigVersionText = "$generatedConfigVersionDistDir/version.txt"
configsForRemoval[generatedServicePropsConfig] = Files.exists(Paths.get(generatedServicePropsConfig))
configsForRemoval[generatedLog4j2Config] = Files.exists(Paths.get(generatedLog4j2Config))
configsForRemoval[generatedConfigVersionText] = Files.exists(Paths.get(generatedConfigVersionText))
configsForRemoval[generatedOldConfigVersionText] = Files.exists(Paths.get(generatedOldConfigVersionText))
dirsForRemoval[generatedConfigDir] = Files.exists(Paths.get(generatedConfigDir))
dirsForRemoval[generatedConfigVersionDistDir] = Files.exists(Paths.get(generatedConfigVersionDistDir))
dirsForRemoval[generatedDistNameDir] = Files.exists(Paths.get(generatedDistNameDir))
}
dirsForRemoval[generatedPlatformDir] = Files.exists(Paths.get(generatedPlatformDir))
}
//Also handle mainConfig, which is not dynamic.
def mainConfigDistNameDir = "${p.projectDir}/src/mainConfig"
def mainConfigVersionDistDir = "$mainConfigDistNameDir/dist"
def mainConfigVersionText = "$mainConfigVersionDistDir/config-version.txt"
def mainConfigOldVersionText = "$mainConfigVersionDistDir/version.txt"
configsForRemoval[mainConfigVersionText] = Files.exists(Paths.get(mainConfigVersionText))
configsForRemoval[mainConfigOldVersionText] = Files.exists(Paths.get(mainConfigOldVersionText))
dirsForRemoval[mainConfigVersionDistDir] = Files.exists(Paths.get(mainConfigVersionDistDir))
dirsForRemoval[mainConfigDistNameDir] = Files.exists(Paths.get(mainConfigDistNameDir))
return [
configsForRemoval: configsForRemoval,
dirsForRemoval: dirsForRemoval
]
}
def genConfigDistTasks = { p, appName ->
deploymentPlatforms.each {
def deploymentPlatform = it
deploymentEnvironments.each {
def deploymentEnv = it
def distributionName = "$deploymentPlatform${deploymentEnv.capitalize()}"
p.distributions.create(distributionName, {
baseName = appName
contents {
into('config') {
from fileTree('src/main/resources').matching {
exclude 'spotbugs-exclusion-filters.xml',
'config/config.properties',
'config/log4j2.xml'
}.files
from fileTree("config/$deploymentPlatform/$deploymentEnv/log4j2.xml").
files
}
}
})
p.tasks.getByName("${distributionName}DistZip") {
preserveFileTimestamps = false
reproducibleFileOrder = true
archiveBaseName = appName
includeEmptyDirs = true
dependsOn p.tasks.transformConfigurations
def appVersion = null
doFirst {
appVersion = p.jar.ext.has('version') ? p.jar.ext.version : {
apply from: "${p.projectDir}/gradle/version-util.gradle"
return getVersionInfo().full
}()
def distDir = "${p.projectDir}/src/$distributionName/dist"
if (Files.notExists(Paths.get(distDir))) {
Files.createDirectories(Paths.get(distDir))
}
Files.write(Paths.get("${p.projectDir}/src/$distributionName/dist/config-version.txt"),
"$appName-config-$appVersion".toString().getBytes())
}
doLast {
def distAppName = "${p.properties['application.configTitle']}-${appVersion}" +
".${distributionName}.config.${archiveExtension.get()}"
p.logger.info(
"Fixing archive name, renaming from ${archiveFileName.get()} to $distAppName"
)
def zabd = "build/distributions"
file("$zabd/${archiveFileName.get()}").
renameTo(file("$zabd/$distAppName")
)
}
}
}
}
}
ext {
//Add closures to extension object
generateConfigurationDistributionTasks = genConfigDistTasks
cleanUpCustomTrash = cleanupCustomTask
generateTransformTasks = genTasks
}
我知道这有点晚了,但也许可以帮助面临大型构建文件或需要模块化构建的人。