1
0
Fork 0
mirror of https://github.com/ethauvin/kobalt.git synced 2025-05-13 07:22:00 -07:00

Compare commits

..

No commits in common. "master" and "0.314" have entirely different histories.

402 changed files with 7862 additions and 19640 deletions

11
.gitignore vendored
View file

@ -1,15 +1,12 @@
.gradle
annotations
.idea/*
*.iml
nonBuildScript
!.idea/modules.xml
buildScript
kobaltBuild
test-output
local.properties
classes
libs
.kobalt/
out
.DS_Store
lib/kotlin-*
build
.history
build/

9
.idea/modules.xml generated Normal file
View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/kobalt.iml" filepath="$PROJECT_DIR$/kobalt.iml" />
<module fileurl="file://$PROJECT_DIR$/modules/wrapper/wrapper.iml" filepath="$PROJECT_DIR$/modules/wrapper/wrapper.iml" />
</modules>
</component>
</project>

8
.travis.yml Normal file
View file

@ -0,0 +1,8 @@
language: java
jdk:
- oraclejdk8
install: true
script: ./kobaltw test --log 1

View file

@ -1,15 +1,11 @@
# Kobalt
[<img src="https://teamcity.jetbrains.com/app/rest/builds/buildType:(id:OpenSourceProjects_Kobalt_Build)/statusIcon.svg">](https://teamcity.jetbrains.com/project.html?projectId=OpenSourceProjects_Kobalt&tab=projectOverview)
Kobalt is a universal build system.
To build it:
```
$ ./kobaltw assemble
./kobaltw assemble
```
Please see [the web site](http://beust.com/kobalt/) for the full documentation.

48
TODO.md
View file

@ -1,33 +1,36 @@
To do:
Android:
General
- [ ] KFiles.findSourceFiles should cache its results
- [ ] createCompilerAction should calculate source files once and for all, including contributors and interceptors
- [ ] Need a -resolve with no dependency which gives the tree for the whole project
- [ ] If jitpack specified with http and not https, 301 is not handled correctly
- [ ] Apt should run from serviceloader
- [ ] The test runner only selects classes with a parameterless constructor, which works for JUnit but not for TestNG
factories
- [ ] Add a "Auto complete Build.kt" menu in the plug-in
- [ ] "All artifacts successfully uploaded" is shown before the upload is actually done
- [ ] use groupId/artifactId
- [ ] Console mode with watch service, so recompilation can occur as soon as a source file is modified
- [ ] ProjectGenerator: support migration from pom.xml (starting with dependencies)
- [ ] Specify where to upload snapshots
- [ ] Upload in a thread pool
- [ ] repos() must appear before plugins(): fix that
- [ ] Support version ranges
- [ ] Generate .idea and other IDEA files
- [ ] logs for users should not show any timestamp, class file or thread id, this should only be in --dev mode
- [ ] Fetch .pom with DynamicGraph
- [ ] Centralize all the executors
- [ ] Archetypes (e.g. "--initWith kobalt-plug-in")
- [ ] Compile TestNG (last piece missing: OSGi headers)
- [ ] Compile TestNG (including generating Version.java and OSGi headers)
- [ ] Support additional .kt files in ~/.kobalt/src
- [ ] generateArchive() should use Path instead of File
- [ ] --init: import dependencies from build.gradle
- [ ] --init: also extract kobalt.bat (or generate it along with kobaltw)
- [ ] Bug: --tasks displays multiple tasks when there are multiple projects
- [ ] Bug: ./kobaltw --dryRun kobalt:uploadJcenter runs "kobalt-wrapper:clean" twice
- [ ] Replace File with java.nio.Files and Path
- [ ] Create a wiki page for plugins
- [ ] Make kobaltw executable in the zip file
- [ ] Encapsulate ProcessBuilder code
- [ ] --resolve <dep>
Done:
- [x] Dex dependencies into kobaltBuild/intermediates/pre-dexed and preserve those across builds
- [x] Compile with javax.tool
- [x] Android: multiple -source/-target flags
- [x] Dokka: allow multiple format outputs e.g `outputFormat("html", "javadoc")`
- [x] Finish abstracting everything in `JvmCompilerPlugin`
@ -67,24 +70,5 @@ just a straight Java class with minimal dependencies for fast start up
- [x] Upload non maven files
- [x] Jar packaging: include the jar in the jar (not supported by JarFile)
- [x] Encapsulate BuildFile for better log messages
- [x] The test runner only selects classes with a parameterless constructor, which works for JUnit but not for TestNG
- [x] Add a "Auto complete Build.kt" menu in the plug-in
- [x] "All artifacts successfully uploaded" is shown before the upload is actually done
- [x] use groupId/artifactId
factories
- [x] Support version ranges
- [x] Upload in a thread pool
- [x] logs for users should not show any timestamp, class file or thread id, this should only be in --dev mode
- [x] Bug: --tasks displays multiple tasks when there are multiple projects
- [x] Bug: ./kobaltw --dryRun kobalt:uploadJcenter runs "kobalt-wrapper:clean" twice
- [x] Create a wiki page for plugins
- [x] Make kobaltw executable in the zip file
- [x] --resolve <dep>
- [x] Move the calculated applicationId back into the merged AndroidManifest.xml
- [x] Dex from android builder
- [x] Keep exploded aars between runs
- [x] aars keep being refetched
- [x] See if there is an android manifest file in builder
- [x] Auto add variant

View file

@ -1,8 +0,0 @@
ulimit -s 1082768
#java -Xmx2048m -jar $(dirname $0)/kobalt/wrapper/kobalt-wrapper.jar $* clean
java -Xmx2048m -jar $(dirname $0)/kobalt/wrapper/kobalt-wrapper.jar $* clean assemble test --parallel

View file

@ -1,58 +1,90 @@
allprojects {
group = 'com.beust'
version = '1.1.0'
}
subprojects {
apply plugin: 'java'
apply plugin: 'maven-publish'
ext {
bndlib = '3.5.0'
findbugs = '3.0.2'
groovy = '2.4.12'
gson = '2.8.2'
guice = '4.2.2'
inject = '1'
jaxb = '2.3.0'
jcommander = '1.72'
kotlin = '1.2.71'
maven = '3.5.2'
mavenResolver = '1.1.0'
okhttp = '3.9.1'
okio = '1.13.0'
retrofit = '2.3.0'
slf4j = '1.7.3'
spark = '2.6.0'
testng = '6.12'
junit = '4.12'
junitJupiter = '5.1.0'
junitPlatform = '1.1.0'
}
buildscript {
ext.kotlin_version = '1.0.0-beta-2423'
repositories {
mavenCentral()
mavenLocal()
jcenter()
maven {
url = 'https://dl.bintray.com/cbeust/maven'
}
maven {
url = 'https://repo.maven.apache.org/maven2'
}
}
sourceCompatibility = '1.7'
task sourcesJar(type: Jar) {
from sourceSets.main.allJava
archiveClassifier = 'sources'
}
task javadocJar(type: Jar) {
from javadoc
archiveClassifier = 'javadoc'
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${kotlin_version}"
}
}
plugins {
id "com.jfrog.bintray" version "1.2"
}
version = '0.121'
//apply plugin: 'java'
apply plugin: 'kotlin'
apply plugin: 'com.jfrog.bintray'
apply from: 'gradle/publishing.gradle'
repositories {
jcenter()
}
dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib:${kotlin_version}",
"org.jetbrains.kotlin:kotlin-compiler-embeddable:${kotlin_version}",
'org.jetbrains.kotlinx:kotlinx.dom:0.0.4',
'org.jetbrains.dokka:dokka-fatjar:0.9.2',
// "org.jetbrains.kotlin:kotlin-compiler:${kotlin_version}",
'com.beust:jcommander:1.48',
'com.squareup.okhttp:okhttp:2.5.0',
'org.jsoup:jsoup:1.8.3',
'com.google.inject:guice:4.0',
'com.google.inject.extensions:guice-assistedinject:4.0',
'javax.inject:javax.inject:1',
'com.google.guava:guava:19.0-rc2',
'org.apache.maven:maven-model:3.3.3',
'com.github.spullara.mustache.java:compiler:0.9.1',
'io.reactivex:rxjava:1.0.16',
'com.google.code.gson:gson:2.4',
'com.squareup.retrofit:retrofit:1.9.0',
'com.squareup.okio:okio:1.6.0',
project("modules/wrapper")
// compile files("/Users/beust/.kobalt/repository/com/beust/kobalt-example-plugin/build/libs/kobalt-example-plugin.jar")
testCompile 'org.testng:testng:6.9.9'
// testCompile 'junit:junit:4.12'
}
task sourceJar(type: Jar) {
group 'Build'
description 'An archive of the source code'
classifier 'sources'
from sourceSets.main.allSource
}
artifacts {
file('build/libs/kobalt.jar')
sourceJar
}
test {
useTestNG()
beforeTest { descriptor ->
logger.lifecycle(" Running test: " + descriptor)
}
}
compileKotlin {
kotlinOptions.suppressWarnings = true
}
apply plugin: 'application'
mainClassName = 'com.beust.kobalt.KobaltPackage'
jar {
manifest {
attributes "Main-Class": "$mainClassName"
}
from {
configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
}
}

11
dist/kobaltw vendored
View file

@ -1,11 +0,0 @@
#!/usr/bin/env sh
case "$(uname)" in
CYGWIN*) DIRNAME=$(cygpath -d "$(dirname "$(readlink -f "$0")")");;
Darwin*) DIRNAME=$(dirname "$(readlink "$0")");;
*) DIRNAME=$(dirname "$(readlink -f "$0")");;
esac
if [ "$DIRNAME" = "." ]; then
DIRNAME="$(dirname "$0")"
fi
java -jar "${DIRNAME}/../kobalt/wrapper/kobalt-wrapper.jar" $*

4
dist/kobaltw.bat vendored
View file

@ -1,4 +0,0 @@
@echo off
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
java -jar "%~dp0/../kobalt/wrapper/kobalt-wrapper.jar" %*

View file

@ -0,0 +1 @@
../gradlew check

60
gradle/publishing.gradle Normal file
View file

@ -0,0 +1,60 @@
import java.text.SimpleDateFormat
Date buildTimeAndDate = new Date()
ext {
buildTime = new SimpleDateFormat('yyyy-MM-dd').format(buildTimeAndDate)
buildDate = new SimpleDateFormat('HH:mm:ss.SSSZ').format(buildTimeAndDate)
}
apply plugin: 'maven-publish'
apply plugin: 'com.jfrog.bintray'
jar {
manifest {
attributes(
'Built-By': System.properties['user.name'],
'Created-By': System.properties['java.version'] + " (" + System.properties['java.vendor'] + " " + System.getProperty("java.vm.version") + ")",
'Build-Date': project.buildTime,
'Build-Time': project.buildDate,
'Specification-Title': project.name,
'Specification-Version': project.version,
)
}
}
publishing {
publications {
mavenCustom(MavenPublication) {
from components.java
artifact sourceJar
groupId 'com.beust'
artifactId 'kobalt'
version project.version
}
}
}
task install(dependsOn: publishToMavenLocal)
Properties properties = new Properties()
if (new File('local.properties').exists()) {
properties.load(project.rootProject.file('local.properties').newDataInputStream())
}
bintray {
user = properties.getProperty("bintray.user")
key = properties.getProperty("bintray.apikey")
publications = ['mavenCustom']
pkg {
repo = 'maven'
name = 'klaxon'
desc = 'JSON parsing for Kotlin'
licenses = ['Apache-2.0']
labels = ['kotlin']
version {
name = project.version //Bintray logical version name
}
}
}

Binary file not shown.

View file

@ -1,5 +1,6 @@
#Sun Oct 04 21:38:45 EDT 2015
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.3-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-2.9-all.zip

126
gradlew vendored
View file

@ -1,20 +1,4 @@
#!/usr/bin/env sh
#
# Copyright 2015 the original author or authors.
#
# 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.
#
#!/usr/bin/env bash
##############################################################################
##
@ -22,6 +6,47 @@
##
##############################################################################
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn ( ) {
echo "$*"
}
die ( ) {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
esac
# For Cygwin, ensure paths are in UNIX format before anything is touched.
if $cygwin ; then
[ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
fi
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
@ -36,49 +61,9 @@ while [ -h "$PRG" ] ; do
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
cd "`dirname \"$PRG\"`/" >&-
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
cd "$SAVED" >&-
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
@ -105,7 +90,7 @@ location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
@ -129,7 +114,6 @@ fi
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
@ -170,19 +154,11 @@ if $cygwin ; then
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
function splitJvmOpts() {
JVM_OPTS=("$@")
}
APP_ARGS=$(save "$@")
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@"
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"

30
gradlew.bat vendored
View file

@ -1,19 +1,3 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem http://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@ -24,14 +8,14 @@
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
@ -62,9 +46,10 @@ echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
@rem Get command-line arguments, handling Windowz variants
if not "%OS%" == "Windows_NT" goto win9xME_args
if "%@eval[2+2]" == "4" goto 4NT_args
:win9xME_args
@rem Slurp the command line arguments.
@ -75,6 +60,11 @@ set _SKIP=2
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
goto execute
:4NT_args
@rem Get arguments from the 4NT Shell from JP Software
set CMD_LINE_ARGS=%$
:execute
@rem Setup the command line

View file

@ -1,78 +0,0 @@
Kobalt's incremental task algorithm is not based on timestamps but on checksums.
You make a task incremental by declaring it `@IncrementalTask` instead of `@Task`. The only other difference is that instead of returning a `TaskResult`, incremental tasks return an `IncrementalTaskInfo`:
```
class IncrementalTaskInfo(
val inputChecksum: String?,
val outputChecksum: String?,
val task: (Project) -> TaskResult)
```
This class contains three fields:
- A task closure, which is your effective task: `(Project) -> TaskResult`
- An input checksum (`String?`)
- An output checksum (`String?`)
These checksums are numbers that each task calculates for their input and output. For example, the `"compile"` task calculates an MD5 checksum of all the source files. Similarly, the output checksum is for produced artifacts, e.g. checksum of `.class` files or `.jar` files, etc...
Example of an incremental task:
```
@IncrementalTask(name = JvmCompilerPlugin.TASK_COMPILE, description = "Compile the project")
fun taskCompile(project: Project) : IncrementalTaskInfo {
val inputChecksum = Md5.toMd5Directories(project.sourceDirectories.map {
File(project.directory, it)
})
return IncrementalTaskInfo(
inputChecksum = inputChecksum,
outputChecksum = "1",
task = { project -> doTaskCompile(project) }
)
}
```
The advantage of checksums is that they take care of all the scenarios that would cause that task to run:
- A file was modified
- A file was added
- A file was removed
The output checksum covers the case where the input is unchanged but the output files were deleted or modified. If
both the input and output checksums match the previous run, it's extremely likely that the task has nothing to do.
Another advantage of checksums is that they are generic and not necessarily tied to files. For example, a Kobalt task might perform some network operations and return a checksum based on a network result to avoid performing a more expensive operation (e.g. don't download a file from a server if it hasn't changed).
Internally, Kobalt maintains information about all the checksums and tasks that it has seen in a file `.kobalt/build-info.json`. Whenever an incremental task is about to run, Kobalt compares its input and output checksums to the ones from the previous run and if any differs, that task is run. Otherwise, it's skipped.
Example timings for Kobalt:
| Task | First run | Second run |
| ---- | --------- | ---------- |
| kobalt-wrapper:compile | 627 ms | 22 ms |
| kobalt-wrapper:assemble | 9 ms | 9 ms |
| kobalt-plugin-api:compile | 10983 ms | 54 ms |
| kobalt-plugin-api:assemble | 1763 ms | 154 ms |
| kobalt:compile | 11758 ms | 11 ms |
| kobalt:assemble | 42333 ms | 2130 ms |
| | 70 seconds | 2 seconds |
Android (u2020):
| Task | First run | Second run |
| ---- | --------- | ---------- |
| u2020:generateRInternalDebug | 32350 ms | 1652 ms |
| u2020:compileInternalDebug | 3629 ms | 24 ms |
| u2020:retrolambdaInternalDebug | 668 ms | 473 ms |
| u2020:generateDexInternalDebug | 6130 ms |55 ms |
| u2020:signApkInternalDebug | 449 ms | 404 ms |
| u2020:assembleInternalDebug | 0 ms | 0 ms |
| | 43 seconds | 2 seconds |

23
kobalt.iml Normal file
View file

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<module external.linked.project.id="kobalt" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="0.121" type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="false">
<output url="file://$MODULE_DIR$/build/classes/main" />
<output-test url="file://$MODULE_DIR$/build/classes/test" />
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/kotlin" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/test/kotlin" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/test/resources" type="java-test-resource" />
<excludeFolder url="file://$MODULE_DIR$/.gradle" />
<excludeFolder url="file://$MODULE_DIR$/build" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" scope="TEST" name="kobalt (Test)" level="project" />
<orderEntry type="library" name="kobalt (Compile)" level="project" />
<orderEntry type="module" module-name="wrapper" />
</component>
</module>

View file

@ -1,61 +1,23 @@
import com.beust.kobalt.*
import com.beust.kobalt.api.Project
import com.beust.kobalt.api.*
import com.beust.kobalt.api.annotation.Task
import com.beust.kobalt.plugin.application.application
import com.beust.kobalt.plugin.java.javaCompiler
import com.beust.kobalt.plugin.kotlin.kotlinCompiler
import com.beust.kobalt.plugin.java.*
import com.beust.kobalt.plugin.kotlin.*
import com.beust.kobalt.plugin.packaging.assemble
import com.beust.kobalt.plugin.publish.autoGitTag
import com.beust.kobalt.plugin.publish.bintray
import com.beust.kobalt.plugin.publish.github
import org.apache.maven.model.Developer
import org.apache.maven.model.License
import org.apache.maven.model.Model
import org.apache.maven.model.Scm
import com.beust.kobalt.plugin.publish.jcenter
import com.beust.kobalt.test
import java.io.File
import java.nio.file.Files
import java.nio.file.Paths
import java.nio.file.StandardCopyOption
import java.util.zip.ZipEntry
import java.util.zip.ZipOutputStream
val bs = buildScript {
repos("https://dl.bintray.com/cbeust/maven")
}
val r = repos("http://dl.bintray.com/kotlin/kotlinx.dom")
object Versions {
val kotlin = "1.2.71"
val okhttp = "3.9.1"
val okio = "1.13.0"
val retrofit = "2.3.0"
val gson = "2.8.2"
val guice = "4.2.2"
val maven = "3.5.2"
val mavenResolver = "1.1.0"
val slf4j = "1.7.3"
val aether = "1.0.2.v20150114"
val testng = "6.12"
val jcommander = "1.72"
// JUnit 5
val junit = "4.12"
val junitPlatform = "1.1.0"
val junitJupiter = "5.1.0"
}
fun mavenResolver(vararg m: String)
= m.map { "org.apache.maven.resolver:maven-resolver-$it:${Versions.mavenResolver}" }
.toTypedArray()
fun aether(vararg m: String)
= m.map { "org.eclipse.aether:aether-$it:${Versions.aether}" }
.toTypedArray()
val wrapper = project {
val wrapper = javaProject {
name = "kobalt-wrapper"
group = "com.beust"
artifactId = name
version = readVersion()
directory = "modules/wrapper"
@ -64,7 +26,6 @@ val wrapper = project {
}
assemble {
jar { }
jar {
name = projectName + ".jar"
manifest {
@ -73,141 +34,56 @@ val wrapper = project {
}
}
productFlavor("dev") {
}
buildType("debug") {
}
application {
mainClass = "com.beust.kobalt.wrapper.Main"
}
bintray {
publish = true
sign = true
}
pom = createPom(name, "Wrapper for Kobalt")
}
val kobaltPluginApi = project {
name = "kobalt-plugin-api"
group = "com.beust"
artifactId = name
version = readVersion()
directory = "modules/kobalt-plugin-api"
description = "A build system in Kotlin"
url = "https://beust.com/kobalt"
pom = createPom(name, "A build system in Kotlin")
dependencies {
compile(
"org.jetbrains.kotlin:kotlin-stdlib:${Versions.kotlin}",
"com.google.inject:guice:${Versions.guice}",
"com.google.inject.extensions:guice-assistedinject:4.1.0",
"javax.inject:javax.inject:1",
"com.google.guava:guava:27.0.1-jre",
"org.apache.maven:maven-model:${Versions.maven}",
"io.reactivex:rxjava:1.3.3",
"com.squareup.okio:okio:${Versions.okio}",
"com.google.code.gson:gson:${Versions.gson}",
"com.squareup.okhttp3:okhttp:${Versions.okhttp}",
"com.squareup.retrofit2:retrofit:${Versions.retrofit}",
"com.squareup.retrofit2:converter-gson:${Versions.retrofit}",
"com.beust:jcommander:${Versions.jcommander}",
"org.eclipse.jgit:org.eclipse.jgit:4.9.0.201710071750-r",
"org.slf4j:slf4j-simple:${Versions.slf4j}",
*mavenResolver("api", "spi", "util", "impl", "connector-basic", "transport-http", "transport-file"),
"org.apache.maven:maven-aether-provider:3.3.9",
"org.testng.testng-remote:testng-remote:1.3.2",
"org.testng:testng:${Versions.testng}",
"org.junit.platform:junit-platform-surefire-provider:${Versions.junitPlatform}",
"org.junit.platform:junit-platform-runner:${Versions.junitPlatform}",
"org.junit.platform:junit-platform-engine:${Versions.junitPlatform}",
"org.junit.platform:junit-platform-console:${Versions.junitPlatform}",
"org.junit.jupiter:junit-jupiter-engine:${Versions.junitJupiter}",
"org.junit.vintage:junit-vintage-engine:${Versions.junitJupiter}",
"org.apache.commons:commons-compress:1.15",
"commons-io:commons-io:2.6",
// Java 9
"javax.xml.bind:jaxb-api:2.3.0"
)
exclude(*aether("impl", "spi", "util", "api"))
}
assemble {
mavenJars {
fatJar = true
manifest {
attributes("Main-Class", "com.beust.kobalt.MainKt")
}
}
}
kotlinCompiler {
args("nowarn")
}
bintray {
publish = true
}
}
val kobaltApp = project(kobaltPluginApi, wrapper) {
val kobalt = kotlinProject(wrapper) {
name = "kobalt"
group = "com.beust"
artifactId = name
version = readVersion()
dependencies {
// Used by the plugins
compile("org.jetbrains.kotlin:kotlin-compiler-embeddable:${Versions.kotlin}")
// Used by the main app
compile(
"org.jetbrains.kotlin:kotlin-stdlib:${Versions.kotlin}",
"com.github.spullara.mustache.java:compiler:0.9.5",
"javax.inject:javax.inject:1",
"com.google.inject:guice:${Versions.guice}",
"com.google.inject.extensions:guice-assistedinject:${Versions.guice}",
"com.beust:jcommander:${Versions.jcommander}",
"org.apache.maven:maven-model:${Versions.maven}",
"com.google.code.findbugs:jsr305:3.0.2",
"com.google.code.gson:gson:${Versions.gson}",
"com.squareup.retrofit2:retrofit:${Versions.retrofit}",
"com.squareup.retrofit2:converter-gson:${Versions.retrofit}",
// "com.squareup.okhttp3:okhttp-ws:3.4.2",
"biz.aQute.bnd:biz.aQute.bndlib:3.5.0",
*mavenResolver("spi"),
"com.squareup.okhttp3:logging-interceptor:3.9.0",
"com.sparkjava:spark-core:2.6.0",
"org.codehaus.groovy:groovy:2.4.12",
// Java 9
"javax.xml.bind:jaxb-api:2.3.0",
"com.sun.xml.bind:jaxb-impl:2.3.0",
"com.sun.xml.bind:jaxb-core:2.3.0",
"com.sun.activation:javax.activation:1.2.0"
// "org.eclipse.jetty:jetty-server:${Versions.jetty}",
// "org.eclipse.jetty:jetty-servlet:${Versions.jetty}",
// "org.glassfish.jersey.core:jersey-server:${Versions.jersey}",
// "org.glassfish.jersey.containers:jersey-container-servlet-core:${Versions.jersey}",
// "org.glassfish.jersey.containers:jersey-container-jetty-http:${Versions.jersey}",
// "org.glassfish.jersey.media:jersey-media-moxy:${Versions.jersey}",
// "org.wasabi:wasabi:0.1.182"
)
}
description = "A build system in Kotlin"
url = "http://beust.com/kobalt"
licenses = arrayListOf(License("Apache 2.0", "http://www.apache.org/licenses/LICENSE-2.0"))
scm = Scm(url = "http://github.com/cbeust/kobalt",
connection = "https://github.com/cbeust/kobalt.git",
developerConnection = "git@github.com:cbeust/kobalt.git")
dependenciesTest {
compile("org.jetbrains.kotlin:kotlin-test:${Versions.kotlin}",
"org.testng:testng:${Versions.testng}",
"org.assertj:assertj-core:3.8.0",
*mavenResolver("util")
)
compile("org.testng:testng:6.9.9")
}
dependencies {
compile("org.jetbrains.kotlin:kotlin-stdlib:1.0.0-beta-2423",
"org.jetbrains.kotlin:kotlin-compiler-embeddable:1.0.0-beta-2423",
"org.jetbrains.dokka:dokka-fatjar:0.9.2",
"org.jetbrains.kotlinx:kotlinx.dom:0.0.4",
"com.beust:jcommander:1.48",
"com.squareup.okhttp:okhttp:2.5.0",
"org.jsoup:jsoup:1.8.3",
"com.google.inject:guice:4.0",
"com.google.inject.extensions:guice-assistedinject:4.0",
"javax.inject:javax.inject:1",
"com.google.guava:guava:19.0-rc2",
"org.apache.maven:maven-model:3.3.3",
"com.github.spullara.mustache.java:compiler:0.9.1",
"io.reactivex:rxjava:1.0.16",
"com.google.code.gson:gson:2.4",
"com.squareup.retrofit:retrofit:1.9.0",
"com.squareup.okio:okio:1.6.0"
)
}
assemble {
mavenJars {
fatJar = true
@ -216,111 +92,63 @@ val kobaltApp = project(kobaltPluginApi, wrapper) {
}
}
zip {
val dir = "kobalt-$version"
val files = listOf(
"dist", "$dir/bin", "kobaltw",
"dist", "$dir/bin", "kobaltw.bat",
"$buildDirectory/libs", "$dir/kobalt/wrapper", "$projectName-$version.jar",
"modules/wrapper/$buildDirectory/libs", "$dir/kobalt/wrapper", "$projectName-wrapper.jar")
(0 .. files.size - 1 step 3).forEach { i ->
include(from(files[i]), to(files[i + 1]), files[i + 2])
}
// Package the sources
val currentDir = Paths.get(".").toAbsolutePath().normalize().toString()
zipFolders("$currentDir/$buildDirectory/libs/all-sources/$projectName-$version-sources.jar",
"$currentDir/$directory/src/main/kotlin",
"$currentDir/${kobaltPluginApi.directory}/src/main/kotlin")
include(from("$buildDirectory/libs/all-sources"), to("$dir/kobalt/wrapper"), "$projectName-$version-sources.jar")
include("kobaltw")
include(from("$buildDirectory/libs"), to("kobalt/wrapper"),
"$projectName-$version.jar")
include(from("modules/wrapper/$buildDirectory/libs"), to("kobalt/wrapper"),
"$projectName-wrapper.jar")
}
}
kotlinCompiler {
args("nowarn")
// install {
// libDir = "lib-test"
// }
test {
args("-log", "1", "src/test/resources/testng.xml")
}
bintray {
publish = true
kotlinCompiler {
args("-nowarn")
}
// dokka {
// outputFormat = "markdown"
// }
//
// dokka {
// outputFormat = "html"
// }
github {
file("$buildDirectory/libs/$name-$version.zip", "$name/$version/$name-$version.zip")
}
test {
args("-log", "2", "src/test/resources/testng.xml")
}
autoGitTag {
enabled = true
}
}
fun zipFolders(zipFilePath: String, vararg foldersPath: String) {
val zip = Paths.get(zipFilePath)
Files.deleteIfExists(zip)
Files.createDirectories(zip.parent)
val zipPath = Files.createFile(zip)
ZipOutputStream(Files.newOutputStream(zipPath)).use {
foldersPath.map {Paths.get(it)}.forEach { folderPath ->
Files.walk(folderPath)
.filter { path -> !Files.isDirectory(path) }
.forEach { path ->
val zipEntry = ZipEntry(folderPath.relativize(path).toString())
try {
it.putNextEntry(zipEntry)
Files.copy(path, it)
it.closeEntry()
} catch (e: Exception) {
}
}
}
jcenter {
publish = true
}
}
fun readVersion() : String {
val localFile =
listOf("src/main/resources/kobalt.properties",
homeDir("kotlin", "kobalt", "src/main/resources/kobalt.properties")).first { File(it).exists() }
with(java.util.Properties()) {
load(java.io.FileReader(localFile))
return getProperty("kobalt.version")
val p = java.util.Properties()
var localFile = java.io.File("src/main/resources/kobalt.properties")
if (! localFile.exists()) {
localFile = File(homeDir("kotlin", "kobalt", "src/main/resources/kobalt.properties"))
}
p.load(java.io.FileReader(localFile))
return p.getProperty("kobalt.version")
}
@Task(name = "copyVersionForWrapper", reverseDependsOn = arrayOf("assemble"), runAfter = arrayOf("clean"))
@Task(name = "copyVersionForWrapper", runBefore = arrayOf("assemble"), runAfter = arrayOf("compile"), description = "")
fun taskCopyVersionForWrapper(project: Project) : TaskResult {
if (project.name == "kobalt-wrapper") {
val toString = "modules/wrapper/kobaltBuild/classes"
File(toString).mkdirs()
val from = Paths.get("src/main/resources/kobalt.properties")
val to = Paths.get("$toString/kobalt.properties")
// Only copy if necessary so we don't break incremental compilation
if (! to.toFile().exists() || (from.toFile().readLines() != to.toFile().readLines())) {
Files.copy(from,
to,
StandardCopyOption.REPLACE_EXISTING)
}
Files.copy(from,
to,
StandardCopyOption.REPLACE_EXISTING)
}
return TaskResult()
}
fun createPom(projectName: String, projectDescription: String) = Model().apply {
name = projectName
description = projectDescription
url = "https://beust.com/kobalt"
licenses = listOf(License().apply {
name = "Apache-2.0"
url = "https://www.apache.org/licenses/LICENSE-2.0"
})
scm = Scm().apply {
url = "https://github.com/cbeust/kobalt"
connection = "https://github.com/cbeust/kobalt.git"
developerConnection = "git@github.com:cbeust/kobalt.git"
}
developers = listOf(Developer().apply {
name = "Cedric Beust"
email = "cedric@beust.com"
})
}

Binary file not shown.

View file

@ -1 +1 @@
kobalt.version=1.0.122
kobalt.version=0.313

View file

@ -1,2 +1,2 @@
#!/usr/bin/env sh
java -jar "`dirname "$0"`/kobalt/wrapper/kobalt-wrapper.jar" $*
#!/usr/bin/env bash
java -jar $(dirname $0)/kobalt/wrapper/kobalt-wrapper.jar $*

View file

@ -1,8 +0,0 @@
#!/usr/bin/env sh
JAR=$(ls -1 -t kobaltBuild/libs/*.jar | grep -Ev "(sources|javadoc)" | head -1)
TEMPDIR=$(mktemp -d)
cp -pf "$JAR" "$TEMPDIR"
TEMPJAR=$TEMPDIR/$(basename "$JAR")
export KOBALT_JAR=$TEMPJAR
java -jar "$TEMPJAR" "$@"
rm -rf "$TEMPDIR"

View file

@ -1,4 +1,2 @@
@echo off
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
java -jar "%DIRNAME%/kobalt/wrapper/kobalt-wrapper.jar" %*
@echo off
java -jar %~dp0/kobalt/wrapper/kobalt-wrapper.jar %*

View file

@ -1,85 +0,0 @@
plugins {
id 'org.jetbrains.kotlin.jvm' version '1.2.71'
id 'com.github.johnrengelman.shadow' version '5.0.0'
}
dependencies {
implementation "biz.aQute.bnd:biz.aQute.bndlib:$bndlib"
implementation "com.google.code.findbugs:jsr305:$findbugs"
implementation "com.sparkjava:spark-core:$spark"
implementation "com.squareup.okhttp3:logging-interceptor:$okhttp"
implementation 'commons-io:commons-io:2.6'
implementation 'io.reactivex:rxjava:1.3.3'
implementation "javax.inject:javax.inject:$inject"
implementation "javax.xml.bind:jaxb-api:$jaxb"
implementation 'org.apache.commons:commons-compress:1.15'
implementation 'org.apache.maven:maven-aether-provider:3.3.9'
implementation "org.apache.maven.resolver:maven-resolver-api:$mavenResolver"
implementation "org.apache.maven.resolver:maven-resolver-connector-basic:$mavenResolver"
implementation "org.apache.maven.resolver:maven-resolver-impl:$mavenResolver"
implementation "org.apache.maven.resolver:maven-resolver-spi:$mavenResolver"
implementation "org.apache.maven.resolver:maven-resolver-transport-file:$mavenResolver"
implementation "org.apache.maven.resolver:maven-resolver-transport-http:$mavenResolver"
implementation "org.apache.maven.resolver:maven-resolver-util:$mavenResolver"
implementation "org.codehaus.groovy:groovy:$groovy"
implementation 'org.eclipse.jgit:org.eclipse.jgit:4.9.0.201710071750-r'
implementation "org.junit.jupiter:junit-jupiter-engine:$junitJupiter"
implementation "org.junit.platform:junit-platform-console:$junitPlatform"
implementation "org.junit.platform:junit-platform-engine:$junitPlatform"
implementation "org.junit.platform:junit-platform-runner:$junitPlatform"
implementation "org.junit.platform:junit-platform-surefire-provider:$junitPlatform"
implementation "org.junit.vintage:junit-vintage-engine:$junitJupiter"
implementation "org.slf4j:slf4j-simple:$slf4j"
implementation "org.testng:testng:$testng"
implementation 'org.testng.testng-remote:testng-remote:1.3.2'
implementation "com.beust:jcommander:$jcommander"
implementation "com.google.code.gson:gson:$gson"
implementation "com.google.inject:guice:$guice"
implementation "com.google.inject.extensions:guice-assistedinject:$guice"
implementation "com.squareup.okio:okio:$okio"
implementation "com.squareup.retrofit2:converter-gson:$retrofit"
implementation "com.squareup.retrofit2:retrofit:$retrofit"
implementation "org.apache.maven:maven-model:$maven"
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin"
}
shadowJar {
classifier = null
}
test {
useTestNG()
}
publishing {
publications {
shadow(MavenPublication) { publication ->
project.shadow.component(publication)
artifact sourcesJar
artifact javadocJar
pom {
name = project.name
description = 'A build system in Kotlin'
url = 'https://beust.com/kobalt'
licenses {
license {
name = 'Apache-2.0'
url = 'https://www.apache.org/licenses/LICENSE-2.0'
}
}
developers {
developer {
name = 'Cedric Beust'
email = 'cedric@beust.com'
}
}
scm {
connection = 'scm:https://github.com/cbeust/kobalt.git'
developerConnection = 'scm:git@github.com:cbeust/kobalt.git'
url = 'https://github.com/cbeust/kobalt'
}
}
}
}
}

View file

@ -1,279 +0,0 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.beust</groupId>
<artifactId>kobalt-pom</artifactId>
<version>1.1.0</version>
<relativePath>../..</relativePath>
</parent>
<artifactId>kobalt-plugin-api</artifactId>
<packaging>jar</packaging>
<version>1.1.0</version>
<dependencies>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib</artifactId>
<version>${kotlin.version}</version>
</dependency>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-aether-provider</artifactId>
<version>3.3.9</version>
<exclusions>
<exclusion>
<groupId>org.eclipse.aether</groupId>
<artifactId>impl</artifactId>
</exclusion>
<exclusion>
<groupId>org.eclipse.aether</groupId>
<artifactId>spi</artifactId>
</exclusion>
<exclusion>
<groupId>org.eclipse.aether</groupId>
<artifactId>util</artifactId>
</exclusion>
<exclusion>
<groupId>org.eclipse.aether</groupId>
<artifactId>api</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.maven.resolver</groupId>
<artifactId>maven-resolver-api</artifactId>
<version>${mavenresolver.version}</version>
</dependency>
<dependency>
<groupId>org.apache.maven.resolver</groupId>
<artifactId>maven-resolver-spi</artifactId>
<version>${mavenresolver.version}</version>
</dependency>
<dependency>
<groupId>org.apache.maven.resolver</groupId>
<artifactId>maven-resolver-util</artifactId>
<version>${mavenresolver.version}</version>
</dependency>
<dependency>
<groupId>org.apache.maven.resolver</groupId>
<artifactId>maven-resolver-impl</artifactId>
<version>${mavenresolver.version}</version>
</dependency>
<dependency>
<groupId>org.apache.maven.resolver</groupId>
<artifactId>maven-resolver-connector-basic</artifactId>
<version>${mavenresolver.version}</version>
</dependency>
<dependency>
<groupId>org.apache.maven.resolver</groupId>
<artifactId>maven-resolver-transport-http</artifactId>
<version>${mavenresolver.version}</version>
</dependency>
<dependency>
<groupId>org.apache.maven.resolver</groupId>
<artifactId>maven-resolver-transport-file</artifactId>
<version>${mavenresolver.version}</version>
</dependency>
<dependency>
<groupId>io.reactivex</groupId>
<artifactId>rxjava</artifactId>
<version>1.3.3</version>
</dependency>
<dependency>
<groupId>com.squareup.okio</groupId>
<artifactId>okio</artifactId>
<version>${okio.version}</version>
</dependency>
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.google.inject</groupId>
<artifactId>guice</artifactId>
<version>4.2.2</version>
</dependency>
<dependency>
<groupId>com.google.inject.extensions</groupId>
<artifactId>guice-assistedinject</artifactId>
<version>4.2.2</version>
</dependency>
<dependency>
<groupId>com.beust</groupId>
<artifactId>jcommander</artifactId>
<version>1.72</version>
</dependency>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-model</artifactId>
<version>3.5.2</version>
</dependency>
<dependency>
<groupId>com.google.code.findbugs</groupId>
<artifactId>jsr305</artifactId>
<version>3.0.2</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.2</version>
</dependency>
<dependency>
<groupId>com.squareup.retrofit2</groupId>
<artifactId>retrofit</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>com.squareup.retrofit2</groupId>
<artifactId>converter-gson</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>biz.aQute.bnd</groupId>
<artifactId>biz.aQute.bndlib</artifactId>
<version>3.5.0</version>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>logging-interceptor</artifactId>
<version>${okhttp3.version}</version>
</dependency>
<dependency>
<groupId>com.sparkjava</groupId>
<artifactId>spark-core</artifactId>
<version>2.6.0</version>
</dependency>
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy</artifactId>
<version>2.4.12</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-compress</artifactId>
<version>1.15</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-surefire-provider</artifactId>
<version>${junit.version}</version>
</dependency>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-runner</artifactId>
<version>${junit.version}</version>
</dependency>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-engine</artifactId>
<version>${junit.version}</version>
</dependency>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-console</artifactId>
<version>${junit.version}</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>${junitJupiter.version}</version>
</dependency>
<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<version>${junitJupiter.version}</version>
</dependency>
<dependency>
<groupId>org.testng.testng-remote</groupId>
<artifactId>testng-remote</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>${testng.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit</artifactId>
<version>4.9.0.201710071750-r</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>${slf4j.version}</version>
</dependency>
<!-- java 9 -->
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<version>${kotlin.version}</version>
<executions>
<execution>
<id>compile</id>
<goals> <goal>compile</goal> </goals>
<configuration>
<sourceDirs>
<sourceDir>${project.basedir}/src/main/kotlin</sourceDir>
</sourceDirs>
</configuration>
</execution>
<execution>
<id>test-compile</id>
<goals> <goal>test-compile</goal> </goals>
<configuration>
<sourceDirs>
<sourceDir>${project.basedir}/src/test/kotlin</sourceDir>
</sourceDirs>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5.1</version>
<executions>
<!-- Replacing default-compile as it is treated specially by maven -->
<execution>
<id>default-compile</id>
<phase>none</phase>
</execution>
<!-- Replacing default-testCompile as it is treated specially by maven -->
<execution>
<id>default-testCompile</id>
<phase>none</phase>
</execution>
<execution>
<id>java-compile</id>
<phase>compile</phase>
<goals> <goal>compile</goal> </goals>
</execution>
<execution>
<id>java-test-compile</id>
<phase>test-compile</phase>
<goals> <goal>testCompile</goal> </goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View file

@ -1,21 +0,0 @@
package com.beust.kobalt
import com.beust.kobalt.api.KobaltContext
import com.beust.kobalt.api.Project
import com.beust.kobalt.archive.Zip
import com.beust.kobalt.misc.KFiles
import java.io.File
interface ArchiveGenerator {
fun findIncludedFiles(project: Project, context: KobaltContext, zip: Zip) : List<IncludedFile>
val suffix: String
fun generateArchive(project: Project, context: KobaltContext, zip: Zip, files: List<IncludedFile>) : File
fun fullArchiveName(project: Project, context: KobaltContext, archiveName: String?) : File {
val fullArchiveName = context.variant.archiveName(project, archiveName, suffix)
val archiveDir = File(KFiles.libsDir(project))
val result = File(archiveDir.path, fullArchiveName)
return result
}
}

View file

@ -1,111 +0,0 @@
package com.beust.kobalt
import com.beust.jcommander.Parameter
class Args {
@Parameter
var targets: List<String> = arrayListOf()
@Parameter(names = arrayOf("-bf", "--buildFile"), description = "The build file")
var buildFile: String? = "kobalt/src/Build.kt"
@Parameter(names = arrayOf("--checkVersions"), description = "Check if there are any newer versions of the " +
"dependencies")
var checkVersions = false
@Parameter(names = arrayOf("--client"))
var client: Boolean = false
@Parameter(names = arrayOf("--dev"), description = "Turn on dev mode, resulting in a more verbose log output")
var dev: Boolean = false
@Parameter(names = arrayOf("--download"), description = "Force a download from the downloadUrl in the wrapper")
var download: Boolean = false
@Parameter(names = arrayOf("--downloadSources"),
description = "Force a download of sources and javadocs when resolving dependencies")
var downloadSources: Boolean = false
@Parameter(names = arrayOf("--dryRun"), description = "Display all the tasks that will get run without " +
"actually running them")
var dryRun: Boolean = false
@Parameter(names = arrayOf("--force"), description = "Force a new server to be launched even if another one" +
" is already running")
var force: Boolean = false
@Parameter(names = arrayOf("--gc"), description = "Delete old files")
var gc: Boolean = false
@Parameter(names = arrayOf("--help", "--usage"), description = "Display the help")
var usage: Boolean = false
@Parameter(names = arrayOf("-i", "--init"), description = "Invoke the templates named, separated by a comma")
var templates: String? = null
@Parameter(names = arrayOf("--listTemplates"), description = "List the available templates")
var listTemplates: Boolean = false
@Parameter(names = arrayOf("--log"), description = "Define the log level " +
"(${Constants.LOG_QUIET_LEVEL}-${Constants.LOG_MAX_LEVEL})")
var log: Int = Constants.LOG_DEFAULT_LEVEL
@Parameter(names = arrayOf("--logTags"),
description = "Comma-separated list of tags to enable logging for")
var logTags: String = ""
@Parameter(names = arrayOf("--forceIncremental"),
description = "Force the build to be incremental even if the build file was modified")
var forceIncremental: Boolean = false
@Parameter(names = arrayOf("--noIncremental"), description = "Turn off incremental builds")
var noIncremental: Boolean = false
@Parameter(names = arrayOf("--offline"), description = "Don't try to download dependencies even if there is no cached version")
var offline: Boolean = false
@Parameter(names = arrayOf("--plugins"), description = "Comma-separated list of plug-in Maven id's")
var pluginIds: String? = null
@Parameter(names = arrayOf("--pluginJarFiles"), description = "Comma-separated list of plug-in jar files")
var pluginJarFiles: String? = null
@Parameter(names = arrayOf("--port"), description = "Port, if --server was specified")
var port: Int? = null
@Parameter(names = arrayOf("--profiles"), description = "Comma-separated list of profiles to run")
var profiles: String? = null
@Parameter(names = arrayOf("--profiling"), description = "Display task timings at the end of the build")
var profiling: Boolean = false
@Parameter(names = arrayOf("--resolve"),
description = "Resolve the given dependency and display its tree")
var dependency: String? = null
@Parameter(names = arrayOf("--projectInfo"), description = "Display information about the current projects")
var projectInfo: Boolean = false
@Parameter(names = arrayOf("--noIncrementalKotlin"), description = "Disable incremental Kotlin compilation")
var noIncrementalKotlin: Boolean = false
companion object {
const val SEQUENTIAL = "--sequential"
}
@Parameter(names = arrayOf(Args.SEQUENTIAL), description = "Build all the projects in sequence")
var sequential: Boolean = false
@Parameter(names = arrayOf("--server"), description = "Run in server mode")
var serverMode: Boolean = false
@Parameter(names = arrayOf("--tasks"), description = "Display the tasks available for this build")
var tasks: Boolean = false
@Parameter(names = arrayOf("--update"), description = "Update to the latest version of Kobalt")
var update: Boolean = false
@Parameter(names = arrayOf("--version"), description = "Display the current version of Kobalt")
var version: Boolean = false
}

View file

@ -1,164 +0,0 @@
package com.beust.kobalt
import java.util.*
/**
* Make Kobalt's output awesome and unique.
*
* I spend so much time staring at build outputs I decided I might as well make them pretty.
* Note that I also experimented with colors but it's hard to come up with a color scheme that
* will work with all the various backgrounds developers use, so I decided to be conservative
* and stick to simple red/yellow for errors and warnings.
*
* @author Cedric Beust <cedric@beust.com>
* @since 10/1/2015
*/
class AsciiArt {
companion object {
private val BANNERS = arrayOf(
" __ __ __ __ __ \n" +
" / //_/ ____ / /_ ____ _ / / / /_\n" +
" / ,< / __ \\ / __ \\ / __ `/ / / / __/\n" +
" / /| | / /_/ / / /_/ // /_/ / / / / /_ \n" +
" /_/ |_| \\____/ /_.___/ \\__,_/ /_/ \\__/ ",
" _ __ _ _ _ \n" +
" | |/ / ___ | |__ __ _ | | | |_ \n" +
" | ' / / _ \\ | '_ \\ / _` | | | | __|\n" +
" | . \\ | (_) | | |_) | | (_| | | | | |_ \n" +
" |_|\\_\\ \\___/ |_.__/ \\__,_| |_| \\__| "
)
val banner : String get() = BANNERS[Random().nextInt(BANNERS.size)]
// fun box(s: String) : List<String> = box(listOf(s))
val horizontalSingleLine = "\u2500\u2500\u2500\u2500\u2500"
val horizontalDoubleLine = "\u2550\u2550\u2550\u2550\u2550"
val verticalBar = "\u2551"
// fun horizontalLine(n: Int) = StringBuffer().apply {
// repeat(n, { append("\u2500") })
// }.toString()
// Repeat
fun r(n: Int, w: String) : String {
with(StringBuffer()) {
repeat(n, { append(w) })
return toString()
}
}
val h = "\u2550"
val ul = "\u2554"
val ur = "\u2557"
val bottomLeft = "\u255a"
val bottomRight = "\u255d"
// Bottom left with continuation
val bottomLeft2 = "\u2560"
// Bottom right with continuation
val bottomRight2 = "\u2563"
fun upperBox(max: Int) = ul + r(max + 2, h) + ur
fun lowerBox(max: Int, bl: String = bottomLeft, br : String = bottomRight) = bl + r(max + 2, h) + br
private fun box(strings: List<String>, bl: String = bottomLeft, br: String = bottomRight) : List<String> {
val v = verticalBar
val maxString: String = strings.maxBy { it.length } ?: ""
val max = maxString.length
val result = arrayListOf(upperBox(max))
result.addAll(strings.map { "$v ${center(it, max - 2)} $v" })
result.add(lowerBox(max, bl, br))
return result
}
fun logBox(strings: List<String>, bl: String = bottomLeft, br: String = bottomRight, indent: Int = 0): String {
return buildString {
val boxLines = box(strings, bl, br)
boxLines.withIndex().forEach { iv ->
append(fill(indent)).append(iv.value)
if (iv.index < boxLines.size - 1) append("\n")
}
}
}
fun logBox(s: String, bl: String = bottomLeft, br: String = bottomRight, indent: Int = 0)
= logBox(listOf(s), bl, br, indent)
fun fill(n: Int) = buildString { repeat(n, { append(" ")})}.toString()
fun center(s: String, width: Int) : String {
val diff = width - s.length
val spaces = diff / 2 + 1
return fill(spaces) + s + fill(spaces + if (diff % 2 == 1) 1 else 0)
}
const val RESET = "\u001B[0m"
const val BLACK = "\u001B[30m"
const val RED = "\u001B[31m"
const val GREEN = "\u001B[32m"
const val YELLOW = "\u001B[33m";
const val BLUE = "\u001B[34m"
const val PURPLE = "\u001B[35m"
const val CYAN = "\u001B[36m"
const val WHITE = "\u001B[37m"
fun wrap(s: CharSequence, color: String) = color + s + RESET
private fun blue(s: CharSequence) = wrap(s, BLUE)
private fun red(s: CharSequence) = wrap(s, RED)
private fun yellow(s: CharSequence) = wrap(s, YELLOW)
fun taskColor(s: CharSequence) = s
fun errorColor(s: CharSequence) = red(s)
fun warnColor(s: CharSequence) = red(s)
}
}
class AsciiTable {
class Builder {
private val headers = arrayListOf<String>()
fun header(name: String) = headers.add(name)
fun headers(vararg names: String) = headers.addAll(names)
private val widths = arrayListOf<Int>()
fun columnWidth(w: Int) : Builder {
widths.add(w)
return this
}
private val rows = arrayListOf<List<String>>()
fun addRow(row: List<String>) = rows.add(row)
private fun col(width: Int, s: String) : String {
val format = " %1\$-${width.toString()}s"
val result = String.format(format, s)
return result
}
val vb = AsciiArt.verticalBar
fun build() : String {
val formattedHeaders =
headers.mapIndexed { index, s ->
val s2 = col(widths[index], s)
s2
}.joinToString(vb)
val result = StringBuffer().apply {
append(AsciiArt.logBox(formattedHeaders, AsciiArt.bottomLeft2, AsciiArt.bottomRight2))
append("\n")
}
var lineLength = 0
rows.forEachIndexed { _, row ->
val formattedRow = row.mapIndexed { i, s -> col(widths[i], s) }.joinToString(vb)
val line = "$vb $formattedRow $vb"
result.append(line).append("\n")
lineLength = line.length
}
result.append(AsciiArt.lowerBox(lineLength - 4))
return result.toString()
}
}
}

