blob: 95401953c51dfabb9ce388d66b5aac109507f74c [file] [log] [blame]
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import org.apache.aurora.build.CoverageReportCheck
import org.gradle.api.JavaVersion
plugins {
id 'com.eriwen.gradle.js' version '1.12.1'
id 'com.github.ben-manes.versions' version '0.11.3'
id 'com.github.hierynomus.license' version '0.11.0'
id 'me.champeau.gradle.jmh' version '0.2.0'
}
apply plugin: 'application'
apply plugin: 'checkstyle'
apply plugin: 'findbugs'
apply plugin: 'jacoco'
apply plugin: 'pmd'
def minJavaVersion = JavaVersion.VERSION_1_8;
allprojects {
apply plugin: 'java'
apply plugin: 'idea'
apply plugin: 'maven-publish'
apply plugin: 'project-report'
buildDir = 'dist'
repositories {
mavenCentral()
}
compileJava {
sourceCompatibility = minJavaVersion
targetCompatibility = minJavaVersion
}
group 'org.apache.aurora'
version = file("${rootDir}/.auroraversion").text.trim().toUpperCase()
if (version.contains('/')) {
throw new GradleException('''
*******************************************************************************
The application version string read by gradle is invalid. This is known to
happen when building on Windows, or within vagrant with a Windows host.
You can work around this issue by cloning git _within_ vagrant and building
from there.
For more details, please see https://issues.apache.org/jira/browse/AURORA-1169
*******************************************************************************
''')
}
task sourceJar(type: Jar) {
from sourceSets.main.allJava
}
if (project.hasProperty('internalMavenUrl')) {
publishing {
repositories {
maven {
credentials {
username = internalMavenUser
password = internalMavenPass
}
url internalMavenUrl
}
}
}
}
ext.commonsLangRev = '2.6'
ext.curatorRev = '2.10.0'
ext.gsonRev = '2.3.1'
ext.guavaRev = '19.0'
ext.guiceRev = '3.0'
ext.jacksonRev = '2.5.1'
ext.jerseyRev = '1.19'
ext.jsrRev = '3.0.1'
ext.junitRev = '4.12'
ext.mybatisRev = '3.3.1'
ext.protobufRev = '2.6.1'
ext.servletRev = '3.1.0'
ext.slf4jRev = '1.7.12'
ext.stringTemplateRev = '3.2.1'
ext.thriftRev = '0.9.1'
ext.zookeeperRev = '3.4.8'
configurations {
compile {
exclude module: 'junit-dep'
// We use logback, so we do not want to pull in log4j artifacts.
exclude module: 'log4j'
exclude module: 'slf4j-log4j12'
// ResolutionStrategy needs to be set in the allprojects block otherwise dependent projects
// will not inherit it. Note that dependencies still need to be specified in a dependencies
// block - this only affects strategy.
// See http://forums.gradle.org/gradle/topics/shouldnt-resolutionstrategy-affect-depending-projects-transitive-dependencies
resolutionStrategy {
failOnVersionConflict()
force "com.fasterxml.jackson.core:jackson-annotations:${jacksonRev}"
force "com.fasterxml.jackson.core:jackson-core:${jacksonRev}"
force "com.google.code.gson:gson:${gsonRev}"
force "com.google.guava:guava:${guavaRev}"
force "com.google.protobuf:protobuf-java:${protobufRev}"
force "junit:junit:${junitRev}"
force "org.apache.thrift:libthrift:${thriftRev}"
force "org.apache.zookeeper:zookeeper:${zookeeperRev}"
force "org.hamcrest:hamcrest-core:1.3"
force "org.slf4j:slf4j-api:${slf4jRev}"
force "org.mybatis:mybatis:${mybatisRev}"
}
}
}
}
/* This gradle project contains the annotation procesor from
* com.twitter.common.args and related dependencies. It needs to be outside of
* the commons project because gradle needs to compile the annotation processor
* first.
*/
project(':commons-args') {
dependencies {
compile "com.google.guava:guava:${guavaRev}"
compile "com.google.code.findbugs:jsr305:${jsrRev}"
compile "commons-lang:commons-lang:${commonsLangRev}"
}
apply plugin: 'license'
license {
header rootProject.file('config/checkstyle/apache.header')
strictCheck true
}
}
project(':commons') {
apply plugin: org.apache.aurora.build.ThriftPlugin
thrift {
version = thriftRev
}
apply plugin: 'license'
license {
header rootProject.file('config/checkstyle/apache.header')
strictCheck true
}
dependencies {
compile project(':commons-args')
compile "com.google.code.findbugs:jsr305:${jsrRev}"
compile "com.google.code.gson:gson:${gsonRev}"
compile "com.google.guava:guava:${guavaRev}"
compile "com.google.inject.extensions:guice-multibindings:${guiceRev}"
compile "com.google.inject:guice:${guiceRev}"
compile "com.sun.jersey:jersey-core:${jerseyRev}"
compile "commons-lang:commons-lang:${commonsLangRev}"
compile "javax.servlet:javax.servlet-api:${servletRev}"
compile "joda-time:joda-time:2.9.1"
compile "org.antlr:stringtemplate:${stringTemplateRev}"
compile "org.apache.zookeeper:zookeeper:${zookeeperRev}"
compile "org.easymock:easymock:3.4"
// There are a few testing support libs in the src/main/java trees that use junit - currently:
// src/main/java/org/apache/aurora/common/zookeeper/testing
// src/main/java/org/apache/aurora/common/testing
compile "junit:junit:${junitRev}"
testCompile "junit:junit:${junitRev}"
testCompile "org.powermock:powermock-module-junit4:1.6.4"
testCompile "org.powermock:powermock-api-easymock:1.6.4"
}
}
project(':api') {
apply plugin: org.apache.aurora.build.ThriftPlugin
apply plugin: org.apache.aurora.build.ThriftEntitiesPlugin
task checkPython << {
def python27Executable = ['python2.7', 'python'].find { python ->
try {
def check = "import sys; sys.exit(0 if sys.version_info >= (2,7) and sys.version_info < (3,) else 1)"
return [python, "-c", check].execute().waitFor() == 0
} catch (IOException e) {
return false
}
}
if (python27Executable == null) {
throw new GradleException('Build requires Python 2.7.')
} else {
thriftEntities.python = python27Executable
}
}
generateThriftEntitiesJava.dependsOn checkPython
tasks.withType(Jar) {
baseName "aurora-api"
}
publishing {
publications {
mavenJava(MavenPublication) {
from components.java
artifactId "aurora-api"
artifact sourceJar {
classifier "sources"
}
}
}
}
thrift {
version = thriftRev
resourcePrefix = 'org/apache/aurora/scheduler/gen/client'
}
thriftEntities {
gsonRev = project.gsonRev
guavaRev = project.guavaRev
inputFiles = fileTree("src/main/thrift/org/apache/aurora/gen").matching {
include "**/*.thrift"
}
}
idea {
module {
[thrift.genJavaDir, thriftEntities.genJavaDir].each {
sourceDirs += it
generatedSourceDirs += it
}
// These directories must exist, else the plugin omits them from the
// generated project. Since this is executed during the configuration
// lifecycle phase, dependency tasks have not yet run and created
// the directories themselves.
// By default, the idea module [1] excludes are set to
// [project.buildDir, project.file('.gradle')]
// This has the side-effect of also excluding our generated sources [2]. Due to the way
// directory exclusion works in idea, you can't exclude a directory and include a child of that
// directory. Clearing the excludes seems to have no ill side-effects, making it preferable to
// other possible approaches.
//
// [1] http://www.gradle.org/docs/current/dsl/org.gradle.plugins.ide.idea.model.IdeaModule.html
// [2] http://issues.gradle.org/browse/GRADLE-1174
excludeDirs = [file(".gradle")]
[
"classes",
"dependency-cache",
"docs",
"jacoco",
"reports",
"test-results",
"tmp"
].each {
excludeDirs << file("$buildDir/$it")
}
}
}
}
def generatedDir = "$buildDir/generated-src"
def httpAssetsPath = 'scheduler/assets'
compileJava {
options.compilerArgs << '-Werror'
options.compilerArgs << '-Xlint:all'
// Don't fail for annotations not claimed by annotation processors.
options.compilerArgs << '-Xlint:-processing'
// Don't fail for serialVersionUID warnings.
options.compilerArgs << '-Xlint:-serial'
// Capture method parameter names in classfiles.
options.compilerArgs << '-parameters'
}
task enforceVersion {
def foundVersion = JavaVersion.current();
if (foundVersion < minJavaVersion) {
throw new GradleException("Build requires at least Java ${minJavaVersion}; but ${foundVersion}"
+ " was found. Consider setting JAVA_HOME to select a specific JDK on your system.");
}
}
compileJava.dependsOn(enforceVersion);
task wrapper(type: Wrapper) {
gradleVersion = project(':buildSrc').GRADLE_VERSION
}
// TODO(ksweeney): Consider pushing this down to API - the scheduler implementation itself should
// only be consumed as an application.
publishing {
publications {
mavenJava(MavenPublication) {
from components.java
artifactId 'aurora-scheduler'
artifact sourceJar {
classifier "sources"
}
}
}
}
task generateBuildProperties (type:Exec) {
def outputDir = file("${buildDir}/build-properties")
def outputFile = file("${outputDir}/build.properties")
outputs.upToDateWhen { false }
outputs.dir outputDir
doFirst {
outputDir.exists() || outputDir.mkdirs()
}
commandLine "${projectDir}/build-support/generate-build-properties", "${outputFile}"
}
sourceSets {
main {
output.dir generateBuildProperties
resources {
srcDir '3rdparty/javascript'
}
}
}
dependencies {
def shiroRev = '1.2.4'
def jettyDep = '9.3.6.v20151106'
compile project(':api')
compile project(':commons')
compile project(':commons-args')
compile 'aopalliance:aopalliance:1.0'
compile 'ch.qos.logback:logback-classic:1.1.3'
compile "com.google.code.findbugs:jsr305:${jsrRev}"
compile "com.google.inject:guice:${guiceRev}"
compile "com.google.inject.extensions:guice-assistedinject:${guiceRev}"
compile "com.google.protobuf:protobuf-java:${protobufRev}"
compile 'com.h2database:h2:1.4.190'
compile 'com.hubspot.jackson:jackson-datatype-protobuf:0.9.3'
compile "com.fasterxml.jackson.core:jackson-core:${jacksonRev}"
compile "com.sun.jersey:jersey-core:${jerseyRev}"
compile "com.sun.jersey:jersey-json:${jerseyRev}"
compile "com.sun.jersey:jersey-server:${jerseyRev}"
compile "com.sun.jersey:jersey-servlet:${jerseyRev}"
compile "com.sun.jersey.contribs:jersey-guice:${jerseyRev}"
compile 'javax.inject:javax.inject:1'
compile "javax.servlet:javax.servlet-api:${servletRev}"
compile "org.antlr:stringtemplate:${stringTemplateRev}"
compile "org.apache.curator:curator-client:${curatorRev}"
compile "org.apache.curator:curator-framework:${curatorRev}"
compile "org.apache.curator:curator-recipes:${curatorRev}"
compile 'org.apache.mesos:mesos:0.28.2'
compile "org.apache.shiro:shiro-guice:${shiroRev}"
compile "org.apache.shiro:shiro-web:${shiroRev}"
compile "org.apache.zookeeper:zookeeper:${zookeeperRev}"
compile "org.eclipse.jetty:jetty-rewrite:${jettyDep}"
compile "org.eclipse.jetty:jetty-server:${jettyDep}"
compile "org.eclipse.jetty:jetty-servlet:${jettyDep}"
compile "org.eclipse.jetty:jetty-servlets:${jettyDep}"
compile "org.mybatis:mybatis:${mybatisRev}"
compile 'org.mybatis:mybatis-guice:3.7'
compile 'org.mybatis:mybatis-migrations:3.2.0'
compile 'org.quartz-scheduler:quartz:2.2.2'
compile "uno.perk:forward:1.0.0"
testCompile "com.sun.jersey:jersey-client:${jerseyRev}"
testCompile "junit:junit:${junitRev}"
}
// For normal developer builds, avoid running the often-time-consuming code quality checks.
// Jenkins will always run these, and developers are encouraged to run these before posting diffs
// and pushing to master.
def runCodeQuality = project.hasProperty('q')
def codeQualityTasks = [
Checkstyle,
FindBugs,
nl.javadude.gradle.plugins.license.License,
Pmd
]
codeQualityTasks.each {
tasks.withType(it) {
enabled = runCodeQuality
}
}
checkstyle {
sourceSets = [sourceSets.main , sourceSets.test, sourceSets.jmh]
toolVersion = '6.13'
}
findbugs {
toolVersion = '3.0.1'
effort = "max"
}
tasks.withType(FindBugs) {
reports {
xml.enabled = false
html.enabled = true
}
maxHeapSize = '1g'
excludeFilter = rootProject.file('config/findbugs/excludeFilter.xml')
}
pmd {
toolVersion = '5.4.1'
consoleOutput = true
}
// As recommended here to work around PMD bugs exposed by otherwise
// handing it an `auxClasspath` to resolve types with:
// https://discuss.gradle.org/t/upgrading-gradle-from-2-7-to-2-8-results-in-pmd-false-positives/13460
tasks.withType(Pmd) {
classpath = null
}
pmdMain {
ruleSetFiles = files('config/pmd/common.xml', 'config/pmd/main.xml')
}
pmdTest {
ruleSetFiles = files('config/pmd/common.xml', 'config/pmd/test.xml')
}
/**
* There is a jshint target recommended in the README for gradle-js-plugin
* but it does not work. This workaround from here:
* https://github.com/eriwen/gradle-js-plugin/issues/73
*/
task jsHint(type:com.eriwen.gradle.js.tasks.JsHintTask) {
source = fileTree("src/main/resources/$httpAssetsPath/js/")
dest file("${buildDir}/jshint.out")
// Set this to false once JS code complies with JSHint.
ignoreExitCode false
outputToStdOut true
jshint.options = [
// See: http://www.jshint.com/docs/options/ for explanation.
browser: true,
camelcase: true,
curly: true,
eqeqeq: true,
indent: 2,
maxlen: 100,
quotmark: true,
trailing: true,
undef: true,
unused: 'vars',
white: true
]
jshint.predef = [
'_': true,
'angular': true,
'moment': true,
'Thrift': true
]
}
tasks.checkstyleMain.dependsOn(jsHint)
tasks.withType(Test) {
maxParallelForks = Runtime.runtime.availableProcessors()
}
// Turn on annotation processors for commons (uses commons-args) and aurora
// (uses commons-args, forward, ...). The crux here is _not_ turning annotation processing on
// for commons-args (this leads to an attempt at self-processing before its processor is
// compiled); so the strategy taken is to white-list modules.
//
// NB: Unfortunately, most of the ipr/iml modeling we need is not exposed by the idea plugin DSL so
// we drop to xml manipulation here only out of necessity.
def apt_projects = ['commons', 'aurora']
idea {
project {
vcs = 'Git'
jdkName = '1.8'
languageLevel = '1.8'
ipr {
withXml {
def projectNode = it.asNode()
// Configure an annotation processor profile.
def compilerConfiguration = projectNode.find {
it.name() == 'component' && it.@name == 'CompilerConfiguration'
}
def apt = compilerConfiguration.find { it.name() == 'annotationProcessing' }
// Turn on annotation processing only for the whitelisted modules.
apt.replaceNode {
annotationProcessing {
profile(default: 'true', name: 'Default', enabled: 'false')
profile(default: 'false', name: 'apt', enabled: 'true') {
sourceOutputDir(name: 'generated')
sourceTestOutputDir(name: 'generated')
processorPath(useClasspath: 'true')
apt_projects.each {
module(name: it)
}
}
}
}
def projectRoot = projectNode.find {
it.name() == 'component' && it.@name == 'ProjectRootManager'
}
projectRoot.remove(projectRoot.output)
// Add parameter names to generated class file debug info.
projectNode
.appendNode('component', [name: 'JavacSettings'])
.appendNode('option', [name: 'ADDITIONAL_OPTIONS_STRING', value: '-parameters'])
}
}
}
}
// Map output dirs explicitly per-module with the end goal of mapping the annotation processor
// generated code output dirs as source (and test) dirs. The key difficulty being worked around
// here is the fact that excludes higher in a directory tree override includes deeper in the tree.
allprojects {
def isApt = it.name in apt_projects
def generatedSourceDir = file("${it.projectDir}/out/production/generated")
def generatedTestDir = file("${it.projectDir}/out/test/generated")
idea {
module {
if (isApt) {
generatedSourceDir.exists() || generatedSourceDir.mkdirs()
sourceDirs += generatedSourceDir
generatedSourceDirs += generatedSourceDir
generatedTestDir.exists() || generatedTestDir.mkdirs()
testSourceDirs += generatedTestDir
generatedSourceDirs += generatedTestDir
}
iml {
withXml {
def moduleNode = it.asNode()
def moduleConfiguration = moduleNode.find {
it.name() == 'component' && it.@name == 'NewModuleRootManager'
}
moduleConfiguration.attributes().remove('inherit-compiler-output')
moduleConfiguration.appendNode('output', [url: 'file://$MODULE_DIR$/out/production'])
moduleConfiguration.appendNode('output-test', [url: 'file://$MODULE_DIR$/out/test'])
if (isApt) {
def excludeOutput = moduleConfiguration.find { it.name() == 'exclude-output' }
if (excludeOutput) {
moduleConfiguration.remove(excludeOutput)
}
}
}
}
}
}
}
// Configuration parameters for the application plugin.
applicationName = 'aurora-scheduler'
mainClassName = 'org.apache.aurora.scheduler.app.SchedulerMain'
// TODO(ksweeney): Configure this to scan resources as well.
tasks.withType(nl.javadude.gradle.plugins.license.License).each {
it.source = files("$projectDir/src/main/java", "$projectDir/src/test/java")
}
license {
header rootProject.file('config/checkstyle/apache.header')
strictCheck true
skipExistingHeaders true
}
def reportPath = "$buildDir/reports/jacoco/test"
jacocoTestReport {
group = "Reporting"
description = "Generate Jacoco coverage reports after running tests."
sourceDirectories = sourceSets.main.java
classDirectories = files("$buildDir/classes/main")
reports {
xml.enabled true
}
doLast {
println "Coverage report generated: file://$reportPath/html/index.html"
}
}
test.finalizedBy jacocoTestReport
task analyzeReport(type: CoverageReportCheck) {
coverageReportFile = "$reportPath/jacocoTestReport.xml"
minInstructionCoverage = 0.89
minBranchCoverage = 0.835
legacyClassesWithoutCoverage = file('config/legacy_untested_classes.txt').readLines()
}
jacocoTestReport.finalizedBy analyzeReport
def jmhHumanOutputPath = "$buildDir/reports/jmh/human.txt"
jmh {
// Run specific benchmarks by passing -Pbenchmarks='<regexp' on the command line. For example
// ./gradlew jmh -Pbenchmarks='ThriftApiBenchmarks.*'
if (project.hasProperty('benchmarks')) {
include = project.getProperty('benchmarks')
}
jmhVersion = '1.11.3'
jvmArgsPrepend = '-Xmx3g'
humanOutputFile = project.file("$jmhHumanOutputPath")
resultsFile = project.file("$buildDir/reports/jmh/results.txt")
}
tasks.getByName('jmh').doLast() {
println "Benchmark report generated: file://$jmhHumanOutputPath"
}
run {
main = 'org.apache.aurora.scheduler.app.local.LocalSchedulerMain'
classpath += sourceSets.test.output
}
startScripts {
def environmentClasspathPrefix = System.env.CLASSPATH_PREFIX
if (!environmentClasspathPrefix?.trim()) {
return
}
doLast {
unixScript.text = unixScript.text.replace('CLASSPATH=', "CLASSPATH=${environmentClasspathPrefix}:")
}
}