Gradle无法从应用的子脚本中识别插件任务
我真的很想将我的脚本模块化为多个部分(测试、默认、文档、android)部分。但当我尝试将子脚本应用于父脚本时,我遇到了问题 这是我的子脚本(此时IDE没有抱怨) 但当我尝试将此脚本应用于主脚本时:Gradle无法从应用的子脚本中识别插件任务,gradle,build.gradle,gradle-kotlin-dsl,Gradle,Build.gradle,Gradle Kotlin Dsl,我真的很想将我的脚本模块化为多个部分(测试、默认、文档、android)部分。但当我尝试将子脚本应用于父脚本时,我遇到了问题 这是我的子脚本(此时IDE没有抱怨) 但当我尝试将此脚本应用于主脚本时: apply(from = "tdd.gradle.kts") 我得到以下错误: Script compilation errors: Line 01: import io.gitlab.arturbosch.detekt.Detekt ^ Unre
apply(from = "tdd.gradle.kts")
我得到以下错误:
Script compilation errors:
Line 01: import io.gitlab.arturbosch.detekt.Detekt
^ Unresolved reference: io
Line 10: val detekt by existing(Detekt::class) {
^ Unresolved reference: Detekt
Line 11: reports {
^ Unresolved reference: reports
Line 12: html {
^ Unresolved reference: html
如何解决此问题,以便在子脚本中应用detekt插件并将其应用于parrent脚本?(情况与您的情况非常相似):
脚本插件无法访问主buildscript类路径
您可以使用以下命令将对mycompany.plugin的依赖项添加到tests.gradle
buildscript{}语法
因此,基本上,使用旧的
buildscript
语法,您的导入应该可以工作我已经尝试了很多方法来破解这个问题。它的长短在Kotlin DSL中是不可能的
未经测试:我会尝试的方法(bc这是我还没有尝试过的一件事)是使用插件的两个应用程序(老式)并将应用脚本插件中的所有项目配置为显式的跨项目配置,这种配置不需要在应用脚本中使用所有项目
或子项目
块
您应该参考gradle当前的项目文档,但通常采用以下形式:
project(":foo") {
// Some configuration here
apply(plugin = "plugin.id.here.sample-plugin")
// Then use configuration techniques in the Kotlin DSL guide for configuring when you don't have access to type-safe accessors, small example below
configure<SamplePluginExtension> {
// Plugin extension configuration...
}
}
build.gradle.kts
这类lambda的一些一般安全提示:
- 对
实例使用项目
将确保使用它的开发人员不需要考虑当前范围内的项目实例(在应用的脚本中或调用它的位置)。这也意味着调用站点的封闭作用域提供的p
仍然可以使用project
- 大量考虑回归单元提供无副作用的用法。一些插件具有扩展,可以通过注册在容器的config块中找到的所有实例来自动配置容器的各种元素。除非绝对需要,否则不支持隐式链接是最安全的选择
- 请记住,在许多上下文中,不能依赖标准库中的kotlin的
,因为它将解析为Gradle的T.apply{}
,因此如果您想这样做(或返回到单元)轻松跨越多个配置lambda,最好创建一个小的helper函数,基本上实现apply在Kotlin中的功能,或者像我所做的那样,构建一个helper,运行给定的块并始终返回单元(或某种泛化类型)apply
@ToYonos-gradle的建议对Kotlin DSL不起作用。我的结果很差(我专门使用了
类路径
依赖项),因为gradle会报告插件从未应用过,并且在同一条错误消息中显示扩展已应用。我粗略的猜测是,这与字节码和/或为应用脚本使用单独的类装入器有关。(很抱歉,我本想在你的回答中添加这一点,但没有足够的代表性)我的回答建立在fuzzyweapon的回答之上,因为我在看到它之前无法理解它,太好了
我的答案基于gradledsl语法。我希望有足够的细节来收集适用于Kotlin DSL的解决方案
您可以通过创建接受项目对象参数的闭包来解决大多数此类问题,而不是将构建逻辑直接放入应用的脚本中(尽管这显然适用于独立于项目的逻辑)。出于我不清楚的原因,在单独的脚本中创建新函数会导致项目对象参数参数参数始终为null。闭包的语法非常接近,几乎与Java lambda语法相同,从调用方的角度来看,函数和闭包之间的语义基本相同(显然与外部脚本内部不同)
我最近不得不解决一个类似的问题,我想将一些动态任务生成和清理逻辑提取到一个单独的脚本中。我就是这样解决的:
$projectDir/gradle/env config.gradle
$projectDir/gradle/env config.gradle
中:
然后在项目构建脚本的适当范围和阶段调用它,在我的例子中,这是在配置阶段的自定义转换任务中:
在$projectDir/build.gradle
中:
我发现,这种分解构建脚本的方法非常灵活,基本上可以处理希望将逻辑分解为单独脚本的大多数情况
下面是最终产品独立构建脚本的精简版本(实际上,它甚至在自定义脚本中应用了不同的自定义脚本):
我知道这有点晚了,但也许可以帮助那些面对大型构建文件或需要模块化构建的人。你能分享你的
buildscript
block吗?@ToYonos:我没有,因为它是
project(":foo") {
// Some configuration here
apply(plugin = "plugin.id.here.sample-plugin")
// Then use configuration techniques in the Kotlin DSL guide for configuring when you don't have access to type-safe accessors, small example below
configure<SamplePluginExtension> {
// Plugin extension configuration...
}
}
val allProjectsConfiguration by extra { p: Project ->
// Some project configuration here...
}
//Boilerplate build script dependencies, repositories, and buildscript block as necessary...
val allProjectsConfiguration: (Project) -> Unit by extra
allProjects {
allProjectsConfiguration(this)
// where `this` is not the host script's project, not the applied script's project (of course), but one of the given projects in *all* of the projects.
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
}
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
}