View file

@ -1,145 +0,0 @@
package com.beust.kobalt
import com.beust.kobalt.api.IClasspathDependency
import com.beust.kobalt.api.Kobalt
import com.beust.kobalt.api.annotation.Directive
import com.beust.kobalt.internal.KobaltSettings
import com.beust.kobalt.internal.PluginInfo
import com.beust.kobalt.maven.DependencyManager
import com.beust.kobalt.maven.dependency.FileDependency
import com.beust.kobalt.misc.KobaltLogger
import org.eclipse.aether.repository.Proxy
import java.io.File
import java.net.InetSocketAddress
var BUILD_SCRIPT_CONFIG : BuildScriptConfig? = null
class BuildScriptConfig {
/** The list of repos used to locate plug-ins. */
@Directive
fun repos(vararg r: String) = newRepos(*r)
/** The list of plug-ins to use for this build file. */
@Directive
fun plugins(vararg pl: String) = newPlugins(*pl)
/** The build file classpath. */
@Directive
fun buildFileClasspath(vararg bfc: String) = newBuildFileClasspath(*bfc)
/** Options passed to Kobalt */
@Directive
fun kobaltOptions(vararg options: String) = Kobalt.addKobaltOptions(options)
/** Where to find additional build files */
@Directive
fun buildSourceDirs(vararg dirs: String) = Kobalt.addBuildSourceDirs(dirs)
// The following settings modify the compiler used to compile the build file, which regular users should
// probably never need to do. Projects should use kotlinCompiler { compilerVersion } to configure the
// Kotin compiler for their source files.
var kobaltCompilerVersion : String? = null
var kobaltCompilerRepo: String? = null
var kobaltCompilerFlags: String? = null
}
@Directive
fun homeDir(vararg dirs: String) : String = SystemProperties.homeDir +
File.separator + dirs.toMutableList().joinToString(File.separator)
@Directive
fun file(file: String) : String = FileDependency.PREFIX_FILE + file
fun plugins(vararg dependency : IClasspathDependency) {
dependency.forEach { Plugins.addDynamicPlugin(it) }
}
fun plugins(vararg dependencies : String) {
KobaltLogger.logger.warn("Build.kt",
"Invoking plugins() directly is deprecated, use the buildScript{} directive")
newPlugins(*dependencies)
}
@Directive
fun newPlugins(vararg dependencies : String) {
val factory = Kobalt.INJECTOR.getInstance(DependencyManager::class.java)
dependencies.forEach {
Plugins.addDynamicPlugin(factory.create(it))
}
}
data class ProxyConfig(val host: String = "", val port: Int = 0, val type: String = "", val nonProxyHosts: String = "") {
fun toProxy() = java.net.Proxy(java.net.Proxy.Type.HTTP, InetSocketAddress(host, port))
fun toAetherProxy() = Proxy(type, host, port) // TODO make support for proxy auth
}
data class HostConfig(var url: String = "", var name: String = HostConfig.createRepoName(url),
var username: String? = null, var password: String? = null) {
companion object {
/**
* For repos specified in the build file (repos()) that don't have an associated unique name,
* create such a name from the URL. This is a requirement from Maven Resolver, and failing to do
* this leads to very weird resolution errors.
*/
private fun createRepoName(url: String) = url.replace("/", "_").replace("\\", "_").replace(":", "_")
}
fun hasAuth() : Boolean {
return (! username.isNullOrBlank()) && (! password.isNullOrBlank())
}
override fun toString() : String {
return url + if (username != null) {
"username: $username, password: ***"
} else {
""
}
}
}
fun repos(vararg repos : String) {
KobaltLogger.logger.warn("Build.kt",
"Invoking repos() directly is deprecated, use the buildScript{} directive")
newRepos(*repos)
}
fun newRepos(vararg repos: String) {
repos.forEach { Kobalt.addRepo(HostConfig(it)) }
}
fun buildFileClasspath(vararg deps: String) {
KobaltLogger.logger.warn("Build.kt",
"Invoking buildFileClasspath() directly is deprecated, use the buildScript{} directive")
newBuildFileClasspath(*deps)
}
fun newBuildFileClasspath(vararg deps: String) {
//FIXME newBuildFileClasspath called twice
deps.forEach { Kobalt.addBuildFileClasspath(it) }
}
@Directive
fun authRepos(vararg repos : HostConfig) {
repos.forEach { Kobalt.addRepo(it) }
}
@Directive
fun authRepo(init: HostConfig.() -> Unit) = HostConfig(name = "").apply { init() }
@Directive
fun glob(g: String) : IFileSpec.GlobSpec = IFileSpec.GlobSpec(g)
/**
* The location of the local Maven repository.
*/
@Directive
fun localMaven() : String {
val pluginInfo = Kobalt.INJECTOR.getInstance(PluginInfo::class.java)
val initial = Kobalt.INJECTOR.getInstance(KobaltSettings::class.java).localMavenRepo
val result = pluginInfo.localMavenRepoPathInterceptors.fold(initial) { current, interceptor ->
File(interceptor.repoPath(current.absolutePath))
}
return result.toURI().toString()
}

View file

