识别Gradle约定

通过约定进行配置具有许多优点,尤其是在简洁方面,因为开发人员不需要显式配置通过约定隐式配置的内容。 但是,在利用约定进行配置时,需要了解约定。 这些约定可能已经记录在案,但是当我可以编程方式确定约定时,我总是喜欢它,因为文档可能会过时(代码背后的相同原理总是正确的,而注释有时是正确的)。 我通过查看如何识别与Gradle Java Plugin关联的特定约定开始本文。 然后,我对该方法进行一般化,以识别与与Gradle构建的根项目相关联的所有任务相关联的所有属性。

Gradle插件上的Gradle文档说明了以下有关Gradle插件的重要性以及它们对Gradle构建的补充:


Gradle的核心故意为现实世界的自动化提供了很少的有用功能。 插件添加了所有有用的功能,例如编译Java代码的功能。 插件添加新任务(例如JavaCompile),域对象(例如SourceSet),约定(例如主Java源位于src / main / java),以及扩展核心对象和其他插件的对象。

这篇文章介绍了Java插件为Gradle构建带来的一些任务,域对象和约定。 首先,我需要一个非常简单的Gradle构建文件。 它仅由应用Java插件的一行组成。 接下来显示在Gradle构建文件build-java-plugin.gradle

build-java-plugin.gradle

apply plugin: 'java'

有了该单行的Gradle构建文件,可以通过运行gradle -b build-java-plugin.gradle tasks命令轻松查看插件提供的Gradle gradle -b build-java-plugin.gradle tasks 。 接下来的两个屏幕快照显示了运行空的Gradle构建文件的输出,以及仅使用Java插件的应用程序运行Gradle构建文件的输出。

emptyGradleBuildTasksOutput

gradleJavaPluginTasksOutput

通过将运行Gradle“任务”的输出与空构建文件的输出与运行Gradle“任务”的构建文件与应用Java插件的输出进行比较,我们可以看到Gradle具有相同的“ Build Setup Tasks”集。和“帮助任务”,无论是否应用了插件。 更重要的是,我们看到Java插件添加了许多新任务,这些任务分类为“构建任务”(汇编,构建,buildDependents,buildNeeded,类,clean,jar,testClasses),“文档任务”(javadoc),“验证任务”(检查,测试)和“规则”。

我在Gradle 1.10中享受的一项功能是Gradle 1.8 (我使用的较早版本)没有的功能是,可以在命令行中查询特定Gradle任务的详细信息 。 下一个屏幕快照展示了Java插件任务compileJavajarjavadoc 。 通过在命令行上使用help --task <task_name>命令,所有这三个任务都有写入标准输出的详细信息。 有关Java插件任务的这些详细信息,也可以在Gradle用户指南中找到

gradle_1_10_help_task_details

由于Gradle基于Groovy构建,因此使用“蛮力”确定Java插件的特性相当容易。 下一个代码清单( build-java-plugin-properties.gradle )演示了如何使用Groovy确定Gradle属性(可以用-P指定的那些,而不是用-D指定的系统属性 ),该属性可在构建脚本之前和之后使用。在应用Java插件之后,然后使用Groovy的高度方便的重写减法运算符来查找差异。 Java插件添加到Gradle脚本的所有属性的名称和值(属性“ properties”除外)按字母顺序显示。

// build-java-plugin-properties.gradle
//
// Displays properties that Gradle Java Plugin adds beyond properties already
// specified for any Gradle build.

def propertiesBefore = this.properties

apply plugin: 'java'

def propertiesAfter = this.properties

def extraProperties = propertiesAfter - propertiesBefore

def extraPropertiesKeys = new TreeSet<String>()
extraProperties.each
{ property ->
   if (property.key != "properties")
   {
      extraPropertiesKeys.add(property.key)
   }
}

extraPropertiesKeys.each
{ key ->
   println "${key} : ${extraProperties.get(key)}"
}

下图显示了屏幕快照,其中包含运行此脚本的输出。 屏幕快照未显示完整的输出,但是在图像后的文本中显示了较大的一部分输出(所有属性)。

propertiesJavaPluginAddsToGradleBuild

从Gradle脚本上方运行以查看Java插件属性的输出