@ -1,25 +0,0 @@
package com.beust.kobalt
import com.beust.kobalt.misc.KFiles
object Constants {
const val LOG_QUIET_LEVEL = 0
const val LOG_DEFAULT_LEVEL = 1
const val LOG_MAX_LEVEL = 3
val BUILD_FILE_NAME = "Build.kt"
val BUILD_FILE_DIRECTORY = "kobalt/src"
val BUILD_FILE_PATH = KFiles.joinDir(BUILD_FILE_DIRECTORY, BUILD_FILE_NAME)
val KOTLIN_COMPILER_VERSION = "1.2.70"
internal val DEFAULT_REPOS = listOf<HostConfig>(
// "https://maven-central.storage.googleapis.com/",
HostConfig("https://repo1.maven.org/maven2/", "Maven"),
HostConfig("https://jcenter.bintray.com/", "JCenter")
// "https://repository.jetbrains.com/all/", // <-- contains snapshots
// snapshots
// "https://oss.sonatype.org/content/repositories/snapshots/"
// , "https://repository.jboss.org/nexus/content/repositories/root_repository/"
)
}

View file

@ -1,39 +0,0 @@
package com.beust.kobalt
import com.beust.kobalt.api.Kobalt
import com.beust.kobalt.api.Project
import com.beust.kobalt.api.annotation.Directive
import com.beust.kobalt.internal.JvmCompilerPlugin
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
@Directive
fun project(vararg projects: Project, init: Project.() -> Unit): Project {
return Project("").apply {
init()
(Kobalt.findPlugin(JvmCompilerPlugin.PLUGIN_NAME) as JvmCompilerPlugin)
.addDependentProjects(this, projects.toList())
}
}
@Directive
fun buildScript(init: BuildScriptConfig.() -> Unit): BuildScriptConfig {
val buildScriptConfig = BuildScriptConfig().apply { init() }
BUILD_SCRIPT_CONFIG = buildScriptConfig
return buildScriptConfig
}
@Directive
fun profile(): ReadWriteProperty<Nothing?, Boolean> {
val result = object: ReadWriteProperty<Nothing?, Boolean> {
var value: Boolean = false
override operator fun getValue(thisRef: Nothing?, property: KProperty<*>): Boolean {
return value
}
override operator fun setValue(thisRef: Nothing?, property: KProperty<*>, value: Boolean) {
this.value = value
}
}
return result
}

View file

@ -1,8 +0,0 @@
package com.beust.kobalt
class Features {
companion object {
/** If true, uses timestamps to speed up the tasks */
const val USE_TIMESTAMPS = true
}
}

View file

@ -1,102 +0,0 @@
package com.beust.kobalt
import com.beust.kobalt.misc.kobaltLog
import java.io.File
import java.nio.file.*
import java.nio.file.attribute.BasicFileAttributes
/**
* Subclasses of IFileSpec can be turned into a list of files. There are two kings: FileSpec (a single file)
* and GlobSpec (a spec defined by a glob, e.g. ** slash *Test.class)
*/
sealed class IFileSpec {
abstract fun toFiles(baseDir: String?, filePath: String, excludes: List<Glob> = emptyList<Glob>()): List<File>
class FileSpec(val spec: String) : IFileSpec() {
override fun toFiles(baseDir: String?, filePath: String, excludes: List<Glob>) = listOf(File(spec))
override fun toString() = spec
}
/**
* A glob matcher, see http://docs.oracle.com/javase/7/docs/api/java/nio/file/FileSystem.html#getPathMatcher%28java.lang.String%29
*/
class GlobSpec(val spec: List<String>) : IFileSpec() {
constructor(spec: String) : this(arrayListOf(spec))
private fun isIncluded(includeMatchers: Glob, excludes: List<Glob>, rel: Path) : Boolean {
excludes.forEach {
if (it.matches(rel)) {
kobaltLog(3, " Excluding ${rel.toFile()}")
return false
}
}
if (includeMatchers.matches(rel)) {
kobaltLog(3, " Including ${rel.toFile().path}")
return true
}
kobaltLog(2, " Excluding ${rel.toFile()} (not matching any include pattern")
return false
}
override fun toFiles(baseDir: String?, filePath: String, excludes: List<Glob>): List<File> {
val result = arrayListOf<File>()
val includes = Glob(*spec.toTypedArray())
if (File(baseDir, filePath).isDirectory) {
val orgRootDir = (if (File(filePath).isAbsolute) Paths.get(filePath)
else if (baseDir != null) Paths.get(baseDir, filePath)
else Paths.get(filePath)).run { normalize() }
// Paths.get(".").normalize() returns an empty string, which is not a valid file :-(
val rootDir = if (orgRootDir.toFile().path.isEmpty()) Paths.get("./") else orgRootDir
if (rootDir.toFile().exists()) {
Files.walkFileTree(rootDir, object : SimpleFileVisitor<Path>() {
override fun visitFile(p: Path, attrs: BasicFileAttributes): FileVisitResult {
val path = p.normalize()
val rel = orgRootDir.relativize(path)
if (isIncluded(includes, excludes, path)) {
kobaltLog(3, " including file " + rel.toFile() + " from rootDir $rootDir")
result.add(rel.toFile())
}
return FileVisitResult.CONTINUE
}
})
} else {
throw AssertionError("Directory \"$rootDir\" should exist")
}
} else {
if (isIncluded(includes, excludes, Paths.get(filePath))) {
result.add(File(filePath))
}
}
return result
}
override fun toString(): String {
var result = ""
spec.apply {
if (!isEmpty()) {
result += "Included files: " + joinToString { ", " }
}
}
return result
}
}
}
/**
* A Glob is a simple file name matcher.
*/
class Glob(vararg specs: String) {
val matchers = prepareMatchers(specs.toList())
private fun prepareMatchers(specs: List<String>): List<PathMatcher> =
specs.map { it -> FileSystems.getDefault().getPathMatcher("glob:$it") }
fun matches(s: String) = matches(Paths.get(s))
fun matches(path: Path) = matchers.any { it.matches(path) }
}

View file

@ -1,43 +0,0 @@
package com.beust.kobalt
import com.beust.kobalt.api.annotation.Directive
import java.io.File
/**
* Base classes for directives that support install(from,to) (e.g. install{} or jar{}).
*/
open class IncludeFromTo {
/**
* Prefix path to be removed from the zip file. For example, if you add "build/lib/a.jar" to the zip
* file and the excludePrefix is "build/lib", then "a.jar" will be added at the root of the zip file.
*/
val includedFiles = arrayListOf<IncludedFile>()
@Directive
fun from(s: String) = From(s)
@Directive
fun to(s: String) = To(s)
@Directive
fun copy(from: From, to: To) {
val dir = File(from.path).absoluteFile.parentFile
includedFiles.add(IncludedFile(from(dir.absolutePath), to, listOf(IFileSpec.FileSpec(from.path))))
}
@Directive
fun include(vararg files: String) {
includedFiles.add(IncludedFile(files.map { IFileSpec.FileSpec(it) }))
}
@Directive
fun include(from: From, to: To, vararg specs: String) {
includedFiles.add(IncludedFile(from, to, specs.map { IFileSpec.FileSpec(it) }))
}
@Directive
fun include(from: From, to: To, vararg specs: IFileSpec.GlobSpec) {
includedFiles.add(IncludedFile(from, to, listOf(*specs)))
}
}

View file

@ -1,44 +0,0 @@
package com.beust.kobalt
import com.beust.kobalt.misc.KFiles
import com.beust.kobalt.misc.toString
import java.io.File
import java.nio.file.Paths
class IncludedFile(val fromOriginal: From, val toOriginal: To, val specs: List<IFileSpec>,
val expandJarFiles: Boolean = false) {
constructor(specs: List<IFileSpec>, expandJarFiles: Boolean = false) : this(From(""), To(""), specs, expandJarFiles)
fun from(s: String) = File(if (fromOriginal.isCurrentDir()) s else KFiles.joinDir(from, s))
val from: String get() = fromOriginal.path.replace("\\", "/")
fun to(s: String) = File(if (toOriginal.isCurrentDir()) s else KFiles.joinDir(to, s))
val to: String get() = toOriginal.path.replace("\\", "/")
override fun toString() = toString("IncludedFile",
"files - ", specs.map { it.toString() },
"from", from,
"to", to)
fun allFromFiles(directory: String? = null): List<File> {
val result = arrayListOf<File>()
specs.forEach { spec ->
// val fullDir = if (directory == null) from else KFiles.joinDir(directory, from)
spec.toFiles(directory, from).forEach { source ->
result.add(if (source.isAbsolute) source else File(source.path))
}
}
return result.map { Paths.get(it.path).normalize().toFile()}
}
}
open class Direction(open val p: String) {
override fun toString() = path
fun isCurrentDir() = path == "./"
val path: String get() =
if (p.isEmpty()) "./"
else if (p.startsWith("/") || p.endsWith("/")) p
else p + "/"
}
class From(override val p: String) : Direction(p)
class To(override val p: String) : Direction(p)

View file

@ -1,17 +0,0 @@
package com.beust.kobalt
import com.beust.kobalt.api.KobaltContext
import com.beust.kobalt.api.Project
/**
* @param inputChecksum The checksum for the input to this task. It gets compared against the previous checksum
* calculated by Kobalt. If they differ, the task gets run. If they are equal, outputChecksums are then compared.
* @param outputChecksum The checksum for the output of this task. If null, the output is absent
* and the task will be run. If non null, it gets compared against the checksum of the previous run and
* if they differ, the task gets run.
* @param task The task to run.
*/
class IncrementalTaskInfo(val inputChecksum: () -> String?,
val outputChecksum: () -> String?,
val task: (Project) -> TaskResult,
val context: KobaltContext)

View file

@ -1,162 +0,0 @@
package com.beust.kobalt
import com.beust.kobalt.api.KobaltContext
import com.beust.kobalt.api.Project
import com.beust.kobalt.archive.Archives
import com.beust.kobalt.archive.MetaArchive
import com.beust.kobalt.archive.Zip
import com.beust.kobalt.maven.DependencyManager
import com.beust.kobalt.maven.aether.Scope
import com.beust.kobalt.misc.KFiles
import com.beust.kobalt.misc.kobaltLog
import com.google.inject.Inject
import java.io.File
import java.io.FileInputStream
import java.nio.file.Paths
import java.util.jar.Manifest
class JarGenerator @Inject constructor(val dependencyManager: DependencyManager) : ArchiveGenerator {
companion object {
fun findIncludedFiles(directory: String, files: List<IncludedFile>, excludes: List<Glob>,
throwOnError: Boolean = true)
: List<IncludedFile> {
val result = arrayListOf<IncludedFile>()
files.forEach { includedFile ->
val includedSpecs = arrayListOf<IFileSpec>()
includedFile.specs.forEach { spec ->
val fromPath = includedFile.from
if (File(directory, fromPath).exists()) {
spec.toFiles(directory, fromPath).forEach { file ->
val fullFile = File(KFiles.joinDir(directory, fromPath, file.path))
if (! fullFile.exists() && throwOnError) {
throw AssertionError("File should exist: $fullFile")
}
if (!KFiles.isExcluded(fullFile, excludes)) {
val normalized = Paths.get(file.path).normalize().toFile().path
includedSpecs.add(IFileSpec.FileSpec(normalized))
} else {
kobaltLog(2, "Not adding ${file.path} to jar file because it's excluded")
}
}
} else {
kobaltLog(2, " Directory $fromPath doesn't exist, not including it in the jar")
}
}
if (includedSpecs.size > 0) {
kobaltLog(3, "Including specs $includedSpecs")
result.add(IncludedFile(From(includedFile.from), To(includedFile.to), includedSpecs))
}
}
return result
}
}
override val suffix = ".jar"
override fun findIncludedFiles(project: Project, context: KobaltContext, zip: Zip) : List<IncludedFile> {
//
// Add all the applicable files for the current project
//
val buildDir = KFiles.buildDir(project)
val result = arrayListOf<IncludedFile>()
val classesDir = KFiles.makeDir(buildDir.path, "classes")
if (zip.includedFiles.isEmpty()) {
// If no includes were specified, assume the user wants a simple jar file made of the
// classes of the project, so we specify a From("build/classes/"), To("") and
// a list of files containing everything under it
val relClassesDir = Paths.get(project.directory).relativize(Paths.get(classesDir.path))
val prefixPath = Paths.get(project.directory).relativize(Paths.get(classesDir.path + "/"))
// Class files
val files = KFiles.findRecursively(classesDir).map { File(relClassesDir.toFile(), it) }
val filesNotExcluded : List<File> = files.filter {
! KFiles.Companion.isExcluded(KFiles.joinDir(project.directory, it.path), zip.excludes)
}
val fileSpecs = arrayListOf<IFileSpec>()
filesNotExcluded.forEach {
fileSpecs.add(IFileSpec.FileSpec(it.path.toString().substring(prefixPath.toString().length + 1)))
}
result.add(IncludedFile(From(prefixPath.toString()), To(""), fileSpecs))
// Resources, if applicable
context.variant.resourceDirectories(project).forEach {
result.add(IncludedFile(From(it.path), To(""), listOf(IFileSpec.GlobSpec("**"))))
}
} else {
//
// The user specified an include, just use it verbatim
//
val includedFiles = findIncludedFiles(project.directory, zip.includedFiles, zip.excludes, false)
result.addAll(includedFiles)
}
//
// If fatJar is true, add all the transitive dependencies as well: compile, runtime and dependent projects
//
if (zip.fatJar) {
val seen = hashSetOf<String>()
@Suppress("UNCHECKED_CAST")
val allDependencies = project.compileDependencies + project.compileRuntimeDependencies +
context.variant.buildType.compileDependencies +
context.variant.buildType.compileRuntimeDependencies +
context.variant.productFlavor.compileDependencies +
context.variant.productFlavor.compileRuntimeDependencies
val transitiveDependencies = dependencyManager.calculateDependencies(project, context,
scopes = listOf(Scope.COMPILE), passedDependencies = allDependencies)
transitiveDependencies.map {
it.jarFile.get()
}.forEach { file : File ->
if (! seen.contains(file.path)) {
seen.add(file.path)
if (! KFiles.Companion.isExcluded(file, zip.excludes)) {
result.add(IncludedFile(specs = arrayListOf(IFileSpec.FileSpec(file.absolutePath)),
expandJarFiles = true))
}
}
}
}
return result
}
override fun generateArchive(project: Project, context: KobaltContext, zip: Zip,
includedFiles: List<IncludedFile>) : File {
//
// Generate the manifest
// If manifest attributes were specified in the build file, use those to generateAndSave the manifest. Otherwise,
// try to find a META-INF/MANIFEST.MF and use that one if we find any. Otherwise, use the default manifest.
//
val manifest =
if (zip.attributes.size > 1) {
context.logger.log(project.name, 2, "Creating MANIFEST.MF from " + zip.attributes.size + " attributes")
Manifest().apply {
zip.attributes.forEach { attribute ->
mainAttributes.putValue(attribute.first, attribute.second)
}
}
} else {
fun findManifestFile(project: Project, includedFiles: List<IncludedFile>): File? {
val allFiles = includedFiles.flatMap { file ->
file.allFromFiles(project.directory).map { file.from(it.path) }
}
val manifestFiles = allFiles.filter { it.path.contains(MetaArchive.MANIFEST_MF) }
return if (manifestFiles.any()) manifestFiles[0] else null
}
val manifestFile = findManifestFile(project, includedFiles)
if (manifestFile != null) {
context.logger.log(project.name, 2, "Including MANIFEST.MF file $manifestFile")
Manifest(FileInputStream(manifestFile))
} else {
null
}
}
return Archives.generateArchive(project, context, zip.name, ".jar", includedFiles,
true /* expandJarFiles */, manifest)
}
}

View file

@ -1,212 +0,0 @@
package com.beust.kobalt
import com.beust.kobalt.api.*
import com.beust.kobalt.api.annotation.IncrementalTask
import com.beust.kobalt.api.annotation.Task
import com.beust.kobalt.internal.IncrementalManager
import com.beust.kobalt.internal.KobaltSettings
import com.beust.kobalt.internal.PluginInfo
import com.beust.kobalt.internal.TaskManager
import com.beust.kobalt.maven.DependencyManager
import com.beust.kobalt.misc.JarUtils
import com.beust.kobalt.misc.KFiles
import com.beust.kobalt.misc.KobaltExecutors
import com.beust.kobalt.misc.kobaltLog
import com.google.inject.Provider
import java.io.File
import java.lang.reflect.Method
import java.lang.reflect.Modifier
import java.net.URLClassLoader
import java.util.*
import java.util.jar.JarFile
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class Plugins @Inject constructor (val taskManagerProvider : Provider<TaskManager>,
val files: KFiles,
val depManager: DependencyManager,
val settings: KobaltSettings,
val executors: KobaltExecutors,
val incrementalManagerFactory: IncrementalManager.IFactory,
val taskManager: TaskManager) {
companion object {
private var pluginMap = hashMapOf<String, IPlugin>()
fun addPluginInstance(plugin: IPlugin) {
pluginMap.put(plugin.name, plugin)
}
val plugins : List<IPlugin>
get() = ArrayList(pluginMap.values)
/**
* The list of plugins found in the build file.
*/
val dynamicPlugins : ArrayList<IClasspathDependency> = arrayListOf()
fun addDynamicPlugin(plugin: IClasspathDependency) = dynamicPlugins.add(plugin)
fun findPlugin(name: String) : IPlugin? = pluginMap[name]
}
fun shutdownPlugins() = plugins.forEach { it.shutdown() }
fun applyPlugins(context: KobaltContext, projects: List<Project>) {
plugins.forEach { plugin ->
addPluginInstance(plugin)
// We could inject this in the plug-in but since these will be written by external users,
// I want to keep the verbosity of plugins to a minimum, so instead, we do the injection
// manually here
if (plugin is BasePlugin) {
plugin.taskManager = taskManagerProvider.get()
plugin.plugins = this
}
// Call apply() on each plug-in that accepts a project
kobaltLog(2, "Applying plug-in \"${plugin.name}\"")
projects.filter { plugin.accept(it) }.forEach { project ->
plugin.apply(project, context)
}
findStaticTasks(plugin, Task::class.java, { method -> isValidTaskMethod(method)}).forEach {
taskManager.addAnnotationTask(plugin, it.first, it.second)
}
findStaticTasks(plugin, IncrementalTask::class.java,
{ method -> isValidIncrementalTaskMethod(method)}).forEach {
taskManager.addIncrementalTask(plugin, it.first, it.second)
}
}
// Collect all the tasks from the task contributors
context.pluginInfo.taskContributors.forEach {
projects.forEach { project ->
taskManager.dynamicTasks.addAll(it.tasksFor(project, context))
}
}
// ... and from the incremental task contributors
val incrementalManager = incrementalManagerFactory.create()
context.pluginInfo.incrementalTaskContributors.forEach {
projects.forEach { project ->
it.incrementalTasksFor(project, context).forEach {
// Convert the closure (Project) -> IncrementalTaskInfo to (Project) -> TaskResult
// and create a DynamicTask out of it
val closure =
incrementalManager.toIncrementalTaskClosure(it.name, it.incrementalClosure, Variant())
val task = DynamicTask(it.plugin, it.name, it.doc, it.group, it.project, it.dependsOn,
it.reverseDependsOn, it.runBefore, it.runAfter, it.alwaysRunAfter,
closure)
taskManager.dynamicTasks.add(task)
}
}
}
// Now that we have collected all static and dynamic tasks, turn them all into plug-in tasks
taskManager.computePluginTasks(projects)
}
private fun <T: Annotation> findStaticTasks(plugin: IPlugin, klass: Class<T>, validate: (Method) -> Boolean)
: List<Pair<Method, T>> {
val result = arrayListOf<Pair<Method, T>>()
var currentClass : Class<in Any>? = plugin.javaClass
// Tasks can come from two different places: plugin classes and build files.
// When a task is read from a build file, ScriptCompiler adds it right away to plugin.methodTasks.
// The following loop introspects the current plugin, finds all the tasks using the @Task annotation
// and adds them to plugin.methodTasks
while (currentClass != null && ! (klass.equals(currentClass))) {
currentClass.declaredMethods.map {
Pair(it, it.getAnnotation(klass))
}.filter {
it.second != null
}.filter {
validate(it.first)
}.forEach {
if (Modifier.isPrivate(it.first.modifiers)) {
throw KobaltException("A task method cannot be private: ${it.first}")
}
result.add(it)
}
currentClass = currentClass.superclass
}
return result
}
/**
* Make sure this task method has the right signature.
*/
private fun isValidIncrementalTaskMethod(method: Method): Boolean {
val t = "Task ${method.declaringClass.simpleName}.${method.name}: "
if (method.returnType != IncrementalTaskInfo::class.java) {
throw IllegalArgumentException("${t}should return a IncrementalTaskInfo")
}
return true
}
/**
* Make sure this task method has the right signature.
*/
private fun isValidTaskMethod(method: Method): Boolean {
val t = "Task ${method.declaringClass.simpleName}.${method.name}: "
if (method.returnType != TaskResult::class.java) {
throw IllegalArgumentException("${t}should return a TaskResult")
}
if (method.parameterTypes.size != 1) {
throw IllegalArgumentException("${t}should take exactly one parameter of type a Project")
}
with(method.parameterTypes) {
if (! Project::class.java.isAssignableFrom(get(0))) {
throw IllegalArgumentException("${t}first parameter should be of type Project," +
"not ${get(0)}")
}
}
return true
}
val dependencies = arrayListOf<IClasspathDependency>()
// @Inject
// lateinit var pluginInfo: PluginInfo
fun installPlugins(dependencies: List<IClasspathDependency>, scriptClassLoader: ClassLoader) {
val executor = executors.newExecutor("Plugins", 5)
dependencies.forEach {
//
// Load all the jar files synchronously (can't compile the build script until
// they are installed locally).
depManager.create(it.id)
//
// Open the jar, parse its kobalt-plugin.xml and add the resulting PluginInfo to pluginInfo
//
val file = it.jarFile.get()
val pluginXml = if (file.isDirectory) {
// The plug-in can point to a directory (e.g. plugin("classes")), in which case we just
// read kobalt-plugin.xml directly
File(file, PluginInfo.PLUGIN_XML).readText()
} else {
// The plug-in is pointing to a jar file, read kobalt-plugin.xml from it
JarUtils.extractTextFile(JarFile(file), PluginInfo.PLUGIN_XML)
}
val pluginInfo = Kobalt.INJECTOR.getInstance(PluginInfo::class.java)
if (pluginXml != null) {
val pluginClassLoader = URLClassLoader(arrayOf(file.toURI().toURL()))
val thisPluginInfo = PluginInfo.readPluginXml(pluginXml, pluginClassLoader, scriptClassLoader)
pluginInfo.addPluginInfo(thisPluginInfo)
thisPluginInfo.plugins.forEach {
Plugins.addPluginInstance(it)
}
} else {
throw KobaltException("Plugin $it doesn't contain a ${PluginInfo.PLUGIN_XML} file")
}
}
executor.shutdown()
}
}

View file

@ -1,117 +0,0 @@
package com.beust.kobalt
import com.beust.kobalt.api.IClasspathDependency
import com.beust.kobalt.maven.LocalRepo
import com.beust.kobalt.maven.MavenId
import com.beust.kobalt.maven.aether.*
import com.beust.kobalt.misc.*
import com.google.inject.Inject
import org.eclipse.aether.artifact.DefaultArtifact
import org.eclipse.aether.graph.DependencyNode
import java.util.*
/**
* Display information about a Maven id.
*/
class ResolveDependency @Inject constructor(
val localRepo: LocalRepo,
val aether: KobaltMavenResolver,
val executors: KobaltExecutors) {
val increment = 8
val leftFirst = "\u2558"
val leftMiddle = "\u255f"
val leftLast = "\u2559"
val vertical = "\u2551"
class Dep(val dep: IClasspathDependency, val level: Int)
fun run(id: String) = displayDependenciesFor(id)
private fun latestMavenArtifact(group: String, artifactId: String, extension: String = "jar"): DependencyNode {
val artifact = DefaultArtifact(group, artifactId, extension, "(0,]")
val resolved = aether.resolveRange(artifact)
if (resolved != null) {
val newArtifact = DefaultArtifact(artifact.groupId, artifact.artifactId, artifact.extension,
resolved.highestVersion.toString())
val artifactResult = aether.resolve(KobaltMavenResolver.artifactToId(newArtifact), null)
return artifactResult.root
} else {
throw KobaltException("Couldn't find latest artifact for $group:$artifactId")
}
}
class PairResult(val dependency: IClasspathDependency, val repoUrl: String)
fun latestArtifact(group: String, artifactId: String, extension: String = "jar"): PairResult
= latestMavenArtifact(group, artifactId, extension).let {
PairResult(AetherDependency(it.artifact), "(TBD repo)")
}
private fun displayDependenciesFor(id: String) {
val mavenId = MavenId.create(id)
val resolved : PairResult =
if (mavenId.hasVersion) {
val node = aether.resolve(id, filter = Filters.EXCLUDE_OPTIONAL_FILTER)
PairResult(AetherDependency(node.root.artifact), node.artifactResults[0].repository.toString())
} else {
latestArtifact(mavenId.groupId, mavenId.artifactId)
}
displayDependencies(resolved.dependency, resolved.repoUrl)
}
private fun displayDependencies(dep: IClasspathDependency, url: String) {
val indent = -1
val root = Node(Dep(dep, indent))
val seen = hashSetOf(dep.id)
root.addChildren(findChildren(root, seen))
kobaltLog(1, AsciiArt.logBox(listOf(dep.id, url, dep.jarFile.get()).map { " $it" }))
display(root.children)
kobaltLog(1, "")
}
private fun display(nodes: List<Node<Dep>>) {
nodes.withIndex().forEach { indexNode ->
val node = indexNode.value
with(node.value) {
val left =
if (indexNode.index == nodes.size - 1) leftLast
else leftMiddle
val indent = level * increment
for(i in 0..indent - 2) {
if (!KobaltLogger.isQuiet) {
if (i == 0 || ((i + 1) % increment == 0)) print(vertical)
else print(" ")
}
}
kobaltLog(1, left + " " + dep.id + (if (dep.optional) " (optional)" else ""))
display(node.children)
}
}
}
private fun findChildren(root: Node<Dep>, seen: HashSet<String>): List<Node<Dep>> {
val result = arrayListOf<Node<Dep>>()
root.value.dep.directDependencies().forEach {
if (! seen.contains(it.id)) {
val dep = Dep(it, root.value.level + 1)
val node = Node(dep)
kobaltLog(2, "Found dependency ${dep.dep.id} level: ${dep.level}")
result.add(node)
seen.add(it.id)
try {
node.addChildren(findChildren(node, seen))
} catch(ex: Exception) {
if (! it.optional) warn("Couldn't resolve " + node)
// else don't warn about missing optional dependencies
}
}
}
kobaltLog(2, "Children for ${root.value.dep.id}: ${result.size}")
return result
}
}

View file

@ -1,22 +0,0 @@
package com.beust.kobalt
class SystemProperties {
companion object {
val javaBase : String
get() {
val jh = System.getenv("JAVA_HOME")
?: System.getProperty("java.home")
?: throw IllegalArgumentException("JAVA_HOME not defined")
val result =
if (jh.toLowerCase().endsWith("jre")) jh.substring(0, jh.length - 4)
else jh
return result
}
val javaVersion = System.getProperty("java.version")
val homeDir = System.getProperty("user.home")
val tmpDir = System.getProperty("java.io.tmpdir")
val currentDir = System.getProperty("user.dir")
val username = System.getProperty("user.name")
}
}

View file

@ -1,8 +0,0 @@
package com.beust.kobalt
class TestResult(val success: Boolean, val shortMessage: String? = null, val longMessage: String? = null)
open class TaskResult(val success: Boolean = true,
val testResult: TestResult? = null,
val errorMessage: String? = null
)

View file

@ -1,40 +0,0 @@
package com.beust.kobalt
import com.beust.kobalt.api.Project
import com.beust.kobalt.api.annotation.Directive
class TestConfig(val project: Project, val isDefault : Boolean = false) {
val testArgs = arrayListOf<String>()
val jvmArgs = arrayListOf<String>()
val testIncludes = arrayListOf("**/*Test.class")
val testExcludes = arrayListOf<String>()
@Directive
var name: String = ""
@Directive
fun args(vararg arg: String) {
testArgs.addAll(arg)
}
@Directive
fun jvmArgs(vararg arg: String) {
jvmArgs.addAll(arg)
}
@Directive
fun include(vararg arg: String) {
testIncludes.apply {
clear()
addAll(arg)
}
}
@Directive
fun exclude(vararg arg: String) {
testExcludes.apply {
clear()
addAll(arg)
}
}
}