apiDocTitle : gradleExample API
archivesBaseName : gradleExample
assemble : task ':assemble'
binaries : [classes 'main', classes 'test']
build : task ':build'
buildDependents : task ':buildDependents'
buildNeeded : task ':buildNeeded'
buildTasks : [build]
check : task ':check'
classes : task ':classes'
clean : task ':clean'
compileJava : task ':compileJava'
compileTestJava : task ':compileTestJava'
defaultArtifacts : org.gradle.api.internal.plugins.DefaultArtifactPublicationSet_Decorated@bc80d8
dependencyCacheDir : C:\java\examples\groovyExamples\gradleExample\build\dependency-cache
dependencyCacheDirName : dependency-cache
distsDir : C:\java\examples\groovyExamples\gradleExample\build\distributions
distsDirName : distributions
docsDir : C:\java\examples\groovyExamples\gradleExample\build\docs
docsDirName : docs
inheritedScope : org.gradle.api.internal.ExtensibleDynamicObject$InheritedDynamicObject@c10304
jar : task ':jar'
javadoc : task ':javadoc'
libsDir : C:\java\examples\groovyExamples\gradleExample\build\libs
libsDirName : libs
manifest : org.gradle.api.java.archives.internal.DefaultManifest@1ad3677
metaInf : []
module : org.gradle.api.internal.artifacts.ProjectBackedModule@d2eead
processResources : task ':processResources'
processTestResources : task ':processTestResources'
rebuildTasks : [clean, build]
reporting : org.gradle.api.reporting.ReportingExtension_Decorated@33ab8f
reportsDir : C:\java\examples\groovyExamples\gradleExample\build\reports
reportsDirName : reports
runtimeClasspath : file collection
sourceCompatibility : 1.7
sourceSets : 
sources : [, ]
status : integration
targetCompatibility : 1.7
test : task ':test'
testClasses : task ':testClasses'
testReportDir : C:\java\examples\groovyExamples\gradleExample\build\reports\tests
testReportDirName : tests
testResultsDir : C:\java\examples\groovyExamples\gradleExample\build\test-results
testResultsDirName : test-results

Gradle使用命令gradle properties可以轻松查看所有Gradle属性,但是此命令行操作将显示所有属性,无论其来源(Gradle本身还是插件)。

Java插件添加到构建中的每个Gradle任务都有其自己的属性集。 这些属性可以在Gradle Build Language Reference中找到 。 该文档的“ 任务类型”部分具有指向每种任务类型的链接。 每个任务类型的链接到页面均包含该任务类型支持的属性的详细信息。 例如,任务类型JavaCompile在其页面上列出为具有诸如classpathdestinationDirsource之 类的属性。

以下相当广泛的脚本显示了compileJava,jar和javadoc Gradle Java Plugin任务的属性设置。 该脚本演示了将Groovy应用于识别Gradle构建设置的强大功能。 如果使用更多的反射,该脚本可能会更短,但是明确地调用任务的属性确实在读取方面和作为每个任务可用属性的参考方面具有优势。

build-java-plugin-metadata.gradle

// build-java-plugin-metadata.gradle
//
// Displays the properties associated with the Gradle Java Plugin tasks
// of "compileJava", "jar", and "javadoc".

import groovy.transform.Field

apply plugin: 'java'

@Field int MAX_COLUMNS = 80
@Field String headerSeparator = "=".multiply(MAX_COLUMNS)

printCompileJavaProperties()
printJarProperties()
printJavadocProperties()

def printCompileJavaProperties()
{
   printHeader("compileJava Task")
   println "compileJava.classpath:\n${extractStringRepresentation(compileJava.classpath)}"
   println "compileJava.destinationDir:\n${extractStringRepresentation(compileJava.destinationDir)}"
   println "compileJava.source:\n${extractStringRepresentation(compileJava.source)}"
   println "compileJava.options:\n${extractStringRepresentation(compileJava.options)}"
   println "compileJava.includes:\n${extractStringRepresentation(compileJava.includes)}"
   println "compileJava.excludes:\n${extractStringRepresentation(compileJava.excludes)}"
   println "compileJava.sourceCompatibility:\n${extractStringRepresentation(compileJava.sourceCompatibility)}"
   println "compileJava.targetCompatibility:\n${extractStringRepresentation(compileJava.targetCompatibility)}"
}