View file

@ -1,18 +0,0 @@
package com.beust.kobalt
import com.beust.kobalt.api.Project
import com.beust.kobalt.api.annotation.Directive
@Directive
fun Project.test(init: TestConfig.() -> Unit): TestConfig = let { project ->
with(testConfigs) {
val tf = TestConfig(project).apply { init() }
if (! map { it.name }.contains(tf.name)) {
add(tf)
tf
} else {
throw KobaltException("Test configuration \"${tf.name}\" already exists, give it a different "
+ "name with test { name = ... }")
}
}
}

View file

@ -1,236 +0,0 @@
package com.beust.kobalt
import com.beust.kobalt.api.*
import com.beust.kobalt.internal.ActorUtils
import com.beust.kobalt.internal.ParallelLogger
import com.beust.kobalt.internal.SourceSet
import com.beust.kobalt.misc.KFiles
import java.io.File
import java.util.*
/**
* Capture the product flavor and the build type of a build.
*/
class Variant(val initialProductFlavor: ProductFlavorConfig? = null,
val initialBuildType: BuildTypeConfig? = null) {
val productFlavor: ProductFlavorConfig by lazy {
initialProductFlavor ?: Variant.DEFAULT_PRODUCT_FLAVOR
}
val buildType: BuildTypeConfig by lazy {
initialBuildType ?: Variant.DEFAULT_BUILD_TYPE
}
val isDefault : Boolean
get() = productFlavor == DEFAULT_PRODUCT_FLAVOR && buildType == DEFAULT_BUILD_TYPE
fun toTask(taskName: String) = taskName + productFlavor.name.capitalize() + buildType.name.capitalize()
/**
* for {internal, release}, return [internalRelease, internal, release]
*/
fun allDirectories(): List<String> {
return arrayListOf<String>().apply {
add(toCamelcaseDir())
add(productFlavor.name)
add(buildType.name)
}
}
fun sourceDirectories(project: Project, context: KobaltContext, sourceSet: SourceSet) : List<File> {
val result = arrayListOf<File>()
val compilerContributors = ActorUtils.selectAffinityActors(project, context,
context.pluginInfo.compilerContributors)
compilerContributors.forEach {
it.compilersFor(project, context).forEach { compiler ->
result.addAll(sourceDirectories(project, compiler.sourceDirectory, variantFirst = true,
sourceSet = sourceSet))
}
}
return result.filter { ! KFiles.isResource(it.path) }.toList()
}
/**
* Might be used by plug-ins.
*/
fun resourceDirectories(project: Project, sourceSet: SourceSet = SourceSet.MAIN)
= sourceDirectories(project, "resources", variantFirst = false, sourceSet = sourceSet)
.filter { KFiles.isResource(it.path) }
/**
* suffix is either "java" (to find source files) or "resources" (to find resources).
* The priority directory is always returned first. For example, if a "pro" product flavor
* is requested, "src/pro/kotlin" will appear in the result before "src/main/kotlin". Later,
* files that have already been seen get skipped, which is how compilation and resources
* receive the correct priority in the final jar.
*/
private fun sourceDirectories(project: Project, suffix: String, variantFirst: Boolean, sourceSet: SourceSet)
: List<File> {
val result = arrayListOf<File>()
val sourceDirectories = sourceSet.correctSourceSet(project)
.filter { File(project.directory, it).exists() }
.map(::File)
if (isDefault) {
result.addAll(sourceDirectories)
} else {
// // The ordering of files is: 1) build type 2) product flavor 3) default
val kobaltLog = Kobalt.INJECTOR.getInstance(ParallelLogger::class.java)
buildType.let {
val dir = File(KFiles.joinDir("src", it.name, suffix))
kobaltLog.log(project.name, 3, "Adding source for build type ${it.name}: ${dir.path}")
result.add(dir)
}
productFlavor.let {
val dir = File(KFiles.joinDir("src", it.name, suffix))
kobaltLog.log(project.name, 3, "Adding source for product flavor ${it.name}: ${dir.path}")
result.add(dir)
}
result.addAll(allDirectories()
.map { File(KFiles.joinDir("src", it, suffix)) }
.filter(File::exists))
// Now that all the variant source directories have been added, add the project's default ones
result.addAll(sourceDirectories)
}
// Generated directory, if applicable
generatedSourceDirectory?.let {
result.add(it)
}
val filteredResult = result.filter { File(project.directory, it.path).exists() }
val sortedResult = if (variantFirst) filteredResult
else filteredResult.reversed().toList()
val deduplicatedResult = LinkedHashSet(sortedResult).toList()
return deduplicatedResult
}
fun archiveName(project: Project, archiveName: String?, suffix: String) : String {
val result =
if (isDefault) {
archiveName ?: project.name + "-" + project.version + suffix
} else {
val base = archiveName?.substring(0, archiveName.length - suffix.length)
?: project.name + "-" + project.version
val flavor = if (productFlavor.name.isEmpty()) "" else "-" + productFlavor.name
val type = if (buildType.name.isEmpty()) "" else "-" + buildType.name
val result: String = base + flavor + type + suffix
result
}
return result
}
var generatedSourceDirectory: File? = null
private fun findBuildTypeBuildConfig(project: Project, variant: Variant?) : BuildConfig? {
val buildTypeName = variant?.buildType?.name
return project.buildTypes[buildTypeName]?.buildConfig
}
private fun findProductFlavorBuildConfig(project: Project, variant: Variant?) : BuildConfig? {
val buildTypeName = variant?.productFlavor?.name
return project.productFlavors[buildTypeName]?.buildConfig
}
/**
* Return a list of the BuildConfigs found on the productFlavor{}, buildType{} and project{} (in that order).
*/
private fun findBuildConfigs(project: Project, variant: Variant?) : List<BuildConfig> {
val result = listOf(
findBuildTypeBuildConfig(project, variant),
findProductFlavorBuildConfig(project, variant),
project.buildConfig)
.filterNotNull()
return result
}
/**
* Generate BuildConfig.java if requested. Also look up if any BuildConfig is defined on the current build type,
* product flavor or main project, and use them to generateAndSave any additional field (in that order to
* respect the priorities). Return the generated file if it was generated, null otherwise.
*/
fun maybeGenerateBuildConfig(project: Project, context: KobaltContext) : File? {
val buildConfigs = findBuildConfigs(project, this)
if (buildConfigs.size > 0) {
val pkg = project.packageName ?: project.group
?: throw KobaltException(
"packageName needs to be defined on the project in order to generateAndSave BuildConfig")
val contributor = ActorUtils.selectAffinityActor(project, context,
context.pluginInfo.buildConfigContributors)
if (contributor != null) {
val code = contributor.generateBuildConfig(project, context, pkg, this, buildConfigs)
val result = KFiles.makeDir(KFiles.generatedSourceDir(project, this, "buildConfig"))
// Make sure the generatedSourceDirectory doesn't contain the project.directory since
// that directory will be added when trying to find recursively all the sources in it
generatedSourceDirectory = result.relativeTo(File(project.directory))
val outputGeneratedSourceDirectory = File(result, pkg.replace('.', File.separatorChar))
val outputDir = File(outputGeneratedSourceDirectory, "BuildConfig." + contributor.buildConfigSuffix)
KFiles.saveFile(outputDir, code)
context.logger.log(project.name, 2, "Generated ${outputDir.path}")
return result
} else {
throw KobaltException("Couldn't find a contributor to generateAndSave BuildConfig")
}
} else {
return null
}
}
override fun toString() = toTask("")
companion object {
val DEFAULT_PRODUCT_FLAVOR = ProductFlavorConfig("")
val DEFAULT_BUILD_TYPE = BuildTypeConfig("")
fun allVariants(project: Project): List<Variant> {
val result = arrayListOf<Variant>()
if (project.buildTypes.size > 0) {
project.buildTypes.keys.forEach {
val bt = project.buildTypes[it]
if (project.productFlavors.size > 0) {
project.productFlavors.keys.forEach {
result.add(Variant(project.productFlavors[it], bt))
}
} else {
result.add(Variant(null, bt))
}
}
} else if (project.productFlavors.size > 0) {
project.productFlavors.keys.forEach {
val pf = project.productFlavors[it]
if (project.buildTypes.size > 0) {
project.buildTypes.keys.forEach {
result.add(Variant(pf, project.buildTypes[it]))
}
} else {
result.add(Variant(pf, null))
}
}
}
return result
}
}
fun toCamelcaseDir() : String {
fun lci(s : String) = if (s.isEmpty() || s.length == 1) s else s[0].toLowerCase() + s.substring(1)
return lci(productFlavor.name) + buildType.name.capitalize()
}
fun toIntermediateDir() : String {
if (isDefault) {
return ""
} else {
return KFiles.joinDir(productFlavor.name, buildType.name)
}
}
}

View file

@ -1,19 +0,0 @@
package com.beust.kobalt.api
import com.beust.kobalt.Plugins
import com.beust.kobalt.internal.TaskManager
abstract class BasePlugin : IPlugin {
lateinit var context: KobaltContext
override fun accept(project: Project) = true
override fun apply(project: Project, context: KobaltContext) {
this.context = context
}
override fun shutdown() {}
override lateinit var taskManager: TaskManager
lateinit var plugins: Plugins
}

View file

@ -1,11 +0,0 @@
package com.beust.kobalt.api
class BuildTypeConfig(val name: String) : IBuildConfig, IDependencyHolder by DependencyHolder() {
var minifyEnabled = false
var applicationIdSuffix: String? = null
var proguardFile: String? = null
override var buildConfig : BuildConfig? = BuildConfig()
}

View file

@ -1,19 +0,0 @@
package com.beust.kobalt.api
import java.util.*
/**
* Actors that have one config object per project can implement `IConfigActor` by delegating to
* `ConfigActor`. Then they can easily add and look up configurations per project.
*/
interface IConfigActor<T> {
val configurations : HashMap<String, T>
fun configurationFor(project: Project?) = if (project != null) configurations[project.name] else null
fun addConfiguration(project: Project, configuration: T) = configurations.put(project.name, configuration)
}
open class ConfigActor<T>: IConfigActor<T> {
override val configurations : HashMap<String, T> = hashMapOf()
}

View file

@ -1,19 +0,0 @@
package com.beust.kobalt.api
import com.google.common.collect.ArrayListMultimap
import com.google.common.collect.ListMultimap
/**
* Actors that have more than config object per project can use this helper class.
*/
interface IConfigsActor<T> {
val configurations : ListMultimap<String, T>
fun configurationFor(project: Project?) = if (project != null) configurations[project.name] else null
fun addConfiguration(project: Project, configuration: T) = configurations.put(project.name, configuration)
}
open class ConfigsActor<T>: IConfigsActor<T> {
override val configurations = ArrayListMultimap.create<String, T>()
}

View file

@ -1,47 +0,0 @@
package com.beust.kobalt.api
import com.beust.kobalt.api.annotation.Directive
import java.util.*
/**
* Various elements in a build file let you specify dependencies: projects, buildType and productFlavor.
* They all implement this interface and delegate to an instance of the `DependencyHolder` concrete class.
*/
interface IDependencyHolder {
var project: Project
val compileDependencies : ArrayList<IClasspathDependency>
val optionalDependencies : ArrayList<IClasspathDependency>
val compileProvidedDependencies : ArrayList<IClasspathDependency>
val compileOnlyDependencies : ArrayList<IClasspathDependency>
val compileRuntimeDependencies : ArrayList<IClasspathDependency>
val excludedDependencies : ArrayList<IClasspathDependency>
val nativeDependencies : ArrayList<IClasspathDependency>
@Directive
var dependencies: Dependencies?
@Directive
fun dependencies(init: Dependencies.() -> Unit) : Dependencies
}
open class DependencyHolder : IDependencyHolder {
override lateinit var project: Project
override val compileDependencies : ArrayList<IClasspathDependency> = arrayListOf()
override val optionalDependencies : ArrayList<IClasspathDependency> = arrayListOf()
override val compileProvidedDependencies : ArrayList<IClasspathDependency> = arrayListOf()
override val compileOnlyDependencies : ArrayList<IClasspathDependency> = arrayListOf()
override val compileRuntimeDependencies : ArrayList<IClasspathDependency> = arrayListOf()
override val excludedDependencies : ArrayList<IClasspathDependency> = arrayListOf()
override val nativeDependencies : ArrayList<IClasspathDependency> = arrayListOf()
override var dependencies : Dependencies? = null
override fun dependencies(init: Dependencies.() -> Unit) : Dependencies {
dependencies = Dependencies(project, compileDependencies, optionalDependencies, compileProvidedDependencies,
compileOnlyDependencies, compileRuntimeDependencies, excludedDependencies, nativeDependencies)
dependencies!!.init()
return dependencies!!
}
}

View file

@ -1,10 +0,0 @@
package com.beust.kobalt.api
import com.beust.kobalt.TaskResult
/**
* Plug-ins that will be invoked during the "assemble" task.
*/
interface IAssemblyContributor : IContributor {
fun assemble(project: Project, context: KobaltContext) : TaskResult
}

View file

@ -1,16 +0,0 @@
package com.beust.kobalt.api
import com.beust.kobalt.Variant
/**
* Plug-ins that can generate a BuildConfig file.
*/
interface IBuildConfigContributor : IProjectAffinity {
fun generateBuildConfig(project: Project, context: KobaltContext, packageName: String, variant: Variant,
buildConfigs: List<BuildConfig>) : String
/**
* The suffix of the generated BuildConfig, e.g. ".java".
*/
val buildConfigSuffix: String
}

View file

@ -1,10 +0,0 @@
package com.beust.kobalt.api
class BuildConfigField(val type: String, val name: String, val value: Any)
/**
* Plug-ins that want to add fields to BuildConfig need to implement this interface.
*/
interface IBuildConfigFieldContributor : IContributor {
fun fieldsFor(project: Project, context: KobaltContext) : List<BuildConfigField>
}

View file

@ -1,20 +0,0 @@
package com.beust.kobalt.api
/**
* Plug-ins that listen to build events.
*/
interface IBuildListener : IListener {
class TaskEndInfo(val success: Boolean, val shortMessage: String? = null,
val longMessage: String? = null)
fun taskStart(project: Project, context: KobaltContext, taskName: String) {}
fun taskEnd(project: Project, context: KobaltContext, taskName: String, info: TaskEndInfo) {}
fun projectStart(project: Project, context: KobaltContext) {}
fun projectEnd(project: Project, context: KobaltContext, status: ProjectBuildStatus) {}
}
enum class ProjectBuildStatus {
SUCCESS, FAILED, SKIPPED
}

View file

@ -1,8 +0,0 @@
package com.beust.kobalt.api
/**
* Plug-ins that produce build reports.
*/
interface IBuildReportContributor : IContributor {
fun generateReport(context: KobaltContext)
}

View file

@ -1,68 +0,0 @@
package com.beust.kobalt.api
import com.beust.kobalt.TaskResult
interface ICompilerDescription : Comparable<ICompilerDescription> {
/**
* The name of the language compiled by this compiler.
*/
val name: String
/**
* The suffixes handled by this compiler (without the dot, e.g. "java" or "kt").
*/
val sourceSuffixes: List<String>
/**
* The trailing end of the source directory (e.g. "kotlin" in "src/main/kotlin")
*/
val sourceDirectory: String
/**
* Run the compilation based on the info.
*/
fun compile(project: Project, context: KobaltContext, info: CompilerActionInfo) : TaskResult
companion object {
val DEFAULT_PRIORITY: Int = 10
}
/**
* The priority of this compiler. Lower priority compilers are run first.
*/
val priority: Int get() = DEFAULT_PRIORITY
override fun compareTo(other: ICompilerDescription) = priority.compareTo(other.priority)
/**
* Can this compiler be passed directories or does it need individual source files?
*/
val canCompileDirectories: Boolean get() = false
}
interface ICompilerContributor : IProjectAffinity, IContributor {
fun compilersFor(project: Project, context: KobaltContext): List<ICompilerDescription>
}
interface ICompiler {
fun compile(project: Project, context: KobaltContext, info: CompilerActionInfo): TaskResult
}
class CompilerDescription(override val name: String, override val sourceDirectory: String,
override val sourceSuffixes: List<String>, val compiler: ICompiler,
override val priority: Int = ICompilerDescription.DEFAULT_PRIORITY,
override val canCompileDirectories: Boolean = false) : ICompilerDescription {
override fun compile(project: Project, context: KobaltContext, info: CompilerActionInfo): TaskResult {
val result =
if (info.sourceFiles.isNotEmpty()) {
compiler.compile(project, context, info)
} else {
context.logger.log(project.name, 2, "$name couldn't find any source files to compile")
TaskResult()
}
return result
}
override fun toString() = name + " compiler"
}

View file

@ -1,29 +0,0 @@
package com.beust.kobalt.api
/**
* Plugins that add compiler flags.
*/
class FlagContributor(val flagPriority: Int = DEFAULT_FLAG_PRIORITY,
val closure: (project: Project, context: KobaltContext, currentFlags: List<String>,
suffixesBeingCompiled: List<String>) -> List<String>) : IContributor {
companion object {
val DEFAULT_FLAG_PRIORITY = 20
}
fun flagsFor(project: Project, context: KobaltContext, currentFlags: List<String>,
suffixesBeingCompiled: List<String>) = closure(project, context, currentFlags, suffixesBeingCompiled)
}
interface IFlagBase {
val flagPriority: Int get() = FlagContributor.DEFAULT_FLAG_PRIORITY
}
interface ICompilerFlagContributor : IContributor, IFlagBase {
fun compilerFlagsFor(project: Project, context: KobaltContext, currentFlags: List<String>,
suffixesBeingCompiled: List<String>): List<String>
}
interface IDocFlagContributor : IContributor, IFlagBase {
fun docFlagsFor(project: Project, context: KobaltContext, currentFlags: List<String>,
suffixesBeingCompiled: List<String>): List<String>
}

View file

@ -1,88 +0,0 @@
package com.beust.kobalt.api
import com.beust.kobalt.maven.aether.Filters.EXCLUDE_OPTIONAL_FILTER
import com.beust.kobalt.maven.aether.KobaltMavenResolver
import com.beust.kobalt.maven.aether.Scope
import com.beust.kobalt.misc.kobaltLog
import org.eclipse.aether.graph.DependencyFilter
import org.eclipse.aether.graph.DependencyNode
/**
* Manage the creation of dependencies and also provide dependencies for projects.
*/
interface IDependencyManager {
/**
* Parse the id and return the correct IClasspathDependency
*/
fun create(id: String, optional: Boolean = false, projectDirectory: String? = null): IClasspathDependency
/**
* Create an IClasspathDependency from a Maven id.
*/
fun createMaven(id: String, optional: Boolean = false): IClasspathDependency
/**
* Create an IClasspathDependency from a path.
*/
fun createFile(path: String): IClasspathDependency
/**
* @return the source dependencies for this project, including the contributors.
*/
fun dependencies(project: Project, context: KobaltContext, scopes: List<Scope>): List<IClasspathDependency>
/**
* @return the test dependencies for this project, including the contributors.
*/
fun testDependencies(project: Project, context: KobaltContext): List<IClasspathDependency>
/**
* @return the classpath for this project, including the IClasspathContributors.
* allDependencies is typically either compileDependencies or testDependencies
*/
fun calculateDependencies(project: Project?, context: KobaltContext,
dependencyFilter: DependencyFilter =
createDependencyFilter(project, project?.compileDependencies ?: emptyList()),
scopes: List<Scope> = listOf(Scope.COMPILE),
vararg passedDependencies: List<IClasspathDependency>): List<IClasspathDependency>
/**
* Create an Aether dependency filter that uses the dependency configuration included in each
* IClasspathDependency.
*/
fun createDependencyFilter(project: Project?, dependencies: List<IClasspathDependency>) : DependencyFilter {
return DependencyFilter { p0, p1 ->
fun isNodeExcluded(node: DependencyNode, passedDep: IClasspathDependency) : Boolean {
val dep = create(KobaltMavenResolver.artifactToId(node.artifact))
return passedDep.excluded.any { ex -> ex.isExcluded(dep)}
}
fun isDepExcluded(node: DependencyNode, excluded: List<IClasspathDependency>?) : Boolean {
val dep = create(KobaltMavenResolver.artifactToId(node.artifact))
return excluded?.map { it.id }?.contains(dep.id) ?: false
}
val accept = dependencies.isEmpty() || dependencies.any {
// Is this dependency excluded?
val isExcluded = isNodeExcluded(p0, it) || isDepExcluded(p0, project?.excludedDependencies)
// Is the parent dependency excluded?
val isParentExcluded =
if (p1.any()) {
isNodeExcluded(p1[0], it) || isDepExcluded(p1[0], project?.excludedDependencies)
} else {
false
}
// Only accept if no exclusions were found
! isExcluded && ! isParentExcluded
}
if (! accept) {
kobaltLog(2, "Excluding $p0")
}
if (accept) EXCLUDE_OPTIONAL_FILTER.accept(p0, p1)
else accept
}
}
}

View file

@ -1,12 +0,0 @@
package com.beust.kobalt.api
import com.beust.kobalt.IncrementalTaskInfo
/**
* Plug-ins that will be invoked during the "assemble" task and wish to return an incremental task instead
* of a regular one.
*/
interface IIncrementalAssemblyContributor : IContributor {
fun assemble(project: Project, context: KobaltContext) : IncrementalTaskInfo
}

View file

@ -1,28 +0,0 @@
package com.beust.kobalt.api
import com.beust.kobalt.IncrementalTaskInfo
/**
* Plug-ins that need to add incremental dynamic tasks (tasks that are not methods annotated with @Task) need
* to implement this interface.
*/
interface IIncrementalTaskContributor : IContributor {
fun incrementalTasksFor(project: Project, context: KobaltContext) : List<IncrementalDynamicTask>
}
class IncrementalDynamicTask(val context: KobaltContext,
val plugin: IPlugin,
val name: String,
val doc: String,
val group: String,
val project: Project,
val dependsOn: List<String> = listOf<String>(),
val reverseDependsOn: List<String> = listOf<String>(),
val runBefore: List<String> = listOf<String>(),
val runAfter: List<String> = listOf<String>(),
val alwaysRunAfter: List<String> = listOf<String>(),
val incrementalClosure: (Project) -> IncrementalTaskInfo) {
override fun toString() = "[IncrementalDynamicTask $name dependsOn=$dependsOn reverseDependsOn=$reverseDependsOn]"
}

View file

@ -1,13 +0,0 @@
package com.beust.kobalt.api
/**
* Plug-ins that add flags to the JVM used to run apps should implement this interface.
*/
interface IJvmFlagContributor : IContributor {
/**
* The list of JVM flags that will be added to the JVM when the app gets run. @param[currentFlags] is only here
* for convenience, in case you need to look at the current JVM flags before adding your own flags.
*/
fun jvmFlagsFor(project: Project, context: KobaltContext, currentFlags: List<String>) : List<String>
}

View file

@ -1,8 +0,0 @@
package com.beust.kobalt.api
/**
* Plug-ins that want to override the local maven repo path.
*/
interface ILocalMavenRepoPathInterceptor : IInterceptor {
fun repoPath(currentPath: String) : String
}

View file

@ -1,10 +0,0 @@
package com.beust.kobalt.api
import com.beust.kobalt.maven.MavenId
/**
* Plug-ins can rewrite Maven id's before Kobalt sees them with this interface.
*/
interface IMavenIdInterceptor: IInterceptor {
fun intercept(mavenId: MavenId) : MavenId
}

View file

@ -1,30 +0,0 @@
package com.beust.kobalt.api
import com.beust.kobalt.internal.TaskManager
interface IPlugin : IPluginActor {
/**
* The name of this plug-in.
*/
val name: String
/**
* @return true if this plug-in decided it should be enabled for this project.
*/
fun accept(project: Project) : Boolean
/**
* Invoked on all plug-ins before the Kobalt execution stops.
*/
fun shutdown()
/**
* Main entry point for a plug-in to initialize itself based on a project and a context.
*/
fun apply(project: Project, context: KobaltContext) {}
/**
* Injected by Kobalt to manage tasks.
*/
var taskManager : TaskManager
}

View file

@ -1,15 +0,0 @@
package com.beust.kobalt.api
interface IPluginActor {
/**
* Clean up any state that your actor might have saved so it can be run again.
*/
fun cleanUpActors() {}
}
interface IContributor : IPluginActor
interface IInterceptor : IPluginActor
interface IListener : IPluginActor

View file

@ -1,20 +0,0 @@
package com.beust.kobalt.api
import java.io.File
/**
* Plug-ins that add source directories to be compiled need to implement this interface.
*/
interface ISourceDirectoryContributor : IContributor {
fun sourceDirectoriesFor(project: Project, context: KobaltContext): List<File>
}
/**
* @return the source directories for this project including source contributors.
*/
fun KobaltContext.sourceDirectories(project: Project) : Set<File> {
val result = pluginInfo.sourceDirContributors.flatMap {
it.sourceDirectoriesFor(project, this)
}
return result.toSet()
}

View file

@ -1,32 +0,0 @@
package com.beust.kobalt.api
import com.beust.kobalt.TaskResult
import com.beust.kobalt.internal.TaskResult2
/**
* Plug-ins that need to add dynamic tasks (tasks that are not methods annotated with @Task) need
* to implement this interface.
*/
interface ITaskContributor : IContributor {
fun tasksFor(project: Project, context: KobaltContext) : List<DynamicTask>
}
class DynamicTask(override val plugin: IPlugin, override val name: String, override val doc: String,
override val group: String,
override val project: Project,
val dependsOn: List<String> = listOf<String>(),
val reverseDependsOn: List<String> = listOf<String>(),
val runBefore: List<String> = listOf<String>(),
val runAfter: List<String> = listOf<String>(),
val alwaysRunAfter: List<String> = listOf<String>(),
val closure: (Project) -> TaskResult) : ITask {
override fun call(): TaskResult2<ITask> {
val taskResult = closure.invoke(project)
return TaskResult2(taskResult.success, errorMessage = taskResult.errorMessage, value = this)
}
override fun toString() =
"[DynamicTask ${project.name}:$name dependsOn=$dependsOn reverseDependsOn=$reverseDependsOn]"
}

View file

@ -1,45 +0,0 @@
package com.beust.kobalt.api
import com.beust.kobalt.Args
/**
* Plugins that want to participate in the --init process (they can generate files to initialize
* a new project).
*/
interface ITemplateContributor : IContributor {
companion object {
val DIRECTORY_NAME = "templates"
}
val templates: List<ITemplate>
}
interface ITemplate {
/**
* The name of this template. This is the name that will be looked up when passed to the --init
* argument.
*/
val templateName: String
/**
* Description of this template.
*/
val templateDescription: String
/**
* The plug-in this template belongs to.
*/
val pluginName: String
/**
* Instructions to display to the user after a template has been generated.
*/
val instructions : String get() = "Build this project with `./kobaltw assemble`"
/**
* Generate the files for this template. The parameter is the arguments that were passed to the kobaltw
* command.
*/
fun generateTemplate(args: Args, classLoader: ClassLoader)
}

View file

@ -1,12 +0,0 @@
package com.beust.kobalt.api
/**
* Plug-ins that add flags to the JVM used to run tests should implement this interface.
*/
interface ITestJvmFlagContributor : IContributor {
/**
* The list of JVM flags that will be added to the JVM when the tests get run. @param[currentFlags] is only here
* for convenience, in case you need to look at the current JVM flags before adding your own flags.
*/
fun testJvmFlagsFor(project: Project, context: KobaltContext, currentFlags: List<String>) : List<String>
}

View file

@ -1,14 +0,0 @@
package com.beust.kobalt.api
/**
* Plug-ins that add flags to the JVM used to run tests should implement this interface.
*/
interface ITestJvmFlagInterceptor : IInterceptor {
/**
* @return the list of all flags that should be used. If you only want to add flags to the current list,
* just return the concatenation of @param[currentFlags] and your own list (or use ITestJvmFlagContributor).
* If you actually alter the list of flags, make sure you don't remove anything critical from @param[currentFlags].
*/
fun testJvmFlagsFor(project: Project, context: KobaltContext, currentFlags: List<String>) : List<String>
}

View file

@ -1,18 +0,0 @@
package com.beust.kobalt.api
import java.io.File
/**
* Plug-ins that add test source directories to be compiled need to implement this interface.
*/
interface ITestSourceDirectoryContributor : IContributor {
fun testSourceDirectoriesFor(project: Project, context: KobaltContext): List<File>
}
fun KobaltContext.testSourceDirectories(project: Project) : List<File> {
val result = pluginInfo.testSourceDirContributors.flatMap {
it.testSourceDirectoriesFor(project, this)
}
return result
}

View file

@ -1,55 +0,0 @@
package com.beust.kobalt.api
import com.beust.kobalt.Args
import com.beust.kobalt.misc.KFiles
import com.beust.kobalt.misc.kobaltLog
import java.io.*
import java.net.URL
import java.util.jar.JarInputStream
/**
* Base class for templates that decompress a jar file.
*/
interface InputStreamJarTemplate : ITemplate {
val inputStream: InputStream
override fun generateTemplate(args: Args, classLoader: ClassLoader) {
val destDir = File(".")
JarInputStream(inputStream).use { ins ->
var entry = ins.nextEntry
while (entry != null) {
val f = File(destDir.path + File.separator + entry.name)
if (entry.isDirectory) {
f.mkdir()
entry = ins.nextEntry
continue
}
kobaltLog(2, " Extracting: $entry to ${f.absolutePath}")
FileOutputStream(f).use { fos ->
KFiles.copy(ins, fos)
}
entry = ins.nextEntry
}
}
}
}
abstract class ResourceJarTemplate(jarName: String, val classLoader: ClassLoader) : InputStreamJarTemplate {
override val inputStream : InputStream = classLoader.getResource(jarName).openConnection().inputStream
}
abstract class FileJarTemplate(val fileName: String) : InputStreamJarTemplate {
override val inputStream = FileInputStream(File(fileName))
}
abstract class HttpJarTemplate(val url: String) : InputStreamJarTemplate {
override val inputStream : InputStream
get() {
try {
return URL(url).openConnection().inputStream
} catch(ex: IOException) {
throw IllegalArgumentException("Couldn't connect to $url")
}
}
}

View file

@ -1,19 +0,0 @@
package com.beust.kobalt.api
import com.beust.kobalt.maven.DependencyManager
import java.io.File
import java.util.concurrent.Future
class JarFinder {
companion object {
/**
* @return a Future for the jar file corresponding to this id.
*/
fun byIdFuture(id: String) : Future<File> = DependencyManager.create(id).jarFile
/**
* @return the jar file corresponding to this id. This might cause a network call.
*/
fun byId(id: String) = byIdFuture(id).get()
}
}

View file

@ -1,141 +0,0 @@
package com.beust.kobalt.api
import com.beust.kobalt.Constants
import com.beust.kobalt.HostConfig
import com.beust.kobalt.Plugins
import com.beust.kobalt.internal.PluginInfo
import com.beust.kobalt.maven.DependencyManager
import com.beust.kobalt.maven.aether.KobaltMavenResolver
import com.google.inject.Guice
import com.google.inject.Injector
import com.google.inject.Module
import java.io.InputStream
import java.time.Duration
import java.util.*
class Kobalt {
companion object {
lateinit var INJECTOR : Injector
fun init(module: Module) {
Kobalt.INJECTOR = Guice.createInjector(module)
//
// Add all the plugins read in kobalt-plugin.xml to the Plugins singleton, so that code
// in the build file that calls Plugins.findPlugin() can find them (code in the
// build file do not have access to the KobaltContext).
//
val pluginInfo = Kobalt.INJECTOR.getInstance(PluginInfo::class.java)
pluginInfo.plugins.forEach { Plugins.addPluginInstance(it) }
}
var context: KobaltContext? = null
/**
* @return the repos calculated from various places where repos can be specified.
*/
val repos : Set<HostConfig>
get() {
val settingsRepos = Kobalt.context?.settings?.defaultRepos?.map { HostConfig(it) } ?: emptyList()
// Repos from <default-repos> in the settings
val result = ArrayList(
(if (settingsRepos.isEmpty()) Constants.DEFAULT_REPOS
else settingsRepos)
)
// Repo from <kobalt-compiler-repo> in the settings
Kobalt.context?.settings?.kobaltCompilerRepo?.let {
result.add(HostConfig(it))
}
// Repos from the repo contributors
Kobalt.context?.pluginInfo?.repoContributors?.forEach {
result.addAll(it.reposFor(null))
}
// Repos from the build file
result.addAll(reposFromBuildFiles)
result.forEach {
KobaltMavenResolver.initAuthentication(it)
}
return result.toHashSet()
}
val reposFromBuildFiles = hashSetOf<HostConfig>()
fun addRepo(repo: HostConfig) = reposFromBuildFiles.add(
if (repo.url.endsWith("/")) repo
else repo.copy(url = (repo.url + "/")))
val buildFileClasspath = arrayListOf<IClasspathDependency>()
fun addBuildFileClasspath(dep: String) {
val dependencyManager = Kobalt.INJECTOR.getInstance(DependencyManager::class.java)
buildFileClasspath.add(dependencyManager.create(dep))
}
private val KOBALT_PROPERTIES = "kobalt.properties"
private val PROPERTY_KOBALT_VERSION = "kobalt.version"
private val PROPERTY_KOBALT_VERSION_CHECK_TIMEOUT = "kobalt.version.checkTimeout" // ISO-8601
/** kobalt.properties */
private val kobaltProperties: Properties by lazy { readProperties() }
/**
* Read the content of kobalt.properties
*/
private fun readProperties() : Properties {
val result = Properties()
// kobalt.properties is internal to Kobalt
val url = Kobalt::class.java.classLoader.getResource(KOBALT_PROPERTIES)
if (url != null) {
readProperties(result, url.openConnection().inputStream)
} else {
throw AssertionError("Couldn't find $KOBALT_PROPERTIES")
}
// local.properties can be used by external users
// Paths.get(LOCAL_PROPERTIES).let { path ->
// if (Files.exists(path)) {
// Files.newInputStream(path).use {
// readProperties(result, it)
// }
// }
// }
return result
}
private fun readProperties(properties: Properties, ins: InputStream) {
properties.load(ins)
ins.close()
properties.forEach { es -> System.setProperty(es.key.toString(), es.value.toString()) }
}
val version: String
get() = kobaltProperties.getProperty(PROPERTY_KOBALT_VERSION)
// Note: Duration is Java 8 only, might need an alternative if we want to support Java < 8
val versionCheckTimeout: Duration
get() = Duration.parse( kobaltProperties.getProperty(PROPERTY_KOBALT_VERSION_CHECK_TIMEOUT) ?: "P1D")
fun findPlugin(name: String) = Plugins.findPlugin(name)
val optionsFromBuild = arrayListOf<String>()
fun addKobaltOptions(options: Array<out String>) {
optionsFromBuild.addAll(options)
}
val buildSourceDirs = arrayListOf<String>()
fun addBuildSourceDirs(dirs: Array<out String>) {
buildSourceDirs.addAll(dirs)
}
fun cleanUp() {
buildSourceDirs.clear()
buildFileClasspath.clear()
}
}
}

View file

@ -1,116 +0,0 @@
package com.beust.kobalt.api
import com.beust.kobalt.Args
import com.beust.kobalt.KobaltException
import com.beust.kobalt.Plugins
import com.beust.kobalt.Variant
import com.beust.kobalt.internal.ILogger
import com.beust.kobalt.internal.IncrementalManager
import com.beust.kobalt.internal.KobaltSettings
import com.beust.kobalt.internal.PluginInfo
import com.beust.kobalt.maven.DependencyManager
import com.beust.kobalt.maven.MavenId
import com.beust.kobalt.maven.PomGenerator
import com.beust.kobalt.maven.SimpleDep
import com.beust.kobalt.maven.aether.KobaltMavenResolver
import com.beust.kobalt.misc.KobaltExecutors
import java.io.File
class KobaltContext(val args: Args) {
lateinit var variant: Variant
val profiles = arrayListOf<String>()
init {
args.profiles?.split(',')?.filterNotNull()?.forEach {
profiles.add(it)
}
}
fun findPlugin(name: String) = Plugins.findPlugin(name)
/**
* Files that can be resolved in the local cache.
*/
enum class FileType { JAR, POM, SOURCES, JAVADOC, OTHER }
/**
* @param{id} is the Maven coordinate (e.g. "org.testng:testng:6.9.11"). If you are looking for a file
* that is not described by the enum (e.g. "aar"), use OTHER and make sure your @param{id} contains
* the fully qualified id (e.g. "com.example:example::aar:1.0").
*/
fun fileFor(id: String, fileType: FileType) : File {
val dep = SimpleDep(MavenId.create(id))
fun toQualifier(dep: SimpleDep, ext: String, qualifier: String?) =
dep.groupId + ":" + dep.artifactId +
":$ext" +
(if (qualifier != null) ":$qualifier" else "") +
":" + dep.version
val fullId =
when (fileType) {
FileType.JAR -> toQualifier(dep, "jar", null)
FileType.POM -> toQualifier(dep, "pom", null)
FileType.SOURCES -> toQualifier(dep, "", "sources")
FileType.JAVADOC -> toQualifier(dep, "", "javadoc")
FileType.OTHER -> id
}
val resolved = resolver.resolveToArtifact(fullId)
if (resolved != null) {
return resolved.file
} else {
throw KobaltException("Couldn't resolve $id")
}
}
/**
* @return the content of the pom.xml for the given project.
*/
fun generatePom(project: Project) = pomGeneratorFactory.create(project).generate()
/** All the projects that are being built during this run */
val allProjects = arrayListOf<Project>()
/** For internal use only */
val internalContext = InternalContext()
//
// Injected
//
lateinit var pluginInfo: PluginInfo
lateinit var pluginProperties: PluginProperties
lateinit var dependencyManager: DependencyManager
lateinit var executors: KobaltExecutors
lateinit var settings: KobaltSettings
lateinit var incrementalManager: IncrementalManager
lateinit var resolver: KobaltMavenResolver
lateinit var pomGeneratorFactory: PomGenerator.IFactory
lateinit var logger: ILogger
}
class InternalContext {
/**
* When an incremental task decides it's up to date, it sets this boolean to true so that subsequent
* tasks in that project can be skipped as well. This is an internal field that should only be set by Kobalt.
*/
private val incrementalSuccesses = hashSetOf<String>()
fun previousTaskWasIncrementalSuccess(projectName: String) = incrementalSuccesses.contains(projectName) ?: false
fun setIncrementalSuccess(projectName: String) = incrementalSuccesses.add(projectName)
/**
* Keep track of whether the build file was modified. If this boolean is true, incremental compilation
* will be disabled.
*/
var buildFileOutOfDate: Boolean = false
/**
* The absolute directory of the current project.
*/
var absoluteDir: File? = null
/**
* If true, will force a recompile of the files even if using the incremental compile
*/
var forceRecompile: Boolean = false
var noIncrementalKotlin: Boolean = false
}

View file

@ -1,20 +0,0 @@
package com.beust.kobalt.api
import com.beust.kobalt.internal.TaskResult2
import java.util.concurrent.Callable
interface ITask : Callable<TaskResult2<ITask>> {
val plugin: IPlugin
val project: Project
val name: String
val doc: String
val group: String
}
abstract class PluginTask : ITask {
override val name: String = ""
override open val doc: String = ""
override open val group: String = "other"
override fun toString() = project.name + ":" + name
}

View file

@ -1,9 +0,0 @@
package com.beust.kobalt.api
class ProductFlavorConfig(val name: String) : IBuildConfig,
IDependencyHolder by DependencyHolder() {
var applicationId: String? = null
override var buildConfig : BuildConfig? = BuildConfig()
}

View file

@ -1,314 +0,0 @@
package com.beust.kobalt.api
import com.beust.kobalt.TestConfig
import com.beust.kobalt.api.annotation.Directive
import com.beust.kobalt.maven.DependencyManager
import com.beust.kobalt.maven.aether.AetherDependency
import com.beust.kobalt.maven.aether.KobaltMavenResolver
import com.beust.kobalt.misc.KFiles
import com.beust.kobalt.misc.kobaltLog
import org.apache.maven.model.Model
import java.io.File
import java.util.*
import java.util.concurrent.Future
import java.util.concurrent.FutureTask
import java.util.regex.Pattern
open class Project(
@Directive open var name: String = "",
@Directive open var version: String? = null,
@Directive open var directory: String = ".",
@Directive open var buildDirectory: String = KFiles.KOBALT_BUILD_DIR,
@Directive open var group: String? = null,
@Directive open var artifactId: String? = null,
@Directive open var packaging: String? = null,
@Directive open var description : String = "",
@Directive open var url: String? = null,
@Directive open var pom: Model? = null,
@Directive open var dependsOn: ArrayList<Project> = arrayListOf<Project>(),
@Directive open var testsDependOn: ArrayList<Project> = arrayListOf<Project>(),
@Directive open var packageName: String? = group)
: IBuildConfig, IDependencyHolder by DependencyHolder() {
init {
this.project = this
}
fun allProjectDependedOn() = project.dependsOn + project.testsDependOn
class ProjectExtra(project: Project) {
var isDirty = false
/**
* @return true if any of the projects we depend on is dirty.
*/
fun dependsOnDirtyProjects(project: Project) = project.allProjectDependedOn().any { it.projectExtra.isDirty }
}
/**
* This field caches a bunch of things we don't want to recalculate all the time, such as the list of suffixes
* found in this project.
*/
val projectExtra = ProjectExtra(this)
val testConfigs = arrayListOf<TestConfig>()
// If one is specified by default, we only generateAndSave a BuildConfig, find a way to fix that
override var buildConfig : BuildConfig? = null // BuildConfig()
val projectProperties = ProjectProperties()
override fun equals(other: Any?) = name == (other as Project).name
override fun hashCode() = name.hashCode()
companion object {
val DEFAULT_SOURCE_DIRECTORIES = setOf("src/main/java", "src/main/kotlin", "src/main/resources")
val DEFAULT_SOURCE_DIRECTORIES_TEST = setOf("src/test/java", "src/test/kotlin", "src/test/resources")
}
//
// Directories
//
@Directive
fun sourceDirectories(init: Sources.() -> Unit) : Sources {
return Sources(this, sourceDirectories).apply { init() }
}
var sourceDirectories = hashSetOf<String>().apply { addAll(DEFAULT_SOURCE_DIRECTORIES)}
@Directive
fun sourceDirectoriesTest(init: Sources.() -> Unit) : Sources {
return Sources(this, sourceDirectoriesTest).apply { init() }
}
var sourceDirectoriesTest = hashSetOf<String>().apply { addAll(DEFAULT_SOURCE_DIRECTORIES_TEST)}
//
// Dependencies
//
@Directive
fun dependenciesTest(init: Dependencies.() -> Unit) : Dependencies {
dependencies = Dependencies(this, testDependencies, arrayListOf(),
testProvidedDependencies, compileOnlyDependencies, compileRuntimeDependencies,
excludedDependencies, nativeDependencies)
dependencies!!.init()
return dependencies!!
}
val testDependencies : ArrayList<IClasspathDependency> = arrayListOf()
val testProvidedDependencies : ArrayList<IClasspathDependency> = arrayListOf()
fun testsDependOn(vararg projects: Project) = testsDependOn.addAll(projects)
fun dependsOn(vararg projects: Project) = dependsOn.addAll(projects)
/** Used to disambiguate various name properties */
@Directive
val projectName: String get() = name
val productFlavors = hashMapOf<String, ProductFlavorConfig>()
fun addProductFlavor(name: String, pf: ProductFlavorConfig) {
productFlavors.put(name, pf)
}
var defaultConfig : BuildConfig? = null
val buildTypes = hashMapOf<String, BuildTypeConfig>()
fun addBuildType(name: String, bt: BuildTypeConfig) {
buildTypes.put(name, bt)
}
fun classesDir(context: KobaltContext): String {
val initial = KFiles.joinDir(buildDirectory, "classes")
val result = context.pluginInfo.buildDirectoryInterceptors.fold(initial, { dir, intercept ->
intercept.intercept(this, context, dir)
})
return result
}
class Dep(val file: File, val id: String)
/**
* @return a list of the transitive dependencies (absolute paths to jar files) for the given dependencies.
* Can be used for example as `collect(compileDependencies)`.
*/
@Directive
fun collect(dependencies: List<IClasspathDependency>) : List<Dep> {
return (Kobalt.context?.dependencyManager?.transitiveClosure(dependencies) ?: emptyList())
.map { Dep(it.jarFile.get(), it.id) }
}
override fun toString() = "[Project $name]"
}
class Sources(val project: Project, val sources: HashSet<String>) {
@Directive
fun path(vararg paths: String) {
sources.addAll(paths)
}
}
class Dependencies(val project: Project,
val dependencies: ArrayList<IClasspathDependency>,
val optionalDependencies: ArrayList<IClasspathDependency>,
val providedDependencies: ArrayList<IClasspathDependency>,
val compileOnlyDependencies: ArrayList<IClasspathDependency>,
val runtimeDependencies: ArrayList<IClasspathDependency>,
val excludedDependencies: ArrayList<IClasspathDependency>,
val nativeDependencies: ArrayList<IClasspathDependency>) {
/**
* Add the dependencies to the given ArrayList and return a list of future jar files corresponding to
* these dependencies. Futures are necessary here since this code is invoked from the build file and
* we might not have set up the extra IRepositoryContributors just yet. By the time these
* future tasks receive a get(), the repos will be correct.
*/
private fun addToDependencies(project: Project, dependencies: ArrayList<IClasspathDependency>,
dep: Array<out String>, optional: Boolean = false, excludeConfig: ExcludeConfig? = null): List<Future<File>>
= with(dep.map {
val resolved =
if (KobaltMavenResolver.isRangeVersion(it)) {
// Range id
val node = Kobalt.INJECTOR.getInstance(KobaltMavenResolver::class.java).resolveToArtifact(it)
val result = KobaltMavenResolver.artifactToId(node)
kobaltLog(2, "Resolved range id $it to $result")
result
} else {
it
}
DependencyManager.create(resolved, optional, project.directory)
}) {
dependencies.addAll(this)
if (excludeConfig != null) {
this.forEach { it.excluded.add(excludeConfig) }
}
this.map { FutureTask { it.jarFile.get() } }
}
@Directive
fun compile(vararg dep: String) = addToDependencies(project, dependencies, dep)
class ExcludeConfig {
val ids = arrayListOf<String>()
@Directive
fun exclude(vararg passedIds: String) = ids.addAll(passedIds)
class ArtifactConfig(
var groupId: String? = null,
var artifactId: String? = null,
var version: String? = null
)
val artifacts = arrayListOf<ArtifactConfig>()
@Directive
fun exclude(groupId: String? = null, artifactId: String? = null, version: String? = null)
= artifacts.add(ArtifactConfig(groupId, artifactId, version))
fun match(pattern: String?, id: String) : Boolean {
return pattern == null || Pattern.compile(pattern).matcher(id).matches()
}
/**
* @return true if the dependency is excluded with any of the exclude() directives. The matches
* are performed by a regular expression match against the dependency.
*/
fun isExcluded(dep: IClasspathDependency) : Boolean {
// Straight id match
var result = ids.any { match(it, dep.id) }
// Match on any combination of (groupId, artifactId, version)
if (! result && dep.isMaven) {
val mavenDep = dep as AetherDependency
val artifact = mavenDep.artifact
result = artifacts.any {
val match1 = it.groupId == null || match(it.groupId, artifact.groupId)
val match2 = it.artifactId == null || match(it.artifactId, artifact.artifactId)
val match3 = it.version == null || match(it.version, artifact.version)
match1 && match2 && match3
}
}
return result
}
}
@Directive
fun compile(dep: String, init: ExcludeConfig.() -> Unit) {
val excludeConfig = ExcludeConfig().apply {
init()
}
addToDependencies(project, dependencies, arrayOf(dep), excludeConfig = excludeConfig)
}
@Directive
fun compileOnly(vararg dep: String) = addToDependencies(project, compileOnlyDependencies, dep)
@Directive
fun compileOptional(vararg dep: String) {
addToDependencies(project, optionalDependencies, dep, optional = true)
addToDependencies(project, dependencies, dep, optional = true)
}
@Directive
fun provided(vararg dep: String) {
addToDependencies(project, providedDependencies, dep)
}
@Directive
fun runtime(vararg dep: String) = addToDependencies(project, runtimeDependencies, dep)
@Directive
fun exclude(vararg dep: String) = addToDependencies(project, excludedDependencies, dep)
@Directive
fun native(vararg dep: String) = addToDependencies(project, nativeDependencies, dep)
}
class BuildConfig {
class Field(val name: String, val type: String, val value: Any) {
override fun hashCode() = name.hashCode()
override fun equals(other: Any?) = (other as Field).name == name
}
val fields = arrayListOf<Field>()
fun field(type: String, name: String, value: Any) {
fields.add(Field(name, type, value))
}
}
interface IBuildConfig {
var buildConfig: BuildConfig?
fun buildConfig(init: BuildConfig.() -> Unit) {
buildConfig = BuildConfig().apply {
init()
}
}
}
fun Project.defaultConfig(init: BuildConfig.() -> Unit) = let { project ->
BuildConfig().apply {
init()
project.defaultConfig = this
}
}
@Directive
fun Project.buildType(name: String, init: BuildTypeConfig.() -> Unit) = BuildTypeConfig(name).apply {
init()
addBuildType(name, this)
}
@Directive
fun Project.productFlavor(name: String, init: ProductFlavorConfig.() -> Unit) = ProductFlavorConfig(name).apply {
init()
addProductFlavor(name, this)
}

View file

@ -1,7 +0,0 @@
package com.beust.kobalt.api
import com.beust.kobalt.misc.toString
data class Task(val pluginName: String, val taskName: String) {
override fun toString() = toString("Task", pluginName, taskName)
}

View file

@ -1,86 +0,0 @@
package com.beust.kobalt.api
import com.beust.kobalt.IncrementalTaskInfo
import com.beust.kobalt.TaskResult
import com.beust.kobalt.Variant
import com.beust.kobalt.api.annotation.AnnotationDefault
import com.beust.kobalt.internal.IncrementalManager
import com.google.inject.Inject
/**
* Plug-ins that are ITaskContributor can use this class to manage their collection of tasks and
* implement the interface by delegating to an instance of this class (if injection permits).
*/
class TaskContributor @Inject constructor(val incrementalManagerFactory: IncrementalManager.IFactory)
: ITaskContributor {
val dynamicTasks = arrayListOf<DynamicTask>()
/**
* Register dynamic tasks corresponding to the variants found in the project,e.g. assembleDevDebug,
* assembleDevRelease, etc...
*
* TODO: this should be done automatically so that users don't have to invoke it themselves.
* Certain tasks could have a boolean flag "hasVariants" and any task that depends on it automatically
* depends on variants of that task.
*/
fun addVariantTasks(plugin: IPlugin, project: Project, context: KobaltContext, taskName: String,
group: String = AnnotationDefault.GROUP,
dependsOn: List<String> = emptyList(),
reverseDependsOn : List<String> = emptyList(),
runBefore : List<String> = emptyList(),
runAfter : List<String> = emptyList(),
runTask: (Project) -> TaskResult) {
Variant.allVariants(project).forEach { variant ->
val variantTaskName = variant.toTask(taskName)
dynamicTasks.add(DynamicTask(plugin, variantTaskName, variantTaskName, group, project,
dependsOn = dependsOn.map { variant.toTask(it) },
reverseDependsOn = reverseDependsOn.map { variant.toTask(it) },
runBefore = runBefore.map { variant.toTask(it) },
runAfter = runAfter.map { variant.toTask(it) },
closure = { p: Project ->
context.variant = variant
runTask(project)
}))
}
}
fun addTask(plugin: IPlugin, project: Project, taskName: String, description: String,
group: String = AnnotationDefault.GROUP,
dependsOn: List<String> = emptyList(),
reverseDependsOn : List<String> = emptyList(),
runBefore : List<String> = emptyList(),
runAfter : List<String> = emptyList(),
alwaysRunAfter: List<String> = emptyList(),
runTask: (Project) -> TaskResult) {
dynamicTasks.add(DynamicTask(plugin, taskName, description, group, project,
dependsOn = dependsOn,
reverseDependsOn = reverseDependsOn,
runBefore = runBefore,
runAfter = runAfter,
alwaysRunAfter = alwaysRunAfter,
closure = { p: Project ->
runTask(project)
}))
}
fun addIncrementalVariantTasks(plugin: IPlugin, project: Project, context: KobaltContext, taskName: String,
group: String = AnnotationDefault.GROUP,
dependsOn: List<String> = emptyList(),
reverseDependsOn : List<String> = emptyList(),
runBefore : List<String> = emptyList(),
runAfter : List<String> = emptyList(),
runTask: (Project) -> IncrementalTaskInfo) {
Variant.allVariants(project).forEach { variant ->
val variantTaskName = variant.toTask(taskName)
context.variant = variant
dynamicTasks.add(DynamicTask(plugin, variantTaskName, variantTaskName, group, project,
dependsOn = dependsOn.map { variant.toTask(it) },
reverseDependsOn = reverseDependsOn.map { variant.toTask(it) },
runBefore = runBefore.map { variant.toTask(it) },
runAfter = runAfter.map { variant.toTask(it) },
closure = incrementalManagerFactory.create().toIncrementalTaskClosure(taskName, runTask, variant)))
}
}
override fun tasksFor(project: Project, context: KobaltContext) : List<DynamicTask> = dynamicTasks
}

View file

@ -1,90 +0,0 @@
package com.beust.kobalt.api.annotation
/**
* Plugins that export directives should annotated those with this annotation so they can be documented and also
* receive special treatment for auto completion in the plug-in.
*/
annotation class Directive
object AnnotationDefault {
const val GROUP = "other"
}
@Retention(AnnotationRetention.RUNTIME)
annotation class Task(
/* This task's name */
val name: String,
/* The documentation for this task */
val description: String = "",
/** Used to show the task in the correct group in the IDE */
val group: String = AnnotationDefault.GROUP,
/** Dependency: tasks this task depends on */
val dependsOn: Array<String> = arrayOf(),
/** Dependency: tasks this task will be made dependend upon */
val reverseDependsOn: Array<String> = arrayOf(),
/** Ordering: tasks that need to be run before this one */
val runBefore: Array<String> = arrayOf(),
/** Ordering: tasks this task runs after */
val runAfter: Array<String> = arrayOf(),
/** Wrapper tasks */
val alwaysRunAfter: Array<String> = arrayOf()
)
@Retention(AnnotationRetention.RUNTIME)
annotation class IncrementalTask(
/* This task's name */
val name: String,
/* The documentation for this task */
val description: String = "",
/** Used to show the task in the correct group in the IDE */
val group: String = AnnotationDefault.GROUP,
/** Dependency: tasks this task depends on */
val dependsOn: Array<String> = arrayOf(),
/** Dependency: tasks this task will be made dependend upon */
val reverseDependsOn: Array<String> = arrayOf(),
/** Tasks that this task depends on */
val runBefore: Array<String> = arrayOf(),
/** Ordering: tasks this task runs after */
val runAfter: Array<String> = arrayOf(),
/** Wrapper tasks */
val alwaysRunAfter: Array<String> = arrayOf()
)
/**
* Plugins that export properties should annotate those with this annotation so they can be documented.
*/
@Retention(AnnotationRetention.RUNTIME)
annotation class ExportedPluginProperty(
/** Documentation for this property */
val doc: String = "",
/** The type of this property */
val type: String = ""
)
/**
* Plugins that export properties on the Project instance should annotate those with this annotation so
* they can be documented.
*/
@Retention(AnnotationRetention.RUNTIME)
annotation class ExportedProjectProperty(
/** Documentation for this property */
val doc: String = "",
/** The type of this property */
val type: String = ""
)