def printJarProperties()
{
   printHeader("jar Task")
   println "jar.appendix:\n${extractStringRepresentation(jar.appendix)}"
   println "jar.archiveName:\n${extractStringRepresentation(jar.archiveName)}"
   println "jar.archivePath:\n${extractStringRepresentation(jar.archivePath)}"
   println "jar.baseName:\n${extractStringRepresentation(jar.baseName)}"
   println "jar.caseSensitive:\n${extractStringRepresentation(jar.caseSensitive)}"
   println "jar.classifier:\n${extractStringRepresentation(jar.classifier)}"
   println "jar.destinationDir:\n${extractStringRepresentation(jar.destinationDir)}"
   println "jar.dirMode:\n${extractStringRepresentation(jar.dirMode)}"
   println "jar.duplicatesStrategy:\n${extractStringRepresentation(jar.duplicatesStrategy)}"
   println "jar.entryCompression:\n${extractStringRepresentation(jar.entryCompression)}"
   println "jar.excludes:\n${extractStringRepresentation(jar.excludes)}"
   println "jar.extension:\n${extractStringRepresentation(jar.extension)}"
   println "jar.fileMode:\n${extractStringRepresentation(jar.fileMode)}"
   println "jar.includeEmptyDirs:\n${extractStringRepresentation(jar.includeEmptyDirs)}"
   println "jar.includes:\n${extractStringRepresentation(jar.includes)}"
   println "jar.manifest:\n${extractStringRepresentation(jar.manifest)}"
   println "jar.source:\n${extractStringRepresentation(jar.source)}"
   println "jar.version:\n${extractStringRepresentation(jar.version)}"
}

def printJavadocProperties()
{
   printHeader("javadoc Task")
   println "javadoc.classpath:\n${extractStringRepresentation(javadoc.classpath)}"
   println "javadoc.destinationDir:\n${extractStringRepresentation(javadoc.destinationDir)}"
   println "javadoc.excludes:\n${extractStringRepresentation(javadoc.excludes)}"
   println "javadoc.executable:\n${extractStringRepresentation(javadoc.executable)}"
   println "javadoc.failOnError:\n${extractStringRepresentation(javadoc.failOnError)}"
   println "javadoc.includes:\n${extractStringRepresentation(javadoc.includes)}"
   println "javadoc.maxMemory:\n${extractStringRepresentation(javadoc.maxMemory)}"
   println "javadoc.options:\n${extractStringRepresentation(javadoc.options)}"
   println "javadoc.source:\n${extractStringRepresentation(javadoc.source)}"
   println "javadoc.title:\n${extractStringRepresentation(javadoc.title)}"
}

def String extractStringRepresentation(Object object)
{
   String returnString
   if (object in String)
   {
      returnString = "\t${object}\n"
   }
   else if (object in File)
   {
      returnString = "\t${object.canonicalPath}\n"
   }
   else if (object in FileCollection)  // FileTree is a FileCollection
   {
      StringBuilder filesStr = new StringBuilder()
      def files = object.files
      files.each
      { file ->
         filesStr << "\t" << file.canonicalPath << "\n" 
      }
      returnString = filesStr.toString()
   }
   else if (object in CompileOptions)
   {
      StringBuilder compileOptionsStr = new StringBuilder()
      def compileProperties = object.properties
      compileProperties.each
      { compileProperty ->
         if (compileProperty.value in DebugOptions)
         {
            compileOptionsStr << "\t" << compileProperty.key << ": " << extractStringRepresentation(compileProperty.value) << "\n"
         }
         else if (compileProperty.value in DependOptions)
         {
            compileOptionsStr << "\t" << compileProperty.key << ": " << extractStringRepresentation(compileProperty.value) << "\n"
         }
         else if (compileProperty.value in ForkOptions)
         {
            compileOptionsStr << "\t" << compileProperty.key << ": " << extractStringRepresentation(compileProperty.value) << "\n"
         }
         else if (compileProperty.key != "class")
         {
            compileOptionsStr << "\t" << compileProperty.key << ": " << compileProperty.value << "\n"
         } 
      }
      returnString = compileOptionsStr.toString()
   }
   else if (object in DebugOptions)
   {
      returnString = "\t${object.debugLevel}"
   }
   else if (object in DependOptions)
   {
      returnString = "\t${object.classpath}"
   }
   else if (object in ForkOptions)
   {
      returnString = "\t${object.executable} executable with ${object.tempDir} temp directory" 
   }
   else if (object in Set || object in Boolean || object in Number || object in Enum)
   {
      returnString = "\t${object.toString()}\n"
   }
   else if (object in Manifest)
   {
      StringBuilder manifestStr = new StringBuilder()
      def manifestAttributes = object.getAttributes()
      manifestAttributes.each
      { manifestAttribute ->
         manifestStr << "\t" << manifestAttribute.key << ": " << manifestAttribute.value << "\n" 
      }
      returnString = manifestStr.toString()
   }
   else if (object in MinimalJavadocOptions)
   {
      returnString = extractJavadocOptionsAsString(object)
   }
   else if (object == null)
   {
      returnString = "\tnull\n"
   }
   else
   {
      returnString = "\t${object?.class} was unexpected type.\n"
   }
   return returnString
}

def String extractJavadocOptionsAsString(MinimalJavadocOptions javadocOptions)
{
   StringBuilder javadocOptionsStr = new StringBuilder()

   javadocOptionsStr << "\tjavadoc.bootClasspath:"
   def bootClasspathFiles = javadocOptions.bootClasspath
   bootClasspathFiles.each
   { bootClasspathFile ->
      javadocOptionsStr << "\t\t" << bootClasspathFile.canonicalName << "\n" 
   }
   javadocOptionsStr << "\n"

   javadocOptionsStr << "\tjavadocOptions.classpath:"
   def classpathFiles = javadocOptions.classpath
   classpathFiles.each
   { classpathFile ->
      javadocOptionsStr << "\t\t" << classpathFile.canonicalName << "\n"
   }
   javadocOptionsStr << "\n"

   javadocOptionsStr << "\tjavadocOptions.destinationDirectory: " << javadocOptions.destinationDirectory?.canonicalName << "\n"

   javadocOptionsStr << "\tjavadocOptions.doclet: " << javadocOptions.doclet << "\n"

   javadocOptionsStr << "\tjavadocOptions.docletpath:"
   def docletpath = javadocOptions.docletpath
   docletpath.each
   { docletEntry ->
      javadocOptionsStr << "\t\t" << docletEntry.canonicalName << "\n"
   }
   javadocOptionsStr << "\n"

   javadocOptionsStr << "\tjavadocOptions.encoding: " << javadocOptions.encoding << "\n"

   javadocOptionsStr << "\tjavadocOptions.extDirs:"
   def extDirs = javadocOptions.extDirs
   extDirs.each
   { extDir ->
      javadocOptionsStr << "\t\t" << extDir.canonicalName << "\n"
   }
   javadocOptionsStr << "\n"

   javadocOptionsStr << "\tjavadocOptions.header: " << javadocOptions.header << "\n"

   javadocOptionsStr << "\tjavadocOptions.JFlags:"
   def jflags = javadocOptions.JFlags
   jflags.each
   { jflag ->
      javadocOptionsStr << "\t\t" << jflag << "\n"
   }
   javadocOptionsStr << "\n"

   javadocOptionsStr << "\tjavadocOptions.locale: " << javadocOptions.locale << "\n"

   javadocOptionsStr << "\tjavadocOptions.memberLevel: " << javadocOptions.memberLevel << "\n"

   javadocOptionsStr << "\tjavadocOptions.optionFiles:"
   def optionFiles = javadocOptions.optionFiles
   optionFiles.each
   { optionFile ->
      javadocOptionsStr << "\t\t" << optionFile.canonicalName << "\n"
   }
   javadocOptionsStr << "\n"

   javadocOptionsStr << "\tjavadocOptions.outputLevel: " << javadocOptions.outputLevel << "\n"

   javadocOptionsStr << "\tjavadocOptions.overview: " << javadocOptions.overview << "\n"

   javadocOptionsStr << "\tjavadocOptions.source: " << javadocOptions.source << "\n"

   javadocOptionsStr << "\tjavadocOptions.sourceNames:"
   def sourceNames = javadocOptions.sourceNames
   sourceNames.each
   { sourceName ->
      javadocOptionsStr << "\t\t" << sourceName << "\n"
   }
   javadocOptionsStr << "\n"

   javadocOptionsStr << "\tjavadocOptions.windowTitle: " << javadocOptions.windowTitle << "\n"

   return javadocOptionsStr.toString()
}