View file

@ -1,83 +0,0 @@
package com.beust.kobalt.archive
import com.beust.kobalt.*
import com.beust.kobalt.api.KobaltContext
import com.beust.kobalt.api.Project
import com.beust.kobalt.api.annotation.ExportedProjectProperty
import com.beust.kobalt.misc.JarUtils
import com.beust.kobalt.misc.KFiles
import com.beust.kobalt.misc.kobaltLog
import java.io.File
import java.util.*
class Archives {
companion object {
@ExportedProjectProperty(doc = "The name of the jar file", type = "String")
const val JAR_NAME = "jarName"
@ExportedProjectProperty(doc = "The name of the a jar file with a main() method", type = "String")
const val JAR_NAME_WITH_MAIN_CLASS = "jarNameWithMainClass"
fun defaultArchiveName(project: Project) = project.name +
if (project.version.isNullOrBlank()) "" else "-${project.version}"
fun generateArchive(project: Project,
context: KobaltContext,
archiveName: String?,
suffix: String,
includedFiles: List<IncludedFile>,
expandJarFiles : Boolean = false,
manifest: java.util.jar.Manifest? = null) : File {
val fullArchiveName = context.variant.archiveName(project, archiveName, suffix)
val archiveDir = File(KFiles.libsDir(project))
val result = File(archiveDir.path, fullArchiveName)
context.logger.log(project.name, 3, "Creating $result")
if (! Features.USE_TIMESTAMPS || isOutdated(project.directory, includedFiles, result)) {
try {
MetaArchive(result, manifest).use { metaArchive ->
JarUtils.addFiles(project.directory, includedFiles, metaArchive, expandJarFiles)
context.logger.log(project.name, 2, "Added ${includedFiles.size} files to $result")
context.logger.log(project.name, 1, " Created $result")
}
} catch (e: Throwable) {
// make sure that incomplete archive is deleted
// otherwise incremental build does not work on next run
result.delete()
throw e
}
} else {
context.logger.log(project.name, 3, " $result is up to date")
}
return result
}
private fun isOutdated(directory: String, includedFiles: List<IncludedFile>, output: File): Boolean {
if (! output.exists()) return true
val lastModified = output.lastModified()
includedFiles.forEach { root ->
val allFiles = root.allFromFiles(directory)
allFiles.forEach { relFile ->
val file = if (relFile.isAbsolute)
relFile // e.g. jar file or classes folder (of another project) when building a fat jar
else
File(KFiles.joinDir(directory, root.from, relFile.path))
if (file.isFile) {
if (file.lastModified() > lastModified) {
kobaltLog(3, " TS - Outdated $file and $output "
+ Date(file.lastModified()) + " " + Date(output.lastModified()))
return true
}
} else if (file.isDirectory) {
// e.g. classes folder (of another project) when building a fat jar
val includedFile = IncludedFile(From(""), To(""), listOf(IFileSpec.GlobSpec("**")))
if (isOutdated(file.absolutePath, listOf(includedFile), output))
return true
}
}
}
return false
}
}
}

View file

@ -1,6 +0,0 @@
package com.beust.kobalt.archive
interface AttributeHolder {
fun addAttribute(k: String, v: String)
}

View file

@ -1,28 +0,0 @@
package com.beust.kobalt.archive
import com.beust.kobalt.api.Project
import com.beust.kobalt.api.annotation.Directive
/**
* A jar is exactly like a zip with the addition of a manifest and an optional fatJar boolean.
*/
open class Jar(override val project: Project,
override var name : String = Archives.defaultArchiveName(project) + ".jar",
override var fatJar: Boolean = false) : Zip(project, name, fatJar), AttributeHolder {
@Directive
fun manifest(init: Manifest.(p: Manifest) -> Unit) : Manifest {
val m = Manifest(this)
m.init(m)
return m
}
// Need to specify the version or attributes will just be dropped
@Directive
override val attributes = arrayListOf(Pair("Manifest-Version", "1.0"))
override fun addAttribute(k: String, v: String) {
attributes.add(Pair(k, v))
}
}

View file

@ -1,11 +0,0 @@
package com.beust.kobalt.archive
import com.beust.kobalt.api.annotation.Directive
class Manifest(val jar: AttributeHolder) {
@Directive
fun attributes(k: String, v: String) {
jar.addAttribute(k, v)
}
}

View file

@ -1,125 +0,0 @@
package com.beust.kobalt.archive
import com.beust.kobalt.Glob
import com.beust.kobalt.misc.KFiles
import org.apache.commons.compress.archivers.ArchiveEntry
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream
import java.io.Closeable
import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream
import java.util.jar.Manifest
import org.apache.commons.compress.archivers.zip.ZipFile as ApacheZipFile
/**
* Abstraction of a zip/jar/war archive that automatically manages the addition of expanded jar files.
* Uses ZipArchiveOutputStream for fast inclusion of expanded jar files.
*/
class MetaArchive(outputFile: File, val manifest: Manifest?) : Closeable {
companion object {
const val MANIFEST_MF = "META-INF/MANIFEST.MF"
}
private val zos= ZipArchiveOutputStream(outputFile).apply {
encoding = "UTF-8"
}
init {
// If no manifest was passed, create an empty one so it's the first one in the archive
val m = manifest ?: Manifest()
val manifestFile = File.createTempFile("kobalt", "tmpManifest")
addEntry(ZipArchiveEntry("META-INF/"), null)
if (manifest != null) {
FileOutputStream(manifestFile).use { fos ->
m.write(fos)
}
}
val entry = zos.createArchiveEntry(manifestFile, MetaArchive.MANIFEST_MF)
addEntry(entry, FileInputStream(manifestFile))
}
fun addFile(f: File, entryFile: File, path: String?) {
maybeCreateParentDirectories(f)
addFile2(f, entryFile, path)
}
private fun addFile2(f: File, entryFile: File, path: String?) {
val file = f.normalize()
FileInputStream(file).use { inputStream ->
val actualPath = KFiles.fixSlashes(if (path != null) path + entryFile.path else entryFile.path)
ZipArchiveEntry(actualPath).let { entry ->
maybeCreateParentDirectories(File(actualPath))
maybeAddEntry(entry) {
addEntry(entry, inputStream)
}
}
}
}
private val createdDirs = hashSetOf<String>()
/**
* For an entry a/b/c/File, an entry needs to be created for each individual directory:
* a/
* a/b/
* a/b/c
* a/b/c/File
*/
private fun maybeCreateParentDirectories(file: File) {
val toCreate = arrayListOf<String>()
var current = file.parentFile
while (current != null && current.path != ".") {
if (!createdDirs.contains(current.path)) {
toCreate.add(0, KFiles.fixSlashes(current) + "/")
createdDirs.add(current.path)
}
current = current.parentFile
}
toCreate.forEach { dir ->
addEntry(ZipArchiveEntry(dir), null)
}
}
fun addArchive(jarFile: File) {
ApacheZipFile(jarFile).use { jar ->
val jarEntries = jar.entries
for (entry in jarEntries) {
maybeAddEntry(entry) {
zos.addRawArchiveEntry(entry, jar.getRawInputStream(entry))
}
}
}
}
private fun okToAdd(name: String) : Boolean {
val result = !KFiles.isExcluded(name,
Glob("META-INF/*.SF", "META-INF/*.DSA", "META-INF/*.RSA", MANIFEST_MF))
// if (name.startsWith("META-INF")) println((if (result) "ADDING" else "NOT ADDING") + " $name")
return result
}
override fun close() = zos.close()
private fun addEntry(entry: ArchiveEntry, inputStream: FileInputStream?) {
zos.putArchiveEntry(entry)
inputStream?.use { ins ->
ins.copyTo(zos, 50 * 1024)
}
zos.closeArchiveEntry()
}
private val seen = hashSetOf<String>()
private fun maybeAddEntry(entry: ArchiveEntry, action:() -> Unit) {
entry.name.let { name ->
if (!seen.contains(name) && okToAdd(name)) {
action()
}
seen.add(name)
}
}
}

View file

@ -1,13 +0,0 @@
package com.beust.kobalt.archive
import com.beust.kobalt.api.Project
import com.beust.kobalt.glob
class War(override val project: Project, override var name: String = Archives.defaultArchiveName(project) + ".war")
: Jar(project, name), AttributeHolder {
init {
include(from("src/main/webapp"), to(""), glob("**"))
include(from("kobaltBuild/classes"), to("WEB-INF/classes"), glob("**"))
}
}

View file

@ -1,27 +0,0 @@
package com.beust.kobalt.archive
import com.beust.kobalt.*
import com.beust.kobalt.api.Project
import com.beust.kobalt.api.annotation.Directive
open class Zip(open val project: Project, open var name: String = Archives.defaultArchiveName(project) + ".zip",
open var fatJar: Boolean = false): AttributeHolder, IncludeFromTo() {
val excludes = arrayListOf<Glob>()
@Directive
fun exclude(vararg files: String) {
files.forEach { excludes.add(Glob(it)) }
}
@Directive
fun exclude(vararg specs: Glob) {
specs.forEach { excludes.add(it) }
}
@Directive
open val attributes = arrayListOf(Pair("Manifest-Version", "1.0"))
override fun addAttribute(k: String, v: String) {
attributes.add(Pair(k, v))
}
}

View file

@ -1,37 +0,0 @@
package com.beust.kobalt.internal
import com.beust.kobalt.api.*
import com.beust.kobalt.misc.KFiles
/**
* Base class for JVM language plug-ins.
*/
abstract class BaseJvmPlugin<T>(open val configActor: ConfigActor<T>) :
BasePlugin(),
IConfigActor<T> by configActor,
ICompilerFlagContributor {
companion object {
// Run before other flag contributors
val FLAG_CONTRIBUTOR_PRIORITY = FlagContributor.DEFAULT_FLAG_PRIORITY - 10
}
protected fun maybeCompilerArgs(sourceSuffixes: List<String>, suffixesBeingCompiled: List<String>,
args: List<String>)
= if (sourceSuffixes.any { suffixesBeingCompiled.contains(it) }) args else emptyList()
override val flagPriority = FLAG_CONTRIBUTOR_PRIORITY
override fun accept(project: Project) = sourceFileCount(project) > 0
// IBuildConfigContributor
protected fun sourceFileCount(project: Project)
= KFiles.findSourceFiles(project.directory, project.sourceDirectories, sourceSuffixes()).size +
KFiles.findSourceFiles(project.directory, project.sourceDirectoriesTest, sourceSuffixes()).size
fun affinity(project: Project) = sourceFileCount(project)
// IDocContributor
open fun affinity(project: Project, context: KobaltContext) = sourceFileCount(project)
abstract fun sourceSuffixes() : List<String>
}

View file

@ -1,186 +0,0 @@
package com.beust.kobalt.internal
import com.beust.kobalt.TestResult
import com.beust.kobalt.api.IBuildListener
import com.beust.kobalt.api.KobaltContext
import com.beust.kobalt.api.Project
import com.beust.kobalt.api.ProjectBuildStatus
import com.beust.kobalt.misc.kobaltLog
import com.google.common.annotations.VisibleForTesting
import com.google.common.collect.ArrayListMultimap
import com.google.common.collect.Multimap
import java.util.*
abstract class BaseProjectRunner {
abstract fun runProjects(taskInfos: List<TaskManager.TaskInfo>, projects: List<Project>)
: TaskManager.RunTargetResult
companion object {
val TAG = "graph"
fun runBuildListenersForProject(project: Project, context: KobaltContext, start: Boolean,
status: ProjectBuildStatus = ProjectBuildStatus.SUCCESS) {
context.pluginInfo.buildListeners.forEach {
if (start) it.projectStart(project, context) else it.projectEnd(project, context, status)
}
}
fun runBuildListenersForTask(project: Project, context: KobaltContext, taskName: String, start: Boolean,
success: Boolean = false, testResult: TestResult? = null) {
context.pluginInfo.buildListeners.forEach {
if (start) {
it.taskStart(project, context, taskName)
} else {
val info = IBuildListener.TaskEndInfo(success, testResult?.shortMessage, testResult?.longMessage)
it.taskEnd(project, context, taskName, info)
}
}
}
/**
* Create a graph representing the tasks and their dependencies. That graph will then be run
* in topological order.
*
* @taskNames is the list of tasks requested by the user. @nodeMap maps these tasks to the nodes
* we'll be adding to the graph while @toName extracts the name of a node.
*/
@VisibleForTesting
fun <T> createTaskGraph(projectName: String, passedTasks: List<TaskManager.TaskInfo>,
nodeMap: Multimap<String, T>,
dependsOn: Multimap<String, String>,
reverseDependsOn: Multimap<String, String>,
runBefore: Multimap<String, String>,
runAfter: Multimap<String, String>,
alwaysRunAfter: Multimap<String, String>,
toName: (T) -> String,
accept: (T) -> Boolean):
DynamicGraph<T> {
/**
* Add an edge from @param from to all its tasks.
*/
fun addEdge(result: DynamicGraph<T>, from: String, to: String, newToProcess: ArrayList<T>, text: String) {
val froms = nodeMap[from]
froms.forEach { f: T ->
nodeMap[to].forEach { t: T ->
kobaltLog(TAG, " Adding edge ($text) $f -> $t")
result.addEdge(f, t)
newToProcess.add(t)
}
}
}
val result = DynamicGraph<T>()
val newToProcess = arrayListOf<T>()
val seen = hashSetOf<String>()
//
// Reverse the always map so that tasks can be looked up.
//
val always = ArrayListMultimap.create<String, String>().apply {
alwaysRunAfter.keySet().forEach { k ->
alwaysRunAfter[k].forEach { v ->
put(v, k)
}
}
}
//
// Keep only the tasks we need to run.
//
val taskInfos = passedTasks.filter {
it.matches(projectName)
}
// The nodes we need to process, initialized with the set of tasks requested by the user.
// As we run the graph and discover dependencies, new nodes get added to @param[newToProcess]. At
// the end of the loop, @param[toProcess] is cleared and all the new nodes get added to it. Then we loop.
val toProcess = ArrayList(taskInfos)
while (toProcess.size > 0) {
/**
* Whenever a task is added to the graph, we also add its alwaysRunAfter tasks.
*/
fun processAlways(always: Multimap<String, String>, node: T) {
kobaltLog(TAG, " Processing always for $node")
always[toName(node)]?.let { to: Collection<String> ->
to.forEach { t ->
nodeMap[t].forEach { from ->
kobaltLog(TAG, " Adding always edge $from -> $node")
result.addEdge(from, node)
}
}
kobaltLog(TAG, " ... done processing always for $node")
}
}
kobaltLog(TAG, " Current batch to process: $toProcess")
//
// Move dependsOn + reverseDependsOn in one multimap called allDepends
//
val allDependsOn = ArrayListMultimap.create<String, String>()
dependsOn.keySet().forEach { key ->
dependsOn[key].forEach { value ->
allDependsOn.put(key, value)
}
}
reverseDependsOn.keySet().forEach { key ->
reverseDependsOn[key].forEach { value ->
allDependsOn.put(value, key)
}
}
//
// Process each node one by one
//
toProcess.forEach { taskInfo ->
val taskName = taskInfo.taskName
kobaltLog(TAG, " ***** Current node: $taskName")
nodeMap[taskName].forEach {
result.addNode(it)
processAlways(always, it)
}
//
// dependsOn and reverseDependsOn are considered for all tasks, explicit and implicit
//
allDependsOn[taskName].forEach { to ->
addEdge(result, taskName, to, newToProcess, "dependsOn")
}
seen.add(taskName)
}
newToProcess.forEach { processAlways(always, it) }
toProcess.clear()
toProcess.addAll(
newToProcess
.filter { !seen.contains(toName(it)) }
.map { TaskManager.TaskInfo(toName(it)) })
newToProcess.clear()
}
//
// Now that we have all the tasks tnat need to run, process runBefore/runAfter, which
// are not allowed to add new tasks. Therefore, we only add edges to the graph if both
// the from and the to are already present.
//
val finalTaskNames = result.nodes.map { TaskManager.TaskInfo(it.toString()).taskName }
finalTaskNames.forEach { taskName ->
runBefore[taskName].filter { finalTaskNames.contains(it) }.forEach { from ->
addEdge(result, from, taskName, newToProcess, "runBefore")
}
runAfter[taskName].filter { finalTaskNames.contains(it) }.forEach { to ->
addEdge(result, to, taskName, newToProcess, "runAfter")
}
}
return result
}
}
}

View file

@ -1,128 +0,0 @@
package com.beust.kobalt.internal
import com.beust.kobalt.Args
import com.beust.kobalt.AsciiArt
import com.beust.kobalt.api.*
import com.beust.kobalt.misc.kobaltLog
import java.util.concurrent.ConcurrentHashMap
/**
* Record timings and statuses for tasks and projects and display them at the end of the build.
*/
class BuildListeners : IBuildListener, IBuildReportContributor {
class ProfilerInfo(val taskName: String, val durationMillis: Long)
class ProjectInfo(val projectName: String, var durationMillis: Long = 0,
var shortMessage: String? = null, var longMessage: String? = null)
private val startTimes = ConcurrentHashMap<String, Long>()
private val timings = arrayListOf<ProfilerInfo>()
private val projectInfos = hashMapOf<String, ProjectInfo>()
private var hasFailures = false
private val args: Args get() = Kobalt.INJECTOR.getInstance(Args::class.java)
private var buildStartTime: Long? = null
// IBuildListener
override fun taskStart(project: Project, context: KobaltContext, taskName: String) {
startTimes.put(taskName, System.currentTimeMillis())
if (! projectInfos.containsKey(project.name)) {
projectInfos.put(project.name, ProjectInfo(project.name))
}
}
// IBuildListener
override fun taskEnd(project: Project, context: KobaltContext, taskName: String, info: IBuildListener.TaskEndInfo) {
val success = info.success
if (! success) hasFailures = true
startTimes[taskName]?.let {
val taskTime = System.currentTimeMillis() - it
timings.add(ProfilerInfo(taskName, taskTime))
projectInfos[project.name]?.let {
it.durationMillis += taskTime
if (info.shortMessage != null && it.shortMessage == null) it.shortMessage = info.shortMessage
if (info.longMessage != null && it.longMessage == null) it.longMessage = info.longMessage
}
}
}
private val projectStatuses = arrayListOf<Pair<Project, String>>()
// IBuildListener
override fun projectStart(project: Project, context: KobaltContext) {
if (buildStartTime == null) buildStartTime = System.currentTimeMillis()
}
// IBuildListener
override fun projectEnd(project: Project, context: KobaltContext, status: ProjectBuildStatus) {
val shortMessage = projectInfos[project.name]?.shortMessage
val statusText = status.toString() + (if (shortMessage != null) " ($shortMessage)" else "")
projectStatuses.add(Pair(project, statusText))
}
// IBuildReportContributor
override fun generateReport(context: KobaltContext) {
fun formatMillis(millis: Long, format: String) = String.format(format, millis.toDouble() / 1000)
fun formatMillisRight(millis: Long, length: Int) = formatMillis(millis, "%1\$$length.2f")
fun formatMillisLeft(millis: Long, length: Int) = formatMillis(millis, "%1\$-$length.2f")
fun millisToSeconds(millis: Long) = (millis.toDouble() / 1000).toInt()
val profiling = args.profiling
if (profiling) {
kobaltLog(1, "\n" + AsciiArt.horizontalSingleLine + " Timings (in seconds)")
timings.sortedByDescending { it.durationMillis }.forEach {
kobaltLog(1, formatMillisRight(it.durationMillis, 10) + " " + it.taskName)
}
kobaltLog(1, "\n")
}
// Calculate the longest short message so we can create a column long enough to contain it
val width = 12 + (projectInfos.values.map { it.shortMessage?.length ?: 0 }.maxBy { it } ?: 0)
fun col1(s: String) = String.format(" %1\$-30s", s)
fun col2(s: String) = String.format(" %1\$-${width}s", s)
fun col3(s: String) = String.format(" %1\$-8s", s)
// Only print the build report if there is more than one project and at least one of them failed
if (timings.any()) {
// if (timings.size > 1 && hasFailures) {
val line = listOf(col1("Project"), col2("Build status"), col3("Time"))
.joinToString(AsciiArt.verticalBar)
val table = StringBuffer()
table.append(AsciiArt.logBox(listOf(line), AsciiArt.bottomLeft2, AsciiArt.bottomRight2, indent = 10) + "\n")
projectStatuses.forEach { pair ->
val projectName = pair.first.name
val cl = listOf(col1(projectName), col2(pair.second),
col3(formatMillisLeft(projectInfos[projectName]!!.durationMillis, 8)))
.joinToString(AsciiArt.verticalBar)
table.append(" " + AsciiArt.verticalBar + " " + cl + " " + AsciiArt.verticalBar + "\n")
}
table.append(" " + AsciiArt.lowerBox(line.length))
kobaltLog(1, table.toString())
// }
}
val buildTime =
if (buildStartTime != null)
millisToSeconds(System.currentTimeMillis() - buildStartTime!!)
else
0
// BUILD SUCCESSFUL / FAILED message
val message =
if (hasFailures) {
String.format("BUILD FAILED", buildTime)
} else if (! args.sequential) {
val sequentialBuildTime = ((projectInfos.values.sumByDouble { it.durationMillis.toDouble() }) / 1000)
.toInt()
String.format("PARALLEL BUILD SUCCESSFUL (%d SECONDS), sequential build would have taken %d seconds",
buildTime, sequentialBuildTime)
} else {
String.format("BUILD SUCCESSFUL (%d SECONDS)", buildTime)
}
kobaltLog(1, message)
}
}

View file

@ -1,14 +0,0 @@
package com.beust.kobalt.internal
fun <T> Collection<T>.doWhile(condition: (T) -> Boolean, action: (T) -> Unit) {
var i = 0
var done = false
while (i < size && ! done) {
elementAt(i).let { element ->
if (! condition(element)) done = true
else action(element)
}
i++
}
}

View file

@ -1,232 +0,0 @@
package com.beust.kobalt.internal
import com.beust.kobalt.TaskResult
import com.beust.kobalt.api.*
import com.beust.kobalt.maven.DependencyManager
import com.beust.kobalt.maven.aether.Scope
import com.beust.kobalt.maven.dependency.FileDependency
import com.beust.kobalt.misc.KFiles
import com.google.inject.Inject
import java.io.File
import java.nio.file.Paths
import java.util.*
/**
* Central place to compile files, used by plug-ins and non plug-ins.
*/
class CompilerUtils @Inject constructor(val files: KFiles, val dependencyManager: DependencyManager) {
class CompilerResult(val successResults: List<TaskResult>, val failedResult: TaskResult?)
fun invokeCompiler(project: Project, context: KobaltContext, compiler: ICompilerDescription,
sourceDirectories: List<File>, isTest: Boolean, buildDirectory: File): CompilerResult {
val results = arrayListOf<TaskResult>()
var failedResult: TaskResult? = null
val contributedSourceDirs =
if (isTest) {
context.testSourceDirectories(project)
} else {
context.sourceDirectories(project)
}
val sourceFiles = KFiles.findSourceFiles(project.directory,
contributedSourceDirs.map { it.path }, compiler.sourceSuffixes)
if (sourceFiles.isNotEmpty()) {
// TODO: createCompilerActionInfo recalculates the source files, only compute them
// once and pass them
val info = createCompilerActionInfo(project, context, compiler, isTest,
sourceDirectories, sourceSuffixes = compiler.sourceSuffixes, buildDirectory = buildDirectory)
val thisResult = invokeCompiler(project, context, compiler, info)
results.addAll(thisResult.successResults)
if (failedResult == null) {
failedResult = thisResult.failedResult
}
} else {
context.logger.log(project.name, 2,
"${compiler.name} compiler not running on ${project.name} since no source files were found")
}
return CompilerResult(results, failedResult)
}
fun invokeCompiler(project: Project, context: KobaltContext, compiler: ICompilerDescription, info: CompilerActionInfo)
: CompilerResult {
val results = arrayListOf<TaskResult>()
var failedResult: TaskResult? = null
val thisResult = compiler.compile(project, context, info)
results.add(thisResult)
if (!thisResult.success && failedResult == null) {
failedResult = thisResult
}
return CompilerResult(results, failedResult)
}
/**
* Create a CompilerActionInfo (all the information that a compiler needs to know) for the given parameters.
* Runs all the contributors and interceptors relevant to that task.
*/
fun createCompilerActionInfo(project: Project, context: KobaltContext, compiler: ICompilerDescription,
isTest: Boolean, sourceDirectories: List<File>, sourceSuffixes: List<String>, buildDirectory: File)
: CompilerActionInfo {
copyResources(project, context, SourceSet.of(isTest))
val fullClasspath = dependencyManager.calculateDependencies(project, context,
scopes = if (isTest) {
listOf(Scope.COMPILE, Scope.COMPILEONLY, Scope.TEST)
} else {
listOf(Scope.COMPILE, Scope.COMPILEONLY)
})
File(project.directory, buildDirectory.path).mkdirs()
// Remove all the excluded dependencies from the classpath
var classpath = fullClasspath
// The classpath needs to contain $buildDirectory/classes as well so that projects that contain
// multiple languages can use classes compiled by the compiler run before them.
fun containsClassFiles(dir: File) =
KFiles.containsCertainFile(dir) {
it.isFile && it.name.endsWith("class")
}
// if (buildDirectory.exists()) {
if (containsClassFiles(buildDirectory)) {
classpath += FileDependency(buildDirectory.path)
}
val initialSourceDirectories = ArrayList<File>(sourceDirectories)
// Source directories from the contributors
val contributedSourceDirs =
if (isTest) {
context.pluginInfo.testSourceDirContributors.flatMap { it.testSourceDirectoriesFor(project, context) }
} else {
context.pluginInfo.sourceDirContributors.flatMap { it.sourceDirectoriesFor(project, context) }
}
initialSourceDirectories.addAll(contributedSourceDirs)
// Transform them with the interceptors, if any
val allSourceDirectories =
if (isTest) {
initialSourceDirectories
} else {
context.pluginInfo.sourceDirectoriesInterceptors.fold(initialSourceDirectories.toList(),
{ sd, interceptor -> interceptor.intercept(project, context, sd) })
}.filter {
File(project.directory, it.path).exists()
}.filter {
! KFiles.isResource(it.path)
}.distinctBy {
Paths.get(it.path)
}
// Now that we have all the source directories, find all the source files in them. Note that
// depending on the compiler's ability, sourceFiles can actually contain a list of directories
// instead of individual source files.
val projectDirectory = File(project.directory)
val sourceFiles =
if (compiler.canCompileDirectories) {
allSourceDirectories.map { File(projectDirectory, it.path).path }
} else {
files.findRecursively(projectDirectory, allSourceDirectories,
{ file -> sourceSuffixes.any { file.endsWith(it) } })
.map { File(projectDirectory, it).path }
}
// Special treatment if we are compiling Kotlin files and the project also has a java source
// directory. In this case, also pass that java source directory to the Kotlin compiler as is
// so that it can parse its symbols
// Note: this should actually be queried on the compiler object so that this method, which
// is compiler agnostic, doesn't hardcode Kotlin specific stuff
val extraSourceFiles = arrayListOf<String>()
fun containsJavaFiles(dir: File) =
KFiles.containsCertainFile(dir) {
it.isFile && it.name.endsWith("java")
}
if (sourceSuffixes.any { it.contains("kt")}) {
val directories = if (isTest) project.sourceDirectoriesTest else project.sourceDirectories
directories.forEach {
val javaDir = File(KFiles.joinDir(project.directory, it))
if (javaDir.exists() && containsJavaFiles(javaDir) && ! KFiles.isResource(javaDir.path)) {
extraSourceFiles.add(javaDir.path)
// Add all the source directories contributed as potential Java directories too
// (except our own)
context.pluginInfo.sourceDirContributors.forEach {
val sd = it.sourceDirectoriesFor(project, context).map { it.path }
.filter { ! it.contains("kotlin") }
if (! sd.contains("kotlin")) {
extraSourceFiles.addAll(sd)
}
}
}
}
}
val distinctSources = (sourceFiles + extraSourceFiles).distinctBy { File(it).toURI().normalize().path }
val allSources = distinctSources
.map { File(it).path }
.distinct()
.filter { File(it).exists() }
// Finally, alter the info with the compiler interceptors before returning it
val initialActionInfo = CompilerActionInfo(projectDirectory.path, classpath, allSources,
sourceSuffixes, buildDirectory, emptyList() /* the flags will be provided by flag contributors */,
emptyList(), context.internalContext.forceRecompile)
val result = context.pluginInfo.compilerInterceptors.fold(initialActionInfo, { ai, interceptor ->
interceptor.intercept(project, context, ai)
})
//
// friendPaths
//
val friendPaths = KFiles.joinDir(project.buildDirectory, KFiles.CLASSES_DIR)
return result
}
/**
* Copy the resources from a source directory to the build one
*/
private fun copyResources(project: Project, context: KobaltContext, sourceSet: SourceSet) {
val outputDir = sourceSet.outputDir
val variantSourceDirs = context.variant.resourceDirectories(project, sourceSet)
if (variantSourceDirs.isNotEmpty()) {
context.logger.log(project.name, 2, "Copying $sourceSet resources")
val absOutputDir = File(KFiles.joinDir(project.directory, project.buildDirectory, outputDir))
variantSourceDirs
.map { File(project.directory, it.path) }
.filter(File::exists)
.forEach {
context.logger.log(project.name, 2, "Copying from $it to $absOutputDir")
KFiles.copyRecursively(it, absOutputDir, replaceExisting = true)
}
} else {
context.logger.log(project.name, 2, "No resources to copy for $sourceSet")
}
}
fun sourceCompilerFlags(project: Project?, context: KobaltContext, info: CompilerActionInfo) : List<String> {
val adapters = context.pluginInfo.compilerFlagContributors.map {
val closure = { project: Project, context: KobaltContext, currentFlags: List<String>,
suffixesBeingCompiled: List<String>
-> it.compilerFlagsFor(project, context, currentFlags, suffixesBeingCompiled) }
FlagContributor(it.flagPriority, closure)
}
return compilerFlags(project, context, info, adapters)
}
fun compilerFlags(project: Project?, context: KobaltContext, info: CompilerActionInfo,
adapters: List<FlagContributor>) : List<String> {
val result = arrayListOf<String>()
if (project != null) {
adapters.sortedBy { it.flagPriority }
adapters.forEach {
result.addAll(it.flagsFor(project, context, result, info.suffixesBeingCompiled))
}
}
return result
}
}

View file

@ -1,408 +0,0 @@
package com.beust.kobalt.internal
import com.beust.kobalt.*
import com.beust.kobalt.misc.*
import com.google.common.collect.HashMultimap
import java.lang.reflect.InvocationTargetException
import java.util.*
import java.util.concurrent.*
open class TaskResult2<T>(success: Boolean, testResult: TestResult? = null,
errorMessage: String? = null, val value: T) : TaskResult(success, testResult, errorMessage) {
override fun toString() = com.beust.kobalt.misc.toString("TaskResult", "value", value, "success", success)
}
class DynamicGraph<T> {
val VERBOSE = 3
val values : Collection<T> get() = nodes.map { it.value }
val nodes = hashSetOf<PrivateNode<T>>()
private val dependedUpon = HashMultimap.create<PrivateNode<T>, PrivateNode<T>>()
private val dependingOn = HashMultimap.create<PrivateNode<T>, PrivateNode<T>>()
class PrivateNode<T>(val value: T) {
override fun hashCode() = value!!.hashCode()
override fun equals(other: Any?) : Boolean {
val result = if (other is PrivateNode<*>) other.value == value else false
return result
}
override fun toString() = value.toString()
}
companion object {
fun <T> transitiveClosure(root: T, childrenFor: (T) -> List<T>) : List<T> {
val result = arrayListOf<T>()
val seen = hashSetOf<T>()
val toProcess = arrayListOf<T>().apply {
add(root)
}
while (toProcess.any()) {
val newToProcess = arrayListOf<T>()
toProcess.forEach {
if (! seen.contains(it)) {
result.add(it)
newToProcess.addAll(childrenFor(it))
seen.add(it)
}
}
toProcess.clear()
toProcess.addAll(newToProcess)
}
return result
}
class Node<T>(val value: T, val children: List<Node<T>>) {
fun dump(root : Node<T> = this, indent: String = "") : String {
return StringBuffer().apply {
append(indent).append(root.value).append("\n")
root.children.forEach {
append(dump(it, indent + " "))
}
}.toString()
}
}
fun <T> transitiveClosureGraph(roots: List<T>, childrenFor: (T) -> List<T>,
filter: (T) -> Boolean): List<Node<T>>
= roots.map { transitiveClosureGraph(it, childrenFor, filter) }
fun <T> transitiveClosureGraph(root: T, childrenFor: (T) -> List<T>,
filter: (T) -> Boolean = { t: T -> true },
seen: HashSet<T> = hashSetOf()) : Node<T> {
val children = arrayListOf<Node<T>>()
childrenFor(root).filter(filter).forEach { child ->
if (! seen.contains(child)) {
seen.add(child)
val c = transitiveClosureGraph(child, childrenFor, filter, seen)
children.add(c)
}
}
return Node(root, children)
}
}
fun childrenOf(v: T) : Collection<T> = dependedUpon[PrivateNode(v)].map { it.value }
fun transitiveClosure(root: T)
= transitiveClosure(root) { element -> dependedUpon[PrivateNode(element)].map { it.value } }
fun addNode(t: T) = synchronized(nodes) {
nodes.add(PrivateNode(t))
}
fun removeNode(t: T) = synchronized(nodes) {
kobaltLog(VERBOSE, " Removing node $t")
PrivateNode(t).let { node ->
nodes.remove(node)
dependingOn.removeAll(node)
val set = dependedUpon.keySet()
val toReplace = arrayListOf<Pair<PrivateNode<T>, Collection<PrivateNode<T>>>>()
set.forEach { du ->
val l = ArrayList(dependedUpon[du])
l.remove(node)
toReplace.add(Pair(du, l))
}
toReplace.forEach {
dependedUpon.replaceValues(it.first, it.second)
}
}
}
/**
* Make "from" depend on "to" ("from" is no longer free).
*/
fun addEdge(from: T, to: T) {
val fromNode = PrivateNode(from)
nodes.add(fromNode)
val toNode = PrivateNode(to)
nodes.add(PrivateNode(to))
dependingOn.put(toNode, fromNode)
dependedUpon.put(fromNode, toNode)
}
val freeNodes: Set<T>
get() {
val nonFree = hashSetOf<T>()
synchronized(nodes) {
nodes.forEach {
val du = dependedUpon[it]
if (du != null && du.size > 0) {
nonFree.add(it.value)
}
}
val result = nodes.map { it.value }.filter { !nonFree.contains(it) }.toHashSet()
kobaltLog(VERBOSE, " Free nodes: $result")
return result
}
}
fun dump() : String {
val result = StringBuffer()
result.append("************ Graph dump ***************\n")
val free = arrayListOf<PrivateNode<T>>()
nodes.forEach { node ->
val d = dependedUpon.get(node)
if (d == null || d.isEmpty()) {
free.add(node)
}
}
result.append("All nodes: $values\n").append("Free nodes: $free").append("\nDependent nodes:\n")
nodes.forEach {
val deps = dependedUpon.get(it)
if (! deps.isEmpty()) {
result.append(" $it -> $deps\n")
}
}
return result.toString()
}
}
interface IWorker<T> : Callable<TaskResult2<T>> {
/**
* @return list of tasks this worker is working on.
*/
// val tasks : List<T>
val name: String
/**
* @return the priority of this task.
*/
val priority : Int
}
interface IThreadWorkerFactory<T> {
/**
* Creates {@code IWorker} for specified set of tasks. It is not necessary that
* number of workers returned be same as number of tasks entered.
*
* @param nodes tasks that need to be executed
* @return list of workers
*/
fun createWorkers(nodes: Collection<T>) : List<IWorker<T>>
}
class DynamicGraphExecutor<T>(val graph : DynamicGraph<T>, val factory: IThreadWorkerFactory<T>,
val threadCount: Int = 1) {
val executor : ExecutorService
= Executors.newFixedThreadPool(threadCount, NamedThreadFactory("DynamicGraphExecutor"))
val completion = ExecutorCompletionService<TaskResult2<T>>(executor)
data class HistoryLog(val name: String, val timestamp: Long, val threadId: Long, val start: Boolean)
val historyLog = arrayListOf<HistoryLog>()
val threadIds = ConcurrentHashMap<Long, Long>()
fun run() : TaskResult {
try {
return run2()
} finally {
executor.shutdown()
}
}
private fun run2() : TaskResult {
var running = 0
val nodesRun = hashSetOf<T>()
var failedResult: TaskResult? = null
val newFreeNodes = HashSet<T>(graph.freeNodes)
while (failedResult == null && (running > 0 || newFreeNodes.size > 0)) {
nodesRun.addAll(newFreeNodes)
val callables : List<IWorker<T>> = factory.createWorkers(newFreeNodes).map {
it -> object: IWorker<T> {
override val priority: Int
get() = it.priority
override val name: String get() = it.name
override fun call(): TaskResult2<T> {
val threadId = Thread.currentThread().id
historyLog.add(HistoryLog(it.name, System.currentTimeMillis(), threadId,
start = true))
threadIds.put(threadId, threadId)
val result = it.call()
historyLog.add(HistoryLog(it.name, System.currentTimeMillis(), Thread.currentThread().id,
start = false))
return result
}
}
}
callables.forEach { completion.submit(it) }
running += callables.size
try {
val future = completion.take()
val taskResult = future.get(2, TimeUnit.SECONDS)
running--
if (taskResult.success) {
nodesRun.add(taskResult.value)
kobaltLog(3, "Task succeeded: $taskResult")
graph.removeNode(taskResult.value)
newFreeNodes.clear()
newFreeNodes.addAll(graph.freeNodes.minus(nodesRun))
} else {
kobaltLog(3, "Task failed: $taskResult")
newFreeNodes.clear()
if (failedResult == null) {
failedResult = taskResult
}
}
} catch(ex: TimeoutException) {
kobaltLog(3, "Time out")
} catch(ex: Exception) {
val ite = ex.cause
if (ite is InvocationTargetException) {
if (ite.targetException is KobaltException) {
throw (ex.cause as InvocationTargetException).targetException
} else {
error("Error: ${ite.cause?.message}", ite.cause)
failedResult = TaskResult(success = false, errorMessage = ite.cause?.message)
}
} else {
error("Error: ${ex.message}", ex)
failedResult = TaskResult(success = false, errorMessage = ex.message)
}
}
}
return if (failedResult != null) failedResult else TaskResult()
}
fun dumpHistory() {
kobaltLog(1, "Thread report")
val table = AsciiTable.Builder()
.columnWidth(11)
threadIds.keys.forEach {
table.columnWidth(24)
}
table.header("Time (sec)")
threadIds.keys.forEach {
table.header("Thread " + it.toString())
}
fun toSeconds(millis: Long) = (millis / 1000).toInt().toString()
fun displayCompressedLog(table: AsciiTable.Builder) : AsciiTable.Builder {
data class CompressedLog(val timestamp: Long, val threadMap: HashMap<Long, String>)
fun compressLog(historyLog: List<HistoryLog>): ArrayList<CompressedLog> {
val compressed = arrayListOf<CompressedLog>()
var currentLog: CompressedLog? = null
val projectStart = hashMapOf<String, Long>()
fun toName(hl: HistoryLog) : String {
var duration = ""
if (! hl.start) {
val start = projectStart[hl.name]
if (start != null) {
duration = " (" + ((hl.timestamp - start) / 1000)
.toInt().toString() + ")"
} else {
kobaltLog(1, "DONOTCOMMIT")
}
}
return hl.name + duration
}
historyLog.forEach { hl ->
kobaltLog(1, "CURRENT LOG: " + currentLog + " HISTORY LINE: " + hl)
if (hl.start) {
projectStart[hl.name] = hl.timestamp
}
if (currentLog == null) {
currentLog = CompressedLog(hl.timestamp, hashMapOf(hl.threadId to hl.name))
} else currentLog?.let { cl ->
if (! hl.start || hl.timestamp - cl.timestamp < 1000) {
kobaltLog(1, " CURRENT LOG IS WITHING ONE SECOND: $hl")
cl.threadMap[hl.threadId] = toName(hl)
} else {
kobaltLog(1, " ADDING COMPRESSED LINE $cl")
compressed.add(cl)
currentLog = CompressedLog(hl.timestamp, hashMapOf(hl.threadId to toName(hl)))
}
}
}
return compressed
}
compressLog(historyLog).forEach {
val row = arrayListOf<String>()
row.add(toSeconds(it.timestamp))
it.threadMap.values.forEach {
row.add(it)
}
table.addRow(row)
}
return table
}
fun displayRegularLog(table: AsciiTable.Builder) : AsciiTable.Builder {
if (historyLog.any()) {
if (historyLog[0] != null) {
val start = historyLog[0].timestamp
val projectStart = ConcurrentHashMap<String, Long>()
historyLog.forEach { line ->
val row = arrayListOf<String>()
row.add(toSeconds(line.timestamp - start))
threadIds.keys.forEach {
if (line.threadId == it) {
var duration = ""
if (line.start) {
projectStart[line.name] = line.timestamp
} else {
val projectStart = projectStart[line.name]
if (projectStart != null) {
duration = " (" + ((line.timestamp - projectStart) / 1000)
.toInt().toString() + ")"
} else {
warn("Couldn't determine project start: " + line.name)
}
}
row.add((line.name + duration))
} else {
row.add("")
}
}
table.addRow(row)
}
} else {
warn("Couldn't find historyLog")
}
}
return table
}
kobaltLog(1, displayRegularLog(table).build())
}
}
fun main(argv: Array<String>) {
val dg = DynamicGraph<String>().apply {
// a -> b
// b -> c, d
// e
addEdge("a", "b")
addEdge("b", "c")
addEdge("b", "d")
addNode("e")
}
val factory = object : IThreadWorkerFactory<String> {
override fun createWorkers(nodes: Collection<String>): List<IWorker<String>> {
return nodes.map {
object: IWorker<String> {
override fun call(): TaskResult2<String>? {
kobaltLog(1, " Running worker $it")
return TaskResult2(true, value = it)
}
override val priority: Int get() = 0
override val name: String = "workerName"
}
}
}
}
DynamicGraphExecutor(dg, factory).run()
}

View file

@ -1,198 +0,0 @@
package com.beust.kobalt.internal
import com.beust.kobalt.*
import com.beust.kobalt.api.*
import com.beust.kobalt.misc.KFiles
import com.google.common.annotations.VisibleForTesting
import com.google.inject.Inject
import java.io.File
import java.util.*
/**
* Base class for testing frameworks that are invoked from a main class with arguments. Test runners can
* subclass this class and override mainClass, args and the name of the dependency that should trigger this runner.
*/
abstract class GenericTestRunner: ITestRunnerContributor {
abstract val dependencyName : String
abstract val mainClass: String
abstract val annotationPackage: String
abstract val runnerName: String
open var shortMessage: String? = null
open var longMessage: String? = null
@Inject
private lateinit var jvm: Jvm
abstract fun args(project: Project, context: KobaltContext, classpath: List<IClasspathDependency>,
testConfig: TestConfig) : List<String>
open fun onFinish(project: Project) {}
open val extraClasspath: List<String> = emptyList()
open fun filterTestClasses(project: Project, context: KobaltContext, classes: List<String>) : List<String> = classes
override fun run(project: Project, context: KobaltContext, configName: String,
classpath: List<IClasspathDependency>) : TaskResult {
val tr = runTests(project, context, classpath, configName)
return TaskResult(tr.success, testResult = tr)
}
override fun affinity(project: Project, context: KobaltContext) : Int {
val result =
if (project.testDependencies.any { it.id.contains(dependencyName) }) IAffinity.DEFAULT_POSITIVE_AFFINITY
else 0
return result
}
protected fun findTestClasses(project: Project, context: KobaltContext, testConfig: TestConfig): List<String> {
val testClassDir = KFiles.joinDir(project.buildDirectory, KFiles.TEST_CLASSES_DIR)
val path = testClassDir.apply {
File(this).mkdirs()
}
val files = IFileSpec.GlobSpec(toClassPaths(testConfig.testIncludes))
.toFiles(project.directory, path, testConfig.testExcludes.map { Glob(it) })
val testClasses = files
.map {
File(KFiles.joinDir(project.directory, testClassDir, it.path))
}
val result = testClasses.map {
val prefix = KFiles.joinDir(project.directory, testClassDir)
val className = it.toString().substring(prefix.length + 1)
.replace("/", ".").replace("\\", ".").replace(".class", "")
Pair(it, className)
}
// .filter {
// val result = acceptClass(it.first, it.second, testClasspath, File(testClassDir))
// result
// }
context.logger.log(project.name, 2, "Found ${result.size} test classes")
return filterTestClasses(project, context, result.map { it.second })
}
/**
* Accept the given class if it contains an annotation of the current test runner's package. Annotations
* are looked up on both the classes and methods.
*/
// private fun acceptClass(cf: File, className: String, testClasspath: List<IClasspathDependency>,
// testClassDir: File): Boolean {
// val cp = (testClasspath.map { it.jarFile.get() } + listOf(testClassDir)).map { it.toURI().toURL() }
// try {
// val cls = URLClassLoader(cp.toTypedArray()).loadClass(className)
// val ann = cls.annotations.filter {
// val qn = it.annotationClass.qualifiedName
// qn != null && qn.contains(annotationPackage)
// }
// if (ann.any()) {
// return true
// } else {
// val ann2 = cls.declaredMethods.flatMap { it.declaredAnnotations.toList() }.filter { it.toString()
// .contains(annotationPackage)}
// if (ann2.any()) {
// val a0 = ann2[0]
// return true
// }
// }
// } catch(ex: Throwable) {
// println("Exception: " + ex.message)
// return false
// }
// return false
// }
private fun toClassPaths(paths: List<String>): ArrayList<String> =
paths.map { if (it.endsWith("class")) it else it + "class" }.toCollection(ArrayList())
/**
* @return true if all the tests passed
*/
open fun runTests(project: Project, context: KobaltContext, classpath: List<IClasspathDependency>,
configName: String) : TestResult {
var result = false
context.logger.log(project.name, 1, "Running tests with $runnerName")
val testConfig = project.testConfigs.firstOrNull { it.name == configName }
var errorCode = -1
if (testConfig != null) {
val args = args(project, context, classpath, testConfig)
if (args.size > 0) {
val java = jvm.javaExecutable
val jvmArgs = calculateAllJvmArgs(project, context, testConfig, classpath,
Kobalt.INJECTOR.getInstance (PluginInfo::class.java))
val allArgs = arrayListOf<String>().apply {
add(java!!.absolutePath)
addAll(jvmArgs)
add(mainClass)
addAll(args)
}
val pb = ProcessBuilder(allArgs)
pb.directory(File(project.directory))
pb.inheritIO()
context.logger.log(project.name, 2, "Running tests with classpath size ${classpath.size}")
context.logger.log(project.name, 2, "Launching " + allArgs.joinToString(" "))
val process = pb.start()
errorCode = process.waitFor()
result = result || errorCode == 0
} else {
context.logger.log(project.name, 1, " No tests to run")
result = true
}
} else {
throw KobaltException("Couldn't find a test configuration named \"$configName\"")
}
onFinish(project)
if (errorCode == 0) {
context.logger.log(project.name, 1, "All tests passed")
} else {
context.logger.log(project.name, 1, longMessage!!)
}
return TestResult(result, shortMessage, longMessage)
}
/*
** @return all the JVM flags from contributors and interceptors.
*/
@VisibleForTesting
fun calculateAllJvmArgs(project: Project, context: KobaltContext,
testConfig: TestConfig, classpath: List<IClasspathDependency>, pluginInfo: IPluginInfo) : List<String> {
val fullClasspath = classpath.map { it.jarFile.get().absolutePath } + extraClasspath
// Default JVM args
val jvmFlags = arrayListOf<String>().apply {
addAll(testConfig.jvmArgs)
add("-ea")
add("-classpath")
add(fullClasspath.joinToString(File.pathSeparator))
}
// JVM flags from the contributors
val jvmFlagsFromContributors = pluginInfo.testJvmFlagContributors.flatMap {
it.testJvmFlagsFor(project, context, jvmFlags)
}
// JVM flags from the interceptors (these overwrite flags instead of just adding to the list)
val result = ArrayList(jvmFlags + jvmFlagsFromContributors)
pluginInfo.testJvmFlagInterceptors.forEach {
val newFlags = it.testJvmFlagsFor(project, context, result)
result.clear()
result.addAll(newFlags)
}
if (result.any()) {
context.logger.log(project.name, 2,
"Final JVM test flags after running the contributors and interceptors: $result")
}
return result
}
}

View file

@ -1,35 +0,0 @@
package com.beust.kobalt.internal
/**
* Generic operations on graph-like structures.
*/
object GraphUtil {
/**
* Apply the operation in `closure` to all the nodes in the tree.
*/
fun <T> map(roots: List<T>, children: (T) -> List<T>, closure: (T) -> Unit) {
roots.forEach {
closure(it)
map(children(it), children, closure)
}
}
/**
* Display each node in the roots by calling the `display` function on each of them.
*/
fun <T> displayGraph(roots: List<T>,
children: (T) -> List<T>,
display: (node: T, indent: String) -> Unit) {
fun pd(node: T, indent: String) {
display(node, indent)
children(node).forEach {
pd(it, indent + " ")
}
}
roots.forEach {
pd(it, "")
}
}
}

View file

@ -1,186 +0,0 @@
package com.beust.kobalt.internal
import com.beust.kobalt.Args
import com.beust.kobalt.IncrementalTaskInfo
import com.beust.kobalt.TaskResult
import com.beust.kobalt.Variant
import com.beust.kobalt.api.Kobalt
import com.beust.kobalt.api.Project
import com.beust.kobalt.misc.KFiles
import com.beust.kobalt.misc.kobaltLog
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.google.inject.Inject
import com.google.inject.assistedinject.Assisted
import java.io.File
import java.io.FileReader
import java.nio.charset.Charset
import java.nio.file.Files
import java.nio.file.Paths
import java.util.*
/**
* Manage the file .kobalt/buildInfo.json, which keeps track of input and output checksums to manage
* incremental builds.
*/
class IncrementalManager @Inject constructor(val args: Args, @Assisted val fileName : String) {
private data class TaskInfo(val taskName: String, var inputChecksum: String? = null,
var outputChecksum: String? = null)
private class BuildInfo(var tasks: List<TaskInfo>)
interface IFactory {
fun create(@Assisted fileName: String = IncrementalManager.BUILD_INFO_FILE) : IncrementalManager
}
companion object {
val BUILD_INFO_FILE = KFiles.joinDir(KFiles.KOBALT_DOT_DIR, "buildInfo.json")
}
private fun buildInfo() = File(fileName).let { file ->
if (file.exists()) {
Gson().fromJson(FileReader(file), BuildInfo::class.java) ?: BuildInfo(emptyList())
} else {
BuildInfo(emptyList())
}
}
private fun taskInfos() = hashMapOf<String, TaskInfo>().apply {
buildInfo().tasks.forEach {
put(it.taskName, it)
}
}
private fun save(map: Map<String, TaskInfo>) {
val bi = BuildInfo(map.values.toList())
val json = GsonBuilder().setPrettyPrinting().create().toJson(bi)
Files.write(Paths.get(fileName), json.toByteArray(Charset.defaultCharset()))
}
private fun taskInfoFor(taskInfos: HashMap<String, TaskInfo>, taskName: String)
= taskInfos.getOrPut(taskName, { -> TaskInfo(taskName) })
fun saveInputChecksum(taskName: String, inputChecksum: String) {
synchronized(BUILD_INFO_FILE) {
with(taskInfos()) {
taskInfoFor(this, taskName).inputChecksum = inputChecksum
save(this)
}
}
}
fun inputChecksumFor(taskName: String) : String? =
synchronized(BUILD_INFO_FILE) {
taskInfoFor(taskInfos(), taskName).inputChecksum
}
fun saveOutputChecksum(taskName: String, outputChecksum: String) {
synchronized(BUILD_INFO_FILE) {
with(taskInfos()) {
taskInfoFor(this, taskName).outputChecksum = outputChecksum
save(this)
}
}
}
fun outputChecksumFor(taskName: String) : String? =
synchronized(BUILD_INFO_FILE) {
taskInfoFor(taskInfos(), taskName).outputChecksum
}
/**
* @param method is assumed to return an IncrementalTaskInfo.
* @return a closure that invokes that method and decide whether to run the task or not based
* on the content of that IncrementalTaskInfo
*/
fun toIncrementalTaskClosure(shortTaskName: String, method: (Project) -> IncrementalTaskInfo,
variant: Variant): (Project) -> TaskResult {
return { project: Project ->
Kobalt.context?.variant = variant
val iti = method(project)
val taskName = project.name + ":" + shortTaskName
var upToDate = false
var taskOutputChecksum : String? = null
if (! args.forceIncremental &&
(args.noIncremental || (Kobalt.context?.internalContext?.buildFileOutOfDate as Boolean))) {
//
// If the user turned off incremental builds or if the build file was modified, always run this task
//
logIncremental(LEVEL, "Incremental builds are turned off, running $taskName")
upToDate = false
// } else if (iti.context.internalContext.previousTaskWasIncrementalSuccess(project.name)) {
// //
// // If the previous task was an incremental success, no need to run this task.
//
// Disabled for now since this can only work if exactly the previous task was
// an incremental success. If it was a regular task, then this boolean should be
// set back to false, which is currently not done.
// //
// logIncremental(LEVEL, "Previous incremental task was a success, not running $shortTaskName")
// upToDate = true
} else {
//
// First, compare the input checksums
//
inputChecksumFor(taskName)?.let { inputChecksum ->
val dependsOnDirtyProjects = project.projectExtra.dependsOnDirtyProjects(project)
if (inputChecksum == iti.inputChecksum() && !dependsOnDirtyProjects) {
//
// Input checksums are equal, compare the output checksums
//
outputChecksumFor(taskName)?.let { outputChecksum ->
taskOutputChecksum = iti.outputChecksum()
if (outputChecksum == taskOutputChecksum) {
upToDate = true
} else {
logIncremental(LEVEL, "Incremental task $taskName output is out of date" +
" (different output checksums), running it")
}
}
} else {
if (dependsOnDirtyProjects) {
logIncremental(LEVEL, "Project ${project.name} depends on dirty project, running $taskName")
} else {
logIncremental(LEVEL, "Incremental task $taskName input is out of date, running it"
+ " (different input checksums old: $inputChecksum new: ${iti.inputChecksum()})")
}
project.projectExtra.isDirty = true
}
}
}
if (!upToDate) {
//
// The task is out of date, invoke the task on the IncrementalTaskInfo object
//
val result = iti.task(project)
if (result.success) {
logIncremental(LEVEL, "Incremental task $taskName done running, saving checksums")
iti.inputChecksum()?.let {
saveInputChecksum(taskName, it)
logIncremental(LEVEL, " input checksum \"$it\" saved")
}
// Important to rerun the checksum here since the output of the task might have changed it
iti.outputChecksum()?.let {
saveOutputChecksum(taskName, it)
logIncremental(LEVEL, " output checksum \"$it\" saved")
}
}
result
} else {
//
// Identical input and output checksums, don't run the task
//
logIncremental(LEVEL, "Incremental task \"$taskName\" is up to date, not running it")
iti.context.internalContext.setIncrementalSuccess(project.name)
TaskResult()
}
}
}
val LEVEL = 2
private fun logIncremental(level: Int, s: String) = kobaltLog(level, " INC - $s")
}

View file