def printHeader(String headerText)
{
   println headerSeparator
   println "= ${headerText.center(MAX_COLUMNS-4)} ="
   println headerSeparator
}

我在此构建文件中使用了Groovy @Field批注 ,以使对其应用到的变量可用于构建文件中的方法。 @Field注释直到Groovy 1.8才可用,这使我想起了有关Gradle和Groovy的其他重要信息:Gradle使用其自己的预包装Groovy,而不是可能在自己的计算机上安装的任何其他版本的Groovy。 您可以使用gradle --version命令确定哪个版本的Groovy。 下一个屏幕快照展示了我的Groovy版本( 2.1.6我的Gradle安装(版本1.10)使用的Groovy版本(1.8.6)不同。 因为Gradle 1.10随Groovy 1.8.6一起提供 ,所以我可以使用@Field注释

gradleGroovyVersionDiffFromOwnGroovyVersion

由于最后一个脚本的输出太长了,因此我在这里将其显示为文本而不是图像。

在build-java-plugin-metadata.gradle上运行Running Gradle的输出

================================================================================
=                               compileJava Task                               =
================================================================================
compileJava.classpath:

compileJava.destinationDir:
 C:\java\examples\groovyExamples\gradleExample\build\classes\main

compileJava.source:
 C:\java\examples\groovyExamples\gradleExample\src\main\java\dustin\examples\Main.java
 C:\java\examples\groovyExamples\gradleExample\src\main\java\dustin\examples\Main2.java
 C:\java\examples\groovyExamples\gradleExample\src\main\java\dustin\examples\Main3.java
 C:\java\examples\groovyExamples\gradleExample\src\main\java\dustin\examples\Main4.java
 C:\java\examples\groovyExamples\gradleExample\src\main\java\dustin\examples\Temperature.java
 C:\java\examples\groovyExamples\gradleExample\src\main\java\dustin\examples\TemperatureScale.java
 C:\java\examples\groovyExamples\gradleExample\src\main\java\dustin\examples\TemperatureUnit.java
 C:\java\examples\groovyExamples\gradleExample\src\main\java\dustin\examples\TemperatureUnit2.java
 C:\java\examples\groovyExamples\gradleExample\src\main\java\dustin\examples\TemperatureUnit3.java

compileJava.options:
 bootClasspath: null
 fork: false
 encoding: null
 deprecation: false
 warnings: true
 forkOptions:  null executable with null temp directory
 failOnError: true
 useDepend: false
 includeJavaRuntime: false
 useAnt: false
 compilerArgs: []
 debug: true
 extensionDirs: null
 compiler: null
 debugOptions:  null
 verbose: false
 optimize: false
 dependOptions:  
 listFiles: false

compileJava.includes:
 []

compileJava.excludes:
 []

compileJava.sourceCompatibility:
 1.7

compileJava.targetCompatibility:
 1.7

================================================================================
=                                   jar Task                                   =
================================================================================
jar.appendix:
 null

jar.archiveName:
 gradleExample.jar

jar.archivePath:
 C:\java\examples\groovyExamples\gradleExample\build\libs\gradleExample.jar

jar.baseName:
 gradleExample

jar.caseSensitive:
 true

jar.classifier:

jar.destinationDir:
 C:\java\examples\groovyExamples\gradleExample\build\libs

jar.dirMode:
 null

jar.duplicatesStrategy:
 INCLUDE

jar.entryCompression:
 DEFLATED

jar.excludes:
 []

jar.extension:
 jar

jar.fileMode:
 null

jar.includeEmptyDirs:
 true

jar.includes:
 []

jar.manifest:
 Manifest-Version: 1.0

jar.source:
 C:\java\examples\groovyExamples\gradleExample\build\tmp\jar\MANIFEST.MF

jar.version:
 null

================================================================================
=                                 javadoc Task                                 =
================================================================================
javadoc.classpath:
 C:\java\examples\groovyExamples\gradleExample\build\classes\main
 C:\java\examples\groovyExamples\gradleExample\build\resources\main

javadoc.destinationDir:
 C:\java\examples\groovyExamples\gradleExample\build\docs\javadoc

javadoc.excludes:
 []

javadoc.executable:
 null

javadoc.failOnError:
 true

javadoc.includes:
 []

javadoc.maxMemory:
 null

javadoc.options:
 javadoc.bootClasspath:
 javadocOptions.classpath:
 javadocOptions.destinationDirectory: null
 javadocOptions.doclet: null
 javadocOptions.docletpath:
 javadocOptions.encoding: null
 javadocOptions.extDirs:
 javadocOptions.header: null
 javadocOptions.JFlags:
 javadocOptions.locale: null
 javadocOptions.memberLevel: null
 javadocOptions.optionFiles:
 javadocOptions.outputLevel: QUIET
 javadocOptions.overview: null
 javadocOptions.source: null
 javadocOptions.sourceNames:
 javadocOptions.windowTitle: null

javadoc.source:
 C:\java\examples\groovyExamples\gradleExample\src\main\java\dustin\examples\Main.java
 C:\java\examples\groovyExamples\gradleExample\src\main\java\dustin\examples\Main2.java
 C:\java\examples\groovyExamples\gradleExample\src\main\java\dustin\examples\Main3.java
 C:\java\examples\groovyExamples\gradleExample\src\main\java\dustin\examples\Main4.java
 C:\java\examples\groovyExamples\gradleExample\src\main\java\dustin\examples\Temperature.java
 C:\java\examples\groovyExamples\gradleExample\src\main\java\dustin\examples\TemperatureScale.java
 C:\java\examples\groovyExamples\gradleExample\src\main\java\dustin\examples\TemperatureUnit.java
 C:\java\examples\groovyExamples\gradleExample\src\main\java\dustin\examples\TemperatureUnit2.java
 C:\java\examples\groovyExamples\gradleExample\src\main\java\dustin\examples\TemperatureUnit3.java

javadoc.title:
 gradleExample API

:help

Welcome to Gradle 1.10.

To run a build, run gradle  ...

To see a list of available tasks, run gradle tasks

To see a list of command-line options, run gradle --help

BUILD SUCCESSFUL

Total time: 14.041 secs

上面显示的示例可以很好地识别与Java Gradle插件关联的特定属性。 这很好用,但是它的局限性包括需要为每个需要其值的属性编写显式代码。 这意味着没有必要知道所有可用属性的进一步限制(我使用文档在上面的示例中显式输出了值)。 进一步暗示的限制是,上面的脚本将来将不会显示添加到这些任务的任何属性值。 下一个Gradle构建示例基于先前的示例,但是此示例未明确声明要显示的任务和属性。 而是查找与根项目关联的所有任务,然后打印与每个任务关联的所有属性。

构建java插件元数据反射.gradle

// build-java-plugin-metadata-reflection.gradle
//
// Displays the properties associated with the tasks associated with the Gradle
// root project.
//

import groovy.transform.Field

apply plugin: 'java'

@Field int MAX_COLUMNS = 80
@Field String headerSeparator = "=".multiply(MAX_COLUMNS)

def rootProject = getRootProject()
def tasks = rootProject.tasks
tasks.each
{ task ->
   printTaskProperties(task)
}

def printTaskProperties(Task task)
{
   printHeader("Task " + task.name)
   def taskProperties = task.properties
   taskProperties.each
   { taskProperty ->
      println "${task.name}.${taskProperty.key}=${extractStringRepresentation(taskProperty.value)}"
   }
}

def String extractStringRepresentation(Object object)
{
   String returnString
   if (object in String)
   {
      returnString = "\t${object}\n"
   }
   else if (object in File)
   {
      returnString = "\t${object.canonicalPath}\n"
   }
   else if (object in FileCollection)  // FileTree is a FileCollection
   {
      StringBuilder filesStr = new StringBuilder()
      def files = object.files
      files.each
      { file ->
         filesStr << "\t" << file.canonicalPath << "\n" 
      }
      returnString = filesStr.toString()
   }
   else if (object in CompileOptions)
   {
      StringBuilder compileOptionsStr = new StringBuilder()
      def compileProperties = object.properties
      compileProperties.each
      { compileProperty ->
         if (compileProperty.value in DebugOptions)
         {
            compileOptionsStr << "\t" << compileProperty.key << ": " << extractStringRepresentation(compileProperty.value) << "\n"
         }
         else if (compileProperty.value in DependOptions)
         {
            compileOptionsStr << "\t" << compileProperty.key << ": " << extractStringRepresentation(compileProperty.value) << "\n"
         }
         else if (compileProperty.value in ForkOptions)
         {
            compileOptionsStr << "\t" << compileProperty.key << ": " << extractStringRepresentation(compileProperty.value) << "\n"
         }
         else if (compileProperty.key != "class")
         {
            compileOptionsStr << "\t" << compileProperty.key << ": " << compileProperty.value << "\n"
         } 
      }
      returnString = compileOptionsStr.toString()
   }
   else if (object in DebugOptions)
   {
      returnString = "\t${object.debugLevel}"
   }
   else if (object in DependOptions)
   {
      returnString = "\t${object.classpath}"
   }
   else if (object in ForkOptions)
   {
      returnString = "\t${object.executable} executable with ${object.tempDir} temp directory" 
   }
   else if (object in Set || object in List || object in Boolean || object in Number || object in Enum || object in Class)
   {
      returnString = "\t${object.toString()}\n"
   }
   else if (object in Manifest)
   {
      StringBuilder manifestStr = new StringBuilder()
      def manifestAttributes = object.getAttributes()
      manifestAttributes.each
      { manifestAttribute ->
         manifestStr << "\t" << manifestAttribute.key << ": " << manifestAttribute.value << "\n" 
      }
      returnString = manifestStr.toString()
   }
   else if (object in MinimalJavadocOptions)
   {
      returnString = extractJavadocOptionsAsString(object)
   }
   else if (object in Convention)
   {
      StringBuilder conventionStr = new StringBuilder()
      object.plugins.each?.keyset
      { plugin ->
         conventionStr << "\t" << plugin << "\n"
      }
      returnString = conventionStr.toString()
   }
   else if (object in LoggingManager)
   {
      returnString = "\n\tCurrent Log Level: ${object.level}\n\tStandard Error: ${object.standardErrorCaptureLevel}\n\tStandard Output: ${object.standardOutputCaptureLevel}\n"
   }
   else if (object == null)
   {
      returnString = "\tnull\n"
   }
   else
   {
      returnString = "\t${object?.class} was unexpected type with value of ${object}.\n"
   }
   return returnString
}

def String extractJavadocOptionsAsString(MinimalJavadocOptions javadocOptions)
{
   StringBuilder javadocOptionsStr = new StringBuilder()

   javadocOptionsStr << "\tjavadoc.bootClasspath:"
   def bootClasspathFiles = javadocOptions.bootClasspath
   bootClasspathFiles.each
   { bootClasspathFile ->
      javadocOptionsStr << "\t\t" << bootClasspathFile.canonicalName << "\n" 
   }
   javadocOptionsStr << "\n"

   javadocOptionsStr << "\tjavadocOptions.classpath:"
   def classpathFiles = javadocOptions.classpath
   classpathFiles.each
   { classpathFile ->
      javadocOptionsStr << "\t\t" << classpathFile.canonicalName << "\n"
   }
   javadocOptionsStr << "\n"

   javadocOptionsStr << "\tjavadocOptions.destinationDirectory: " << javadocOptions.destinationDirectory?.canonicalName << "\n"

   javadocOptionsStr << "\tjavadocOptions.doclet: " << javadocOptions.doclet << "\n"

   javadocOptionsStr << "\tjavadocOptions.docletpath:"
   def docletpath = javadocOptions.docletpath
   docletpath.each
   { docletEntry ->
      javadocOptionsStr << "\t\t" << docletEntry.canonicalName << "\n"
   }
   javadocOptionsStr << "\n"

   javadocOptionsStr << "\tjavadocOptions.encoding: " << javadocOptions.encoding << "\n"

   javadocOptionsStr << "\tjavadocOptions.extDirs:"
   def extDirs = javadocOptions.extDirs
   extDirs.each
   { extDir ->
      javadocOptionsStr << "\t\t" << extDir.canonicalName << "\n"
   }
   javadocOptionsStr << "\n"

   javadocOptionsStr << "\tjavadocOptions.header: " << javadocOptions.header << "\n"

   javadocOptionsStr << "\tjavadocOptions.JFlags:"
   def jflags = javadocOptions.JFlags
   jflags.each
   { jflag ->
      javadocOptionsStr << "\t\t" << jflag << "\n"
   }
   javadocOptionsStr << "\n"

   javadocOptionsStr << "\tjavadocOptions.locale: " << javadocOptions.locale << "\n"

   javadocOptionsStr << "\tjavadocOptions.memberLevel: " << javadocOptions.memberLevel << "\n"

   javadocOptionsStr << "\tjavadocOptions.optionFiles:"
   def optionFiles = javadocOptions.optionFiles
   optionFiles.each
   { optionFile ->
      javadocOptionsStr << "\t\t" << optionFile.canonicalName << "\n"
   }
   javadocOptionsStr << "\n"

   javadocOptionsStr << "\tjavadocOptions.outputLevel: " << javadocOptions.outputLevel << "\n"

   javadocOptionsStr << "\tjavadocOptions.overview: " << javadocOptions.overview << "\n"

   javadocOptionsStr << "\tjavadocOptions.source: " << javadocOptions.source << "\n"

   javadocOptionsStr << "\tjavadocOptions.sourceNames:"
   def sourceNames = javadocOptions.sourceNames
   sourceNames.each
   { sourceName ->
      javadocOptionsStr << "\t\t" << sourceName << "\n"
   }
   javadocOptionsStr << "\n"

   javadocOptionsStr << "\tjavadocOptions.windowTitle: " << javadocOptions.windowTitle << "\n"

   return javadocOptionsStr.toString()
}

def printHeader(String headerText)
{
   println headerSeparator
   println "= ${headerText.center(MAX_COLUMNS-4)} ="
   println headerSeparator
}

因为此输出是针对与Gradle构建的根项目相关联的所有Tasks相关的所有属性,所以输出太长而无法在此处包含。 并非所有的属性值实例都具有extractStringRepresentation(Object object)方法准备处理的类,但是可以将这些情况添加到该方法的if-else if结构中以进行处理。 此版本的Gradle构建比早期版本更通用,并打印出与Task关联的属性,这些属性按Task分组。

由于Gradle构建与Groovy紧密耦合,因此可以使用Groovy语法和功能来了解有关Gradle构建的更多信息。 这篇文章中的示例利用了许多Groovy的优点。 上面的Gradle构建代码之所以如此冗长,是因为大多数用于属性值的Gradle类都没有重写toString()方法,因此,如果没有特殊的代码来调用特定的方法来获得有用的表示,就不会显示出真正有用的输出 。 在本文的示例中我没有这样做,但是要解决缺少覆盖的toString()方法的另一种方法是使用Groovy的拦截功能metaClass.invokeMethod )拦截对toString()调用并提供覆盖的版本。 那将基本上与上面使用的代码相同,但是将被封装在拦截对象中,而不是包含在脚本代码中。

结论

Gradle确实有很好的文档(特别是Gradle用户指南Gradle构建语言参考 ),并且可以从该文档中轻松访问与Java Plugin for Gradle(和其他插件)相关的大多数任务和属性。 但是,我想知道如何以编程方式识别重要的约定,以防万一文档被误解,或者我使用的版本与文档所支持的版本不同。 这篇文章的另一个目的是演示与Gradle合作时了解Groovy的有用性。 出于这个原因,我相信Gradle日益重要的地位不免会增加对Groovy的兴趣。

参考:从我们的JCG合作伙伴 Dustin Marx在“ 实际事件启发”博客中确定Gradle约定

翻译自: https://www.javacodegeeks.com/2014/01/identifying-gradle-conventions.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值