@ -1,152 +0,0 @@
package com.beust.kobalt.internal
import com.beust.jcommander.JCommander
import com.beust.jcommander.Parameter
import com.beust.kobalt.TestConfig
import com.beust.kobalt.api.IAffinity
import com.beust.kobalt.api.IClasspathDependency
import com.beust.kobalt.api.KobaltContext
import com.beust.kobalt.api.Project
import com.beust.kobalt.misc.KFiles
import com.beust.kobalt.misc.KobaltLogger
import com.google.inject.Inject
import org.junit.platform.engine.TestExecutionResult
import org.junit.platform.engine.discovery.DiscoverySelectors
import org.junit.platform.engine.reporting.ReportEntry
import org.junit.platform.engine.support.descriptor.MethodSource
import org.junit.platform.launcher.LauncherDiscoveryRequest
import org.junit.platform.launcher.TestExecutionListener
import org.junit.platform.launcher.TestIdentifier
import org.junit.platform.launcher.TestPlan
import org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder
import org.junit.platform.launcher.core.LauncherFactory
import java.io.File
import java.nio.file.Paths
/**
* Runner for JUnit 5 tests. This class also contains a main() entry point since JUnit 5 no longer supplies one.
*/
class JUnit5Runner @Inject constructor(kFiles: KFiles) : GenericTestRunner() {
override val dependencyName = "jupiter"
override val annotationPackage = "org.junit.jupiter.api"
override val mainClass = "com.beust.kobalt.internal.JUnit5RunnerKt"
override val runnerName = "JUnit 5"
override fun affinity(project: Project, context: KobaltContext) : Int {
val result =
if (project.testDependencies.any { it.id.contains("junit5") || it.id.contains("jupiter") })
IAffinity.DEFAULT_POSITIVE_AFFINITY + 100
else 0
return result
}
override fun args(project: Project, context: KobaltContext, classpath: List<IClasspathDependency>, testConfig: TestConfig): List<String> {
val testClassDir = KFiles.joinDir(project.buildDirectory, KFiles.TEST_CLASSES_DIR)
val classDir = KFiles.joinDir(project.buildDirectory, KFiles.CLASSES_DIR)
val args = listOf("--testClassDir", testClassDir,
"--classDir", classDir,
"--log", KobaltLogger.LOG_LEVEL.toString())
return args
}
override val extraClasspath = kFiles.kobaltJar
}
private class Args {
@Parameter(names = arrayOf("--log"))
var log: Int = 1
@Parameter(names = arrayOf("--testClassDir"))
var testClassDir: String = "kobaltBuild/test-classes"
@Parameter(names = arrayOf("--classDir"))
var classDir: String = "kobaltBuild/classes"
}
fun main(argv: Array<String>) {
val args = Args()
val jc = JCommander(args)
jc.parse(*argv)
val testClassDir = File(args.testClassDir).absolutePath
val classDir = File(args.classDir).absolutePath
val request : LauncherDiscoveryRequest = LauncherDiscoveryRequestBuilder()
.selectors(DiscoverySelectors.selectClasspathRoots(setOf(
Paths.get(testClassDir),
Paths.get(classDir)
)))
.selectors(DiscoverySelectors.selectDirectory(testClassDir))
.build()
fun testName(id: TestIdentifier) : String? {
val result =
if (id.source.isPresent) {
val source = id.source.get()
if (source is MethodSource) {
source.className + "." + source.methodName
} else {
null
}
} else {
null
}
return result
}
var passed = 0
var failed = 0
var skipped = 0
var aborted = 0
fun log(level: Int, s: String) {
if (level <= args.log) println(s)
}
val listener = object: TestExecutionListener {
override fun executionFinished(testIdentifier: TestIdentifier, testExecutionResult: TestExecutionResult) {
val testName = testName(testIdentifier)
if (testName != null) {
when(testExecutionResult.status) {
TestExecutionResult.Status.FAILED -> {
log(1, "FAILED: $testName, reason: " + testExecutionResult.throwable.get().toString())
failed++
}
TestExecutionResult.Status.ABORTED -> {
log(1, "ABORTED: $testName, reason: " + testExecutionResult.throwable.get().toString())
aborted++
}
TestExecutionResult.Status.SUCCESSFUL -> {
log(2, "PASSED: $testName")
passed++
} else -> {
}
}
}
}
override fun executionSkipped(testIdentifier: TestIdentifier, reason: String) {
testName(testIdentifier)?.let {
log(1, "Skipping $it because $reason")
skipped++
}
}
override fun executionStarted(testIdentifier: TestIdentifier) {
testName(testIdentifier)?.let {
log(2, "Starting $it")
}
}
override fun testPlanExecutionStarted(testPlan: TestPlan?) {}
override fun dynamicTestRegistered(testIdentifier: TestIdentifier?) {}
override fun reportingEntryPublished(testIdentifier: TestIdentifier?, entry: ReportEntry?) {}
override fun testPlanExecutionFinished(testPlan: TestPlan?) {}
}
LauncherFactory.create().execute(request, listener)
log(1, "TEST RESULTS: $passed PASSED, $failed FAILED, $skipped SKIPPED, $aborted ABORTED")
}

View file

@ -1,32 +0,0 @@
package com.beust.kobalt.internal
import com.beust.kobalt.TestConfig
import com.beust.kobalt.api.IClasspathDependency
import com.beust.kobalt.api.KobaltContext
import com.beust.kobalt.api.Project
import com.beust.kobalt.maven.DependencyManager
import com.google.inject.Inject
import java.lang.reflect.Modifier
import java.net.URLClassLoader
open class JUnitRunner() : GenericTestRunner() {
override val mainClass = "org.junit.runner.JUnitCore"
override val annotationPackage = "org.junit"
override val dependencyName = "junit"
override val runnerName = "JUnit 4"
override fun args(project: Project, context: KobaltContext, classpath: List<IClasspathDependency>,
testConfig: TestConfig) = findTestClasses(project, context, testConfig)
@Inject
lateinit var dependencyManager: DependencyManager
override fun filterTestClasses(project: Project, context: KobaltContext, classes: List<String>) : List<String> {
val deps = dependencyManager.testDependencies(project, context)
val cl = URLClassLoader(deps.map { it.jarFile.get().toURI().toURL() }.toTypedArray())
return classes.filter { !Modifier.isAbstract(cl.loadClass(it).modifiers) }
}
}

View file

@ -1,261 +0,0 @@
package com.beust.kobalt.internal
import com.beust.kobalt.IncrementalTaskInfo
import com.beust.kobalt.KobaltException
import com.beust.kobalt.TaskResult
import com.beust.kobalt.TestConfig
import com.beust.kobalt.api.*
import com.beust.kobalt.api.annotation.ExportedProjectProperty
import com.beust.kobalt.api.annotation.IncrementalTask
import com.beust.kobalt.api.annotation.Task
import com.beust.kobalt.maven.DependencyManager
import com.beust.kobalt.maven.Md5
import com.beust.kobalt.maven.aether.Scope
import com.beust.kobalt.misc.KFiles
import com.beust.kobalt.misc.KobaltExecutors
import com.beust.kobalt.misc.error
import com.beust.kobalt.misc.warn
import java.io.File
import java.util.*
import javax.inject.Inject
import javax.inject.Singleton
/**
* This plug-in takes care of compilation: it declares several common tasks ("compile", "compileTest")
* and picks up all the compiler contributors in order to run them whenever a compilation is requested.
*/
@Singleton
open class JvmCompilerPlugin @Inject constructor(
open val files: KFiles,
open val dependencyManager: DependencyManager,
open val executors: KobaltExecutors,
open val taskContributor : TaskContributor,
val compilerUtils: CompilerUtils)
: BasePlugin(), ISourceDirectoryContributor, IProjectContributor, ITaskContributor by taskContributor {
companion object {
val PLUGIN_NAME = "JvmCompiler"
@ExportedProjectProperty(doc = "Compiler args", type = "List<String>")
const val COMPILER_ARGS = "compilerArgs"
const val TASK_COMPILE = "compile"
const val TASK_COMPILE_TEST = "compileTest"
const val TASK_CLEAN = "clean"
const val TASK_TEST = "test"
const val DOCS_DIRECTORY = "docs/javadoc"
const val GROUP_TEST = "test"
const val GROUP_BUILD = "build"
const val GROUP_DOCUMENTATION = "documentation"
}
override val name: String = PLUGIN_NAME
override fun accept(project: Project) = true
override fun apply(project: Project, context: KobaltContext) {
super.apply(project, context)
// cleanUpActors()
taskContributor.addIncrementalVariantTasks(this, project, context, "compile", GROUP_BUILD,
runTask = { taskCompile(project) })
//
// Add each test config as a test task. If none was specified, create a default one so that
// users don't have to specify a test{}
//
if (project.testConfigs.isEmpty()) {
project.testConfigs.add(TestConfig(project, isDefault = true))
}
project.testConfigs.forEach { config ->
val taskName = if (config.name.isEmpty()) TASK_TEST else TASK_TEST + config.name
taskManager.addTask(this, project, taskName, group = GROUP_TEST,
dependsOn = listOf(JvmCompilerPlugin.TASK_COMPILE, JvmCompilerPlugin.TASK_COMPILE_TEST),
task = { taskTest(project, config.name)} )
}
}
private fun taskTest(project: Project, configName: String): TaskResult {
context.logger.log(project.name, 2, "Running tests: $configName")
val testContributor = ActorUtils.selectAffinityActor(project, context,
context.pluginInfo.testRunnerContributors)
if (testContributor != null && testContributor.affinity(project, context) > 0) {
// val td1 = dependencyManager.testDependencies(project, context)
val testDependencies = dependencyManager.calculateDependencies(project, context,
dependencyFilter = dependencyManager.createDependencyFilter(project, project.testDependencies),
scopes = listOf(Scope.TEST))
val compileDependencies = dependencyManager.calculateDependencies(project, context,
scopes = listOf(Scope.COMPILE, Scope.COMPILEONLY))
val allDependencies = (testDependencies + compileDependencies).distinct()
return testContributor.run(project, context, configName, allDependencies.toList())
} else {
context.logger.log(project.name, 2,
"Couldn't find a test runner for project ${project.name}, did you specify dependenciesTest{}?")
return TaskResult()
}
}
@Task(name = TASK_CLEAN, description = "Clean the project", group = GROUP_BUILD,
runBefore = arrayOf(JvmCompilerPlugin.TASK_COMPILE))
fun taskClean(project: Project): TaskResult {
java.io.File(project.directory, project.buildDirectory).let { dir ->
if (!dir.deleteRecursively()) {
warn("Couldn't delete $dir")
}
}
return TaskResult()
}
@IncrementalTask(name = TASK_COMPILE_TEST, description = "Compile the tests", group = GROUP_BUILD,
dependsOn = arrayOf(TASK_COMPILE))
fun taskCompileTest(project: Project): IncrementalTaskInfo {
return IncrementalTaskInfo(
inputChecksum = {
Md5.toMd5Directories(context.testSourceDirectories(project).map { File(project.directory, it.path)})
},
outputChecksum = {
Md5.toMd5Directories(listOf(KFiles.makeOutputTestDir(project)))
},
task = { project -> doTaskCompileTest(project)},
context = context
)
}
private fun sourceDirectories(project: Project, context: KobaltContext, isTest: Boolean)
= context.variant.sourceDirectories(project, context, SourceSet.of(isTest))
@IncrementalTask(name = JvmCompilerPlugin.TASK_COMPILE, description = "Compile the project", group = GROUP_BUILD,
runAfter = arrayOf(TASK_CLEAN))
fun taskCompile(project: Project): IncrementalTaskInfo {
return IncrementalTaskInfo(
inputChecksum = {
Md5.toMd5Directories(context.sourceDirectories(project).map { File(project.directory, it.path) })
},
outputChecksum = {
Md5.toMd5Directories(listOf(File(project.directory, project.classesDir(context))))
},
task = { project -> doTaskCompile(project) },
context = context
)
}
private fun doTaskCompile(project: Project) = doTaskCompile(project, isTest = false)
private fun doTaskCompileTest(project: Project) = doTaskCompile(project, isTest = true)
private fun doTaskCompile(project: Project, isTest: Boolean): TaskResult {
val results = arrayListOf<TaskResult>()
val compilerContributors = context.pluginInfo.compilerContributors
ActorUtils.selectAffinityActors(project, context, context.pluginInfo.compilerContributors)
var failedResult: TaskResult? = null
if (compilerContributors.isEmpty()) {
throw KobaltException("Couldn't find any compiler for project ${project.name}")
} else {
// Generate BuildConfig if applicable
context.variant.maybeGenerateBuildConfig(project, context)
val allCompilers = compilerContributors.flatMap { it.compilersFor(project, context)}.sorted()
/**
* Swap the Java and Kotlin compilers from the list.
*/
fun swapJavaAndKotlin(allCompilers: List<ICompilerDescription>): List<ICompilerDescription> {
val result = ArrayList(allCompilers)
var ik = -1
var ij = -1
allCompilers.withIndex().forEach { wi ->
if (wi.value.sourceSuffixes.contains("java")) ij = wi.index
if (wi.value.sourceSuffixes.contains("kt")) ik = wi.index
}
if (ik >= 0 && ij >= 0) {
Collections.swap(result, ik, ij)
}
return result
}
// If this project has a kapt{} directive, we want to run the Java compiler first
val hasKapt = project.projectProperties.get("kaptConfig") != null
val allCompilersSorted = if (hasKapt) swapJavaAndKotlin(allCompilers) else allCompilers
var done = false
// The directory where the classes get compiled
val buildDirectory =
if (isTest) File(KFiles.joinDir(project.buildDirectory, KFiles.TEST_CLASSES_DIR))
else File(KFiles.joinDir(project.classesDir(context)))
allCompilersSorted.doWhile({ ! done }) { compiler ->
val compilerResults = compilerUtils.invokeCompiler(project, context, compiler,
sourceDirectories(project, context, isTest), isTest, buildDirectory)
results.addAll(compilerResults.successResults)
if (failedResult == null) failedResult = compilerResults.failedResult
compilerResults.failedResult?.let { failedResult ->
done = true
failedResult.errorMessage?.let { errorMessage ->
error(text = errorMessage)
}
}
}
return if (failedResult != null) failedResult!!
else if (results.size > 0) results[0]
else TaskResult(true)
}
}
private val allProjects = arrayListOf<ProjectDescription>()
// IProjectContributor
override fun projects() = allProjects
override fun cleanUpActors() {
allProjects.clear()
}
fun addDependentProjects(project: Project, dependents: List<Project>) {
project.dependsOn.addAll(dependents)
with(ProjectDescription(project, dependents)) {
allProjects.add(this)
}
}
@Task(name = "doc", description = "Generate the documentation for the project", group = GROUP_DOCUMENTATION,
runBefore = arrayOf("assemble"), runAfter = arrayOf("clean"))
fun taskJavadoc(project: Project): TaskResult {
val docGenerator = ActorUtils.selectAffinityActor(project, context, context.pluginInfo.docContributors)
if (docGenerator != null) {
val buildDirectory = File(project.buildDirectory, JvmCompilerPlugin.DOCS_DIRECTORY)
val contributors =
ActorUtils.selectAffinityActors(project, context, context.pluginInfo.compilerContributors)
var result: TaskResult? = null
contributors.forEach {
it.compilersFor(project, context).forEach { compiler ->
result = docGenerator.generateDoc(project, context,
compilerUtils.createCompilerActionInfo(project, context, compiler,
isTest = false, sourceDirectories = sourceDirectories(project, context, false),
sourceSuffixes = compiler.sourceSuffixes, buildDirectory = buildDirectory))
}
}
return result!!
} else {
warn("Couldn't find any doc contributor for project ${project.name}")
return TaskResult()
}
}
// ISourceDirectoryContributor
override fun sourceDirectoriesFor(project: Project, context: KobaltContext)
= if (accept(project)) {
sourceDirectories(project, context, isTest = false)
} else {
arrayListOf()
}
open val compiler: ICompilerContributor? = null
}

View file

@ -1,278 +0,0 @@
package com.beust.kobalt.internal
import com.beust.kobalt.KobaltException
import com.beust.kobalt.api.*
import com.beust.kobalt.misc.kobaltLog
import java.io.ByteArrayInputStream
import java.io.InputStream
import javax.xml.bind.JAXBContext
import javax.xml.bind.annotation.XmlElement
import javax.xml.bind.annotation.XmlRootElement
//
// Operations related to the parsing of kobalt-plugin.xml: XML parsing, PluginInfo, etc...
//
/**
* If a plug-in didn't specify a factory, we use our own injector to instantiate all its components.
*/
class GuiceFactory : IFactory {
override fun <T> instanceOf(c: Class<T>) : T = Kobalt.INJECTOR.getInstance(c)
}
/////
// XML parsing
//
// The following classes are used by JAXB to parse the kobalt-plugin.xml file.
/**
* The root element of kobalt-plugin.xml
*/
@XmlRootElement(name = "kobalt-plugin")
class KobaltPluginXml {
@XmlElement @JvmField
var name: String? = null
@XmlElement(name = "plugin-actors") @JvmField
var pluginActors : ClassNameXml? = null
@XmlElement(name = "factory-class-name") @JvmField
var factoryClassName: String? = null
}
class ContributorXml {
@XmlElement @JvmField
val name: String? = null
}
class ClassNameXml {
@XmlElement(name = "class-name") @JvmField
var className: List<String> = arrayListOf()
}
/**
* The fields in this interface have tests.
*/
interface IPluginInfo {
val testJvmFlagContributors : List<ITestJvmFlagContributor>
val testJvmFlagInterceptors : List<ITestJvmFlagInterceptor>
}
open class BasePluginInfo : IPluginInfo {
override val testJvmFlagContributors = arrayListOf<ITestJvmFlagContributor>()
override val testJvmFlagInterceptors = arrayListOf<ITestJvmFlagInterceptor>()
}
/**
* Turn a KobaltPluginXml (the raw content of kobalt-plugin.xml mapped to POJO's) into a PluginInfo object, which
* contains all the contributors instantiated and other information that Kobalt can actually use. Kobalt code that
* needs to access plug-in info can then just inject a PluginInfo object.
*/
class PluginInfo(val xml: KobaltPluginXml, val pluginClassLoader: ClassLoader?, val classLoader: ClassLoader?)
: BasePluginInfo() {
val plugins = arrayListOf<IPlugin>()
val projectContributors = arrayListOf<IProjectContributor>()
val classpathContributors = arrayListOf<IClasspathContributor>()
val templateContributors = arrayListOf<ITemplateContributor>()
val repoContributors = arrayListOf<IRepoContributor>()
val compilerFlagContributors = arrayListOf<ICompilerFlagContributor>()
val compilerInterceptors = arrayListOf<ICompilerInterceptor>()
val sourceDirectoriesInterceptors = arrayListOf<ISourceDirectoryInterceptor>()
val buildDirectoryInterceptors = arrayListOf<IBuildDirectoryInterceptor>()
// val runnerContributors = arrayListOf<IRunnerContributor>()
val testRunnerContributors = arrayListOf<ITestRunnerContributor>()
val classpathInterceptors = arrayListOf<IClasspathInterceptor>()
val compilerContributors = arrayListOf<ICompilerContributor>()
val docContributors = arrayListOf<IDocContributor>()
val sourceDirContributors = arrayListOf<ISourceDirectoryContributor>()
val testSourceDirContributors = arrayListOf<ITestSourceDirectoryContributor>()
val buildConfigFieldContributors = arrayListOf<IBuildConfigFieldContributor>()
val taskContributors = arrayListOf<ITaskContributor>()
val assemblyContributors = arrayListOf<IAssemblyContributor>()
val incrementalAssemblyContributors = arrayListOf<IIncrementalAssemblyContributor>()
val incrementalTaskContributors = arrayListOf<IIncrementalTaskContributor>()
// Not documented yet
val buildConfigContributors = arrayListOf<IBuildConfigContributor>()
val mavenIdInterceptors = arrayListOf<IMavenIdInterceptor>()
val jvmFlagContributors = arrayListOf<IJvmFlagContributor>()
val localMavenRepoPathInterceptors = arrayListOf<ILocalMavenRepoPathInterceptor>()
val buildListeners = arrayListOf<IBuildListener>()
val buildReportContributors = arrayListOf<IBuildReportContributor>()
val docFlagContributors = arrayListOf<IDocFlagContributor>()
// Note: intentionally repeating them here even though they are defined by our base class so
// that this class always contains the full list of contributors and interceptors
override val testJvmFlagContributors = arrayListOf<ITestJvmFlagContributor>()
override val testJvmFlagInterceptors = arrayListOf<ITestJvmFlagInterceptor>()
companion object {
/**
* Where plug-ins define their plug-in actors.
*/
val PLUGIN_XML = "META-INF/kobalt-plugin.xml"
/**
* Kobalt's core XML file needs to be different from kobalt-plugin.xml because classloaders
* can put a plug-in's jar file in front of Kobalt's, which means we'll read
* that one instead of the core one.
*/
val PLUGIN_CORE_XML = "META-INF/kobalt-core-plugin.xml"
/**
* Read Kobalt's own kobalt-plugin.xml.
*/
fun readKobaltPluginXml(): PluginInfo {
// Note: use forward slash here since we're looking up this file in a .jar file
val url = Kobalt::class.java.classLoader.getResource(PLUGIN_CORE_XML)
kobaltLog(2, "URL for core kobalt-plugin.xml: $url")
if (url != null) {
return readPluginXml(url.openConnection().inputStream)
} else {
throw AssertionError("Couldn't find $PLUGIN_XML")
}
}
/**
* Read a general kobalt-plugin.xml.
*/
fun readPluginXml(ins: InputStream, pluginClassLoader: ClassLoader? = null,
classLoader: ClassLoader? = null): PluginInfo {
val jaxbContext = JAXBContext.newInstance(KobaltPluginXml::class.java)
val kobaltPlugin: KobaltPluginXml = jaxbContext.createUnmarshaller().unmarshal(ins)
as KobaltPluginXml
kobaltLog(2, "Parsed plugin XML file, found: " + kobaltPlugin.name)
val result =
try {
PluginInfo(kobaltPlugin, pluginClassLoader, classLoader)
} catch(ex: Exception) {
throw KobaltException("Couldn't create PluginInfo: " + ex.message, ex)
}
return result
}
fun readPluginXml(s: String, pluginClassLoader: ClassLoader?, scriptClassLoader: ClassLoader? = null)
= readPluginXml(ByteArrayInputStream(s.toByteArray(Charsets.UTF_8)), pluginClassLoader,
scriptClassLoader)
}
init {
val factory = if (xml.factoryClassName != null) {
Class.forName(xml.factoryClassName).newInstance() as IFactory
} else {
GuiceFactory()
}
fun forName(className: String) : Class<*> {
fun loadClass(className: String, classLoader: ClassLoader?) : Class<*>? {
try {
return classLoader?.loadClass(className)
} catch(ex: ClassNotFoundException) {
return null
}
}
val result = loadClass(className, classLoader)
?: loadClass(className, pluginClassLoader)
?: Class.forName(className)
return result
}
//
// Populate pluginInfo with what was found in Kobalt's own kobalt-plugin.xml
//
@Suppress("UNCHECKED_CAST")
xml.pluginActors?.className?.forEach {
with(factory.instanceOf(forName(it))) {
// Note: can't use "when" here since the same instance can implement multiple interfaces
if (this is IBuildConfigFieldContributor) buildConfigFieldContributors.add(this)
if (this is IBuildDirectoryInterceptor) buildDirectoryInterceptors.add(this)
if (this is IClasspathContributor) classpathContributors.add(this)
if (this is IClasspathInterceptor) classpathInterceptors.add(this)
if (this is ICompilerContributor) compilerContributors.add(this)
if (this is ICompilerFlagContributor) compilerFlagContributors.add(this)
if (this is ICompilerInterceptor) compilerInterceptors.add(this)
if (this is IDocContributor) docContributors.add(this)
if (this is ITemplateContributor) templateContributors.add(this)
if (this is IPlugin) plugins.add(this)
if (this is IProjectContributor) projectContributors.add(this)
if (this is IRepoContributor) repoContributors.add(this)
// if (this is IRunnerContributor) runnerContributors.add(this)
if (this is ISourceDirectoryContributor) sourceDirContributors.add(this)
if (this is ISourceDirectoryInterceptor) sourceDirectoriesInterceptors.add(this)
if (this is ITaskContributor) taskContributors.add(this)
if (this is ITestRunnerContributor) testRunnerContributors.add(this)
if (this is IMavenIdInterceptor) mavenIdInterceptors.add(this)
if (this is ITestSourceDirectoryContributor) testSourceDirContributors.add(this)
if (this is IBuildConfigContributor) buildConfigContributors.add(this)
if (this is IAssemblyContributor) assemblyContributors.add(this)
if (this is IIncrementalAssemblyContributor) incrementalAssemblyContributors.add(this)
if (this is IIncrementalTaskContributor) incrementalTaskContributors.add(this)
// Not documented yet
if (this is ITestJvmFlagContributor) testJvmFlagContributors.add(this)
if (this is ITestJvmFlagInterceptor) testJvmFlagInterceptors.add(this)
if (this is IJvmFlagContributor) jvmFlagContributors.add(this)
if (this is ILocalMavenRepoPathInterceptor) localMavenRepoPathInterceptors.add(this)
if (this is IBuildListener) buildListeners.add(this)
if (this is IBuildReportContributor) buildReportContributors.add(this)
if (this is IDocFlagContributor) docFlagContributors.add(this)
}
}
}
fun cleanUp() {
listOf(projectContributors, classpathContributors, templateContributors,
repoContributors, compilerFlagContributors, compilerInterceptors,
sourceDirectoriesInterceptors, buildDirectoryInterceptors,
/* runnerContributors, */ testRunnerContributors, classpathInterceptors,
compilerContributors, docContributors, sourceDirContributors,
testSourceDirContributors, buildConfigFieldContributors,
taskContributors, incrementalTaskContributors, assemblyContributors,
incrementalAssemblyContributors, testJvmFlagInterceptors,
jvmFlagContributors, localMavenRepoPathInterceptors, buildListeners,
buildReportContributors, docFlagContributors
).forEach {
it.forEach(IPluginActor::cleanUpActors)
}
}
/**
* Add the content of @param[pluginInfo] to this pluginInfo.
*/
fun addPluginInfo(pluginInfo: PluginInfo) {
kobaltLog(2, "Found new plug-in, adding it to pluginInfo: $pluginInfo")
plugins.addAll(pluginInfo.plugins)
classpathContributors.addAll(pluginInfo.classpathContributors)
projectContributors.addAll(pluginInfo.projectContributors)
templateContributors.addAll(pluginInfo.templateContributors)
repoContributors.addAll(pluginInfo.repoContributors)
compilerFlagContributors.addAll(pluginInfo.compilerFlagContributors)
compilerInterceptors.addAll(pluginInfo.compilerInterceptors)
sourceDirectoriesInterceptors.addAll(pluginInfo.sourceDirectoriesInterceptors)
buildDirectoryInterceptors.addAll(pluginInfo.buildDirectoryInterceptors)
// runnerContributors.addAll(pluginInfo.runnerContributors)
testRunnerContributors.addAll(pluginInfo.testRunnerContributors)
classpathInterceptors.addAll(pluginInfo.classpathInterceptors)
compilerContributors.addAll(pluginInfo.compilerContributors)
docContributors.addAll(pluginInfo.docContributors)
sourceDirContributors.addAll(pluginInfo.sourceDirContributors)
buildConfigFieldContributors.addAll(pluginInfo.buildConfigFieldContributors)
taskContributors.addAll(pluginInfo.taskContributors)
incrementalTaskContributors.addAll(pluginInfo.incrementalTaskContributors)
testSourceDirContributors.addAll(pluginInfo.testSourceDirContributors)
mavenIdInterceptors.addAll(pluginInfo.mavenIdInterceptors)
buildConfigContributors.addAll(pluginInfo.buildConfigContributors)
assemblyContributors.addAll(pluginInfo.assemblyContributors)
incrementalAssemblyContributors.addAll(pluginInfo.incrementalAssemblyContributors)
testJvmFlagContributors.addAll(pluginInfo.testJvmFlagContributors)
testJvmFlagInterceptors.addAll(pluginInfo.testJvmFlagInterceptors)
jvmFlagContributors.addAll(pluginInfo.jvmFlagContributors)
localMavenRepoPathInterceptors.addAll(pluginInfo.localMavenRepoPathInterceptors)
buildListeners.addAll(pluginInfo.buildListeners)
buildReportContributors.addAll(pluginInfo.buildReportContributors)
docFlagContributors.addAll(pluginInfo.docFlagContributors)
}
}

Some files were not shown because too many files have changed in this diff Show more