1
0
Fork 0
mirror of https://github.com/ethauvin/kobalt.git synced 2025-04-24 23:47:11 -07:00

First commit

This commit is contained in:
Cedric Beust 2015-10-03 21:38:15 -07:00
commit c061e7df85
102 changed files with 6717 additions and 0 deletions

10
.gitignore vendored Normal file
View file

@ -0,0 +1,10 @@
.gradle
annotations
.idea
build
buildScript
kobaltBuild
test-output
.kobalt/dist
local.properties

3
README.md Normal file
View file

@ -0,0 +1,3 @@
# Kobalt
Kobalt is a universal build system, please see [the web site](http://beust.com/kobalt/) for the full documentation.

43
TODO Normal file
View file

@ -0,0 +1,43 @@
To do:
- --dryRun
- ProjectGenerator: support migration from pom.xml (starting with dependencies)
- Make files appear in download list automatically on bintray (undocumented API)
- logs for users should not show any timestamp, class file or thread id, this should only be in --dev mode
- Project ordering: kotlinProject(wrapper) {}
- Fetch .pom with DynamicGraph
- Centralize all the executors
- Compile TestNG (including generating Version.java and OSGi headers)
- Storage API: Plugins.storage("myplugin").get/set()
- Support additional .kt files in ~/.kobalt/src
- generateArchive() should use Path instead of File
- uploadMaven
+ Create sources.jar
+ Create javadoc.jar
- Bug: --tasks displays multiple tasks when there are multiple projects
- Replace File with java.nio.Files and Path
- Create a wiki page for plugins
- Make kobaltw executable in the zip file
Done:
- --checkVersions: displays which plugins have a newer version than the one specified in the build
- Make it possible to target jar for individual projects: ./kobaltw kobalt:uploadJcenter
- --buildFile doesn't use the local .kobalt directory
- Better plugin() parsing
- kobalt-wrapper.jar contains too much stuff, should be just com.beust.kobalt.wrapper.* + kotlin runtime or maybe
just a straight Java class with minimal dependencies for fast start up
- Add repos from the build file
- Support tasks added in the build file
- Replace "removePrefixes" with an overloaded include(prefix, file)
- --init: Don't overwrite existing file
- Handle snapshots/metadata: https://repository.jboss.org/nexus/content/repositories/root_repository//commons-lang/commons-lang/2.7-SNAPSHOT/commons-lang-2.7-SNAPSHOT.jar
- JUnit
- Compiler nowarn section
- Parse plugins and repos in build files
- Stop always recompiling preBuildScript.jar
- Upload non maven files
- Jar packaging: include the jar in the jar (not supported by JarFile)
- Encapsulate BuildFile for better log messages

17
build-apt-plugin Normal file
View file

@ -0,0 +1,17 @@
jar=$HOME/t/kobalt-apt-0.3.jar
rm -f $jar
cd $HOME/kotlin/kobalt
jar cf $jar -C build/classes/main com/beust/kobalt/plugin/apt
jar uf $jar -C src/main/resources .
rm -rf /tmp/META-INF
mkdir -p /tmp/META-INF
echo > /tmp/META-INF/MANIFEST.MF Manifest-Version: 1.0
echo >>/tmp/META-INF/MANIFEST.MF "Created-By: 1.8.0_25 (Oracle Corporation)"
echo >>/tmp/META-INF/MANIFEST.MF Kobalt-Plugin-Class: com.beust.kobalt.plugin.apt.AptPlugin
(cd /tmp && jar uf $jar META-INF)

101
build.gradle Normal file
View file

@ -0,0 +1,101 @@
buildscript {
ext.kotlin_version = '0.14.449'
repositories {
mavenCentral()
jcenter()
maven {
url 'http://oss.sonatype.org/content/repositories/snapshots'
}
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${kotlin_version}"
classpath "org.jetbrains.kotlin:kotlin-stdlib:${kotlin_version}"
}
}
plugins {
id "com.jfrog.bintray" version "1.2"
}
version = '0.121'
//apply plugin: 'java'
apply plugin: 'kotlin'
//apply plugin: 'application'
apply plugin: 'com.jfrog.bintray'
apply from: 'gradle/publishing.gradle'
repositories {
mavenCentral()
jcenter()
maven {
url 'http://oss.sonatype.org/content/repositories/snapshots'
}
}
dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib:${kotlin_version}",
"org.jetbrains.kotlin:kotlin-compiler-embeddable:${kotlin_version}",
// "org.jetbrains.kotlin:kotlin-compiler:${kotlin_version}",
'com.beust:jcommander:1.48',
'com.beust:klaxon:0.16',
'com.squareup.okhttp:okhttp:2.4.0',
'org.slf4j:slf4j-api:1.7.12',
'org.slf4j:slf4j-simple:1.7.12',
'ch.qos.logback:logback-classic:1.1.2',
'org.jsoup:jsoup:1.8.2',
'com.google.inject:guice:4.0',
'com.google.inject.extensions:guice-assistedinject:4.0',
'com.google.guava:guava:18.0',
'org.apache.maven:maven-model:3.3.3',
'com.github.spullara.mustache.java:compiler:0.8.18'
// compile files("/Users/beust/.kobalt/repository/com/beust/kobalt-example-plugin/build/libs/kobalt-example-plugin.jar")
testCompile 'org.testng:testng:6.9.4'
compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
// 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
}
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
test.java.srcDirs += 'src/test/kotlin'
}
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) }
}
}

View file

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

58
gradle/publishing.gradle Normal file
View file

@ -0,0 +1,58 @@
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()
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
}
}
}

BIN
gradle/wrapper/gradle-wrapper.jar vendored Normal file

Binary file not shown.

View file

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

164
gradlew vendored Normal file
View file

@ -0,0 +1,164 @@
#!/usr/bin/env bash
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# 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"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >&-
APP_HOME="`pwd -P`"
cd "$SAVED" >&-
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
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
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
function splitJvmOpts() {
JVM_OPTS=("$@")
}
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"

90
gradlew.bat vendored Normal file
View file

@ -0,0 +1,90 @@
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@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 Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@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.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
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
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

42
kobalt.iml Normal file
View file

@ -0,0 +1,42 @@
<?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" name="Gradle: com.google.guava:guava:18.0" level="project" />
<orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:0.14.449" level="project" />
<orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-compiler-embeddable:0.14.449" level="project" />
<orderEntry type="library" name="Gradle: com.beust:jcommander:1.48" level="project" />
<orderEntry type="library" name="Gradle: com.beust:klaxon:0.16" level="project" />
<orderEntry type="library" name="Gradle: com.squareup.okhttp:okhttp:2.4.0" level="project" />
<orderEntry type="library" name="Gradle: org.slf4j:slf4j-api:1.7.12" level="project" />
<orderEntry type="library" name="Gradle: org.slf4j:slf4j-simple:1.7.12" level="project" />
<orderEntry type="library" name="Gradle: ch.qos.logback:logback-classic:1.1.2" level="project" />
<orderEntry type="library" name="Gradle: org.jsoup:jsoup:1.8.2" level="project" />
<orderEntry type="library" name="Gradle: com.google.inject:guice:4.0" level="project" />
<orderEntry type="library" name="Gradle: com.google.inject.extensions:guice-assistedinject:4.0" level="project" />
<orderEntry type="library" name="Gradle: org.apache.maven:maven-model:3.3.3" level="project" />
<orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-runtime:0.14.449" level="project" />
<orderEntry type="library" name="Gradle: com.squareup.okio:okio:1.4.0" level="project" />
<orderEntry type="library" name="Gradle: ch.qos.logback:logback-core:1.1.2" level="project" />
<orderEntry type="library" name="Gradle: javax.inject:javax.inject:1" level="project" />
<orderEntry type="library" name="Gradle: aopalliance:aopalliance:1.0" level="project" />
<orderEntry type="library" name="Gradle: org.codehaus.plexus:plexus-utils:3.0.20" level="project" />
<orderEntry type="library" scope="TEST" name="Gradle: org.testng:testng:6.9.4" level="project" />
<orderEntry type="library" scope="TEST" name="Gradle: org.beanshell:bsh:2.0b4" level="project" />
<orderEntry type="library" name="Gradle: com.github.spullara.mustache.java:compiler:0.8.18" level="project" />
</component>
</module>

153
kobalt/src/Build.kt Normal file
View file

@ -0,0 +1,153 @@
import com.beust.kobalt.*
import com.beust.kobalt.internal.test
import com.beust.kobalt.plugin.java.javaProject
import com.beust.kobalt.plugin.kotlin.kotlinProject
import com.beust.kobalt.plugin.packaging.assemble
import com.beust.kobalt.plugin.kotlin.kotlinCompiler
import com.beust.kobalt.plugin.publish.jcenter
val repos = repos("https://dl.bintray.com/cbeust/maven/")
//val plugins = plugins(
// "com.beust:kobalt-example-plugin:0.42")
//val plugins2 = plugins(
// "com.beust.kobalt:kobalt-line-count:0.2"
//// file(homeDir("kotlin/kobalt-line-count/kobaltBuild/libs/kobalt-line-count-0.1.jar"))
//// file(homeDir("kotlin/kobalt-example-plugin/kobaltBuild/libs/kobalt-example-plugin-0.17.jar"))
//)
fun readVersion() : String {
val p = java.util.Properties()
p.load(java.io.FileReader(java.io.File("src/main/resources/kobalt.properties")))
return p.getProperty("kobalt.version")
}
val wrapper = javaProject {
name = "kobalt-wrapper"
version = readVersion()
directory = homeDir("kotlin/kobalt/modules/wrapper")
}
val assembleWrapper = assemble(wrapper) {
jar {
name = wrapper.name + ".jar"
manifest {
attributes("Main-Class", "com.beust.kobalt.wrapper.Main")
}
}
}
val kobalt = kotlinProject(wrapper) {
name = "kobalt"
group = "com.beust"
artifactId = name
version = readVersion()
dependenciesTest {
// compile("junit:junit:4.12")
compile("org.testng:testng:6.9.5")
}
// sourceDirectories {
// path("src/main/kotlin")
// path("src/main/resources")
// path("src/main/java")
// }
dependencies {
compile("org.jetbrains.kotlin:kotlin-stdlib:0.14.449",
"org.jetbrains.kotlin:kotlin-compiler-embeddable:0.14.449",
// "org.jetbrains.kotlin:kotlin-compiler:0.14.449",
// file(homeDir("java/jcommander/target/jcommander-1.47.jar")),
"com.beust:jcommander:1.48",
"com.beust:klaxon:0.16",
"com.squareup.okhttp:okhttp:2.4.0",
"org.slf4j:slf4j-api:1.7.12",
"org.slf4j:slf4j-simple:1.7.12",
"ch.qos.logback:logback-classic:1.1.2",
"org.jsoup:jsoup:1.8.2",
"com.google.inject:guice:4.0",
"com.google.inject.extensions:guice-assistedinject:4.0",
"com.google.guava:guava:18.0",
"org.apache.maven:maven-model:3.3.3",
"com.github.spullara.mustache.java:compiler:0.8.18"
)
}
}
val testKobalt = test(kobalt) {
args("-log", "2", "src/test/resources/testng.xml")
}
val assembleKobalt = assemble(kobalt) {
mavenJars {
fatJar = true
manifest {
attributes("Main-Class", "com.beust.kobalt.KobaltPackage")
}
}
// jar {
// fatJar = true
// name = "${kobalt.name}-wrapper.jar"
// manifest {
// attributes("Main-Class", "com.beust.kobalt.wrapper.WrapperPackage")
// }
// }
zip {
include("kobaltw")
include(from("${kobalt.buildDirectory}/libs"), to("kobalt/wrapper"),
"${kobalt.name}-${kobalt.version}.jar")
include(from("modules/wrapper/${kobalt.buildDirectory}/libs"), to("kobalt/wrapper"),
"${kobalt.name}-wrapper.jar")
}
}
val cs = kotlinCompiler {
args("-nowarn")
}
val jc = jcenter(kobalt) {
publish = true
file("${kobalt.buildDirectory}/libs/${kobalt.name}-${kobalt.version}.zip",
"${kobalt.name}/${kobalt.version}/${kobalt.name}-${kobalt.version}.zip")
}
//val testng = javaProject {
// name = "testng"
// group = "org.testng"
// artifactId = name
// version = "6.9.6-SNAPSHOT"
// directory = homeDir("java/testng")
// buildDirectory = "kobaltBuild"
//
// sourceDirectoriesTest {
// path("src/test/java")
// path("src/test/resources")
// }
// sourceDirectories {
// path("src/main/java")
// path("src/generated/java")
// }
// dependencies {
// compile("org.apache.ant:ant:1.7.0",
// "junit:junit:4.10",
// "org.beanshell:bsh:2.0b4",
// "com.google.inject:guice:4.0:no_aop",
// "com.beust:jcommander:1.48",
// "org.yaml:snakeyaml:1.15")
// }
//}
//
//@Task(name = "generateVersionFile", description = "Generate the Version.java file", runBefore = arrayOf("compile"))
//fun createVersionFile(project: Project) : com.beust.kobalt.internal.TaskResult {
// val dirFrom = testng.directory + "/src/main/resources/org/testng/internal/"
// val dirTo = testng.directory + "/src/generated/java/org/testng/internal/"
// println("COPYING VERSION FILE")
// Files.copy(Paths.get(dirFrom + "VersionTemplateJava"), Paths.get(dirTo + "Version.java"),
// StandardCopyOption.REPLACE_EXISTING)
// return com.beust.kobalt.internal.TaskResult()
//}
//

Binary file not shown.

View file

@ -0,0 +1 @@
kobalt.version=0.144

1
kobaltw Normal file
View file

@ -0,0 +1 @@
java -jar kobalt/wrapper/kobalt-wrapper.jar $*

3
kobaltw-windows Normal file
View file

@ -0,0 +1,3 @@
jar=`ls -ltr $HOME/kotlin/kobalt/build/libs/*jar|grep -v sources|awk '{print $9}'`
java -jar $jar $*

View file

@ -0,0 +1,260 @@
package com.beust.kobalt.wrapper;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Properties;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
public class Main {
public static void main(String[] argv) throws IOException, InterruptedException {
new Main().installAndLaunchMain(argv);
}
private static final String KOBALT_PROPERTIES = "kobalt.properties";
private static final String KOBALTW = "kobaltw";
private static final String KOBALT_WRAPPER_PROPERTIES = "kobalt-wrapper.properties";
private static final String PROPERTY_VERSION = "kobalt.version";
private static final String URL = "https://dl.bintray.com/cbeust/generic/";
private static final String FILE_NAME = "kobalt";
private static final String DISTRIBUTIONS_DIR =
System.getProperty("user.home") + "/.kobalt/wrapper/dist";
private final Properties properties = new Properties();
private static int logLevel = 1;
private void installAndLaunchMain(String[] argv) throws IOException, InterruptedException {
for (int i = 0; i < argv.length; i++) {
switch(argv[i]) {
case "--log":
logLevel = Integer.parseInt(argv[i + 1]);
i++;
break;
}
}
Path kobaltJarFile = installJarFile();
launchMain(kobaltJarFile, argv);
}
private void readProperties(Properties properties, InputStream ins) throws IOException {
properties.load(ins);
ins.close();
}
private Properties maybeCreateProperties() throws IOException {
Properties result = new Properties();
URL url = getClass().getClassLoader().getResource(KOBALT_PROPERTIES);
if (url != null) {
readProperties(result, url.openConnection().getInputStream());
} else {
throw new IllegalArgumentException("Couldn't find " + KOBALT_PROPERTIES);
}
return result;
}
private File getWrapperDir() {
return new File("kobalt", "wrapper");
}
private void initWrapperFile(String version) throws IOException {
File config = new File(getWrapperDir(), KOBALT_WRAPPER_PROPERTIES);
if (! config.exists()) {
saveFile(config, PROPERTY_VERSION + "=" + version);
}
properties.load(new FileReader(config));
}
private String getWrapperVersion() {
return properties.getProperty(PROPERTY_VERSION);
}
private boolean isWindows() {
return System.getProperty("os.name").contains("Windows");
}
private Path installJarFile() throws IOException {
Properties properties = maybeCreateProperties();
String version = properties.getProperty(PROPERTY_VERSION);
initWrapperFile(version);
log(2, "Wrapper version: " + getWrapperVersion());
String fileName = FILE_NAME + "-" + getWrapperVersion() + ".zip";
new File(DISTRIBUTIONS_DIR).mkdirs();
Path localZipFile = Paths.get(DISTRIBUTIONS_DIR, fileName);
String zipOutputDir = DISTRIBUTIONS_DIR + "/" + getWrapperVersion();
Path kobaltJarFile = Paths.get(zipOutputDir,
getWrapperDir().getPath() + "/" + FILE_NAME + "-" + getWrapperVersion() + ".jar");
if (! Files.exists(localZipFile) || ! Files.exists(kobaltJarFile)) {
if (!Files.exists(localZipFile)) {
String fullUrl = URL + "/" + fileName;
download(fullUrl, localZipFile.toFile());
if (!Files.exists(localZipFile)) {
log(2, localZipFile + " downloaded, extracting it");
} else {
log(2, localZipFile + " already exists, extracting it");
}
}
//
// Extract all the zip files
//
ZipFile zipFile = new ZipFile(localZipFile.toFile());
Enumeration<? extends ZipEntry> entries = zipFile.entries();
File outputDirectory = new File(DISTRIBUTIONS_DIR);
outputDirectory.mkdirs();
while (entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();
File entryFile = new File(entry.getName());
if (entry.isDirectory()) {
entryFile.mkdirs();
} else {
Path dest = Paths.get(zipOutputDir, entryFile.getPath());
log(2, " Writing " + entry.getName() + " to " + dest);
Files.createDirectories(dest.getParent());
Files.copy(zipFile.getInputStream(entry), dest, StandardCopyOption.REPLACE_EXISTING);
}
}
}
//
// Copy the wrapper files in the current kobalt/wrapper directory
//
log(2, "Copying the wrapper files");
for (String file : FILES) {
Path from = Paths.get(zipOutputDir, file);
Path to = Paths.get(new File(".").getAbsolutePath(), file);
try {
if (isWindows() && to.toFile().exists()) {
log(1, "Windows detected, not overwriting " + to);
} else {
Files.copy(from, to, StandardCopyOption.REPLACE_EXISTING);
}
} catch(IOException ex) {
log(1, "Couldn't copy " + from + " to " + to + ": " + ex.getMessage());
}
}
new File(KOBALTW).setExecutable(true);
return kobaltJarFile;
}
private static final String[] FILES = new String[] { KOBALTW, "kobalt/wrapper/" + FILE_NAME + "-wrapper.jar" };
private void download(String fileUrl, File file) throws IOException {
URL url = new URL(fileUrl);
HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();
int responseCode = httpConn.getResponseCode();
// always check HTTP response code first
if (responseCode == HttpURLConnection.HTTP_OK) {
String fileName = "";
String disposition = httpConn.getHeaderField("Content-Disposition");
String contentType = httpConn.getContentType();
int contentLength = httpConn.getContentLength();
if (disposition != null) {
// extracts file name from header field
int index = disposition.indexOf("filename=");
if (index > 0) {
fileName = disposition.substring(index + 10,
disposition.length() - 1);
}
} else {
// extracts file name from URL
fileName = fileUrl.substring(fileUrl.lastIndexOf("/") + 1,
fileUrl.length());
}
log(2, "Content-Type = " + contentType);
log(2, "Content-Disposition = " + disposition);
log(2, "Content-Length = " + contentLength);
log(2, "fileName = " + fileName);
// opens input stream from the HTTP connection
InputStream inputStream = httpConn.getInputStream();
// opens an output stream to save into file
FileOutputStream outputStream = new FileOutputStream(file);
int bytesRead = -1;
long bytesSoFar = 0;
byte[] buffer = new byte[100_000];
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
bytesSoFar += bytesRead;
if (bytesRead > 0) {
if (contentLength > 0) {
float percent = bytesSoFar * 100 / contentLength;
log2(1, "\rDownloading " + url + " " + percent + "%");
} else {
log2(1, ".");
}
}
}
log2(1, "\n");
outputStream.close();
inputStream.close();
log(1, "Downloaded " + fileUrl);
} else {
error("No file to download. Server replied HTTP code: " + responseCode);
}
httpConn.disconnect();
}
private void saveFile(File file, String text) throws IOException {
file.getAbsoluteFile().getParentFile().mkdirs();
file.delete();
log(2, "Wrote " + file);
Files.write(Paths.get(file.toURI()), text.getBytes());
}
private static void log2(int level, String s) {
p(level, s, false);
}
private static void log(int level, String s) {
p(level, "[Wrapper] " + s, true);
}
private static void p(int level, String s, boolean newLine) {
if (level <= logLevel) {
if (newLine) System.out.println(s);
else System.out.print(s);
}
}
private void error(String s) {
System.out.println("[Wrapper error] *** " + s);
}
private static final String KOBALT_MAIN_CLASS = "com.beust.kobalt.KobaltPackage";
private void launchMain(Path kobaltJarFile, String[] argv) throws IOException, InterruptedException {
List<String> args = new ArrayList<>();
args.add("java");
args.add("-jar");
args.add(kobaltJarFile.toFile().getAbsolutePath());
for (String arg : argv) {
args.add(arg);
}
ProcessBuilder pb = new ProcessBuilder(args);
pb.inheritIO();
log(2, "Launching " + args);
Process process = pb.start();
process.waitFor();
}
}

View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

View file

@ -0,0 +1,25 @@
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? = null
@Parameter(names = arrayOf("--tasks"), description = "Display the tasks available for this build")
var tasks: Boolean = false
@Parameter(names = arrayOf("--log"), description = "Define the log level (1-3)")
var log: Int = 1
@Parameter(names = arrayOf("-i", "--init"), description = "Create a new build file based on the current project")
var init: Boolean = false
@Parameter(names = arrayOf("--checkVersions"), description = "Check if there are any newer versions of the " +
"dependencies")
var checkVersions = false
}

View file

@ -0,0 +1,25 @@
package com.beust.kobalt
import java.util.*
class Banner {
companion object {
val BANNERS = arrayOf(
" __ __ __ __ __ \n" +
" / //_/ ____ / /_ ____ _ / / / /_\n" +
" / ,< / __ \\ / __ \\ / __ `/ / / / __/\n" +
" / /| | / /_/ / / /_/ // /_/ / / / / /_ \n" +
" /_/ |_| \\____/ /_.___/ \\__,_/ /_/ \\__/ ",
" _ __ _ _ _ \n" +
" | |/ / ___ | |__ __ _ | | | |_ \n" +
" | ' / / _ \\ | '_ \\ / _` | | | | __|\n" +
" | . \\ | (_) | | |_) | | (_| | | | | |_ \n" +
" |_|\\_\\ \\___/ |_.__/ \\__,_| |_| \\__| "
)
fun get() = BANNERS.get(Random().nextInt(BANNERS.size()))
}
}

View file

@ -0,0 +1,13 @@
package com.beust.kobalt
import com.beust.kobalt.api.Plugin
import com.beust.kobalt.api.PluginTask
import com.beust.kobalt.api.Project
public abstract class BasePluginTask(override val plugin: Plugin,
override val name: String,
override val doc: String,
override val project: Project)
: PluginTask {
override val dependsOn = arrayListOf<String>()
}

View file

@ -0,0 +1,40 @@
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.maven.DepFactory
import com.beust.kobalt.maven.IClasspathDependency
import com.beust.kobalt.misc.KobaltExecutors
import com.beust.kobalt.plugin.java.SystemProperties
import java.io.File
@Directive
fun homeDir(vararg dirs: String) : String = SystemProperties.homeDir +
File.separator + dirs.toArrayList().join(File.separator)
@Directive
fun file(file: String) : String = IClasspathDependency.PREFIX_FILE + file
@Directive
fun plugins(vararg dependency : IClasspathDependency) {
Plugins.dynamicPlugins.addAll(dependency)
}
@Directive
fun plugins(vararg dependencies : String) {
val executor = INJECTOR.getInstance(KobaltExecutors::class.java)
.newExecutor("BuildScript", 5)
val factory = INJECTOR.getInstance(DepFactory::class.java)
dependencies.forEach {
Plugins.dynamicPlugins.add(factory.create(it, executor))
}
}
@Directive
fun repos(vararg repos : String) {
repos.forEach { Kobalt.addRepo(it) }
}
@Directive
fun glob(g: String) : IFileSpec.Glob = IFileSpec.Glob(g)

View file

@ -0,0 +1,39 @@
package com.beust.kobalt
import com.beust.kobalt.api.Project
import com.beust.kobalt.misc.KobaltLogger
import java.io.File
import java.nio.file.*
import java.nio.file.attribute.BasicFileAttributes
sealed class IFileSpec {
abstract fun toFiles(directory: String): List<File>
class FileSpec(val spec: String) : IFileSpec() {
override public fun toFiles(directory: String) = arrayListOf(File(spec))
override public fun toString() = spec
}
class Glob(val spec: String) : IFileSpec(), KobaltLogger {
override public fun toFiles(directory: String): List<File> {
val result = arrayListOf<File>()
val matcher = FileSystems.getDefault().getPathMatcher("glob:${spec}")
Files.walkFileTree(Paths.get(directory), object : SimpleFileVisitor<Path>() {
override public fun visitFile(path: Path, attrs: BasicFileAttributes): FileVisitResult {
val rel = Paths.get(directory).relativize(path)
if (matcher.matches(rel)) {
log(3, "Adding ${rel.toFile()}")
result.add(rel.toFile())
}
return FileVisitResult.CONTINUE
}
})
return result
}
override public fun toString() = spec
}
}

View file

@ -0,0 +1,180 @@
package com.beust.kobalt
import com.beust.jcommander.JCommander
import com.beust.kobalt.api.Kobalt
import com.beust.kobalt.api.KobaltContext
import com.beust.kobalt.internal.*
import com.beust.kobalt.kotlin.BuildFile
import com.beust.kobalt.kotlin.ScriptCompiler
import com.beust.kobalt.maven.*
import com.beust.kobalt.misc.*
import com.beust.kobalt.plugin.java.SystemProperties
import com.beust.kobalt.plugin.publish.JCenterApi
import com.beust.kobalt.plugin.publish.UnauthenticatedJCenterApi
import com.beust.kobalt.wrapper.Wrapper
import com.google.inject.Guice
import java.io.File
import java.nio.file.Paths
import java.util.*
import javax.inject.Inject
val INJECTOR = Guice.createInjector(MainModule())
public fun main(argv: Array<String>) {
INJECTOR.getInstance(Main::class.java).run(argv)
}
private class Main @Inject constructor(
val scriptCompilerFactory: ScriptCompiler.IFactory,
val plugins: Plugins,
val taskManager: TaskManager,
val http: Http,
val files: KFiles,
val executors: KobaltExecutors,
val localRepo: LocalRepo,
val depFactory: DepFactory,
val checkVersions: CheckVersions,
val jcenter: UnauthenticatedJCenterApi)
: KobaltLogger {
data class RunInfo(val jc: JCommander, val args: Args)
public fun run(argv: Array<String>) {
// Check for new version
// Commented out until I can find a way to get the latest available download
// from bintray. Right now, it always returns all the versions uploaded, not
// just the one I mark
// val p = jcenter.kobaltPackage
// val current = Versions.toLongVersion(Kobalt.version)
// val remote = Versions.toLongVersion(p.latestPublishedVersion)
// if (remote > current) {
// log(1, "*****")
// log(1, "***** New Kobalt version available: ${p.latestPublishedVersion}")
// log(1, "*****")
// }
benchmark("Build", {
println(Banner.get() + Kobalt.version + "\n")
// runTest()
val (jc, args) = parseArgs(argv)
runWithArgs(jc, args)
executors.shutdown()
debug("All done")
})
}
public class Worker<T>(val runNodes: ArrayList<T>, val n: T) : IWorker<T>, KobaltLogger {
override val priority = 0
override fun call() : TaskResult2<T> {
log(2, "Running node ${n}")
runNodes.add(n)
return TaskResult2(n != 3, n)
}
}
private fun runTest() {
val dg = Topological<String>()
dg.addEdge("b1", "a1")
dg.addEdge("b1", "a2")
dg.addEdge("b2", "a1")
dg.addEdge("b2", "a2")
dg.addEdge("c1", "b1")
dg.addEdge("c1", "b2")
val sorted = dg.sort(arrayListOf("a1", "a2", "b1", "b2", "c1", "x", "y"))
println("Sorted: ${sorted}")
}
private fun parseArgs(argv: Array<String>): RunInfo {
val args = Args()
val result = JCommander(args)
result.parse(*argv)
KobaltLogger.LOG_LEVEL = args.log
return RunInfo(result, args)
}
private val SCRIPT_JAR = "buildScript.jar"
private fun runWithArgs(jc: JCommander, args: Args) {
val p = if (args.buildFile != null) File(args.buildFile) else findBuildFile()
args.buildFile = p.absolutePath
val buildFile = BuildFile(Paths.get(p.absolutePath), p.name)
if (args.init) {
//
// --init: create a new build project and install the wrapper
//
Wrapper().install()
ProjectGenerator().run(args)
} else {
if (! buildFile.exists()) {
jc.usage()
} else {
// Install all the plugins found
plugins.installDynamicPlugins(arrayListOf(buildFile))
// Compile the build script
val output = scriptCompilerFactory.create(plugins.pluginJarFiles,
{ n: String, j: File? ->
plugins.instantiateClassName(n, j)
})
.compile(buildFile, buildFile.lastModified(), KFiles.findBuildScriptLocation(buildFile, SCRIPT_JAR))
//
// Force each project.directory to be an absolute path, if it's not already
//
output.projects.forEach {
val fd = File(it.directory)
if (! fd.isAbsolute) {
it.directory =
if (args.buildFile != null) {
KFiles.findDotDir(File(args.buildFile)).parentFile.absolutePath
} else {
fd.absolutePath
}
}
}
plugins.applyPlugins(KobaltContext(args), output.projects)
if (args.tasks) {
//
// List of tasks
//
val sb = StringBuffer("List of tasks\n")
Plugins.plugins.forEach { plugin ->
if (plugin.tasks.size() > 0) {
sb.append("\n ===== ${plugin.name} =====\n")
plugin.tasks.forEach { task ->
sb.append(" ${task.name}\t\t${task.doc}\n")
}
}
}
println(sb.toString())
} else if (args.checkVersions) {
checkVersions.run(output.projects)
} else {
//
// Launch the build
//
taskManager.runTargets(args.targets, output.projects)
}
}
}
}
private fun findBuildFile(): File {
val files = arrayListOf("Build.kt", "build.kobalt", KFiles.src("build.kobalt"),
KFiles.src("Build.kt"))
try {
return files.map {
File(SystemProperties.currentDir, it)
}.first {
it.exists()
}
} catch(ex: NoSuchElementException) {
return File("Build.kt")
}
}
}

View file

@ -0,0 +1,319 @@
package com.beust.kobalt
import com.beust.kobalt.api.*
import com.beust.kobalt.api.annotation.Task
import com.beust.kobalt.internal.TaskManager
import com.beust.kobalt.internal.TaskResult
import com.beust.kobalt.kotlin.BuildFile
import com.beust.kobalt.kotlin.ScriptCompiler
import com.beust.kobalt.maven.DepFactory
import com.beust.kobalt.maven.IClasspathDependency
import com.beust.kobalt.maven.KobaltException
import com.beust.kobalt.maven.LocalRepo
import com.beust.kobalt.misc.KFiles
import com.beust.kobalt.misc.KobaltExecutors
import com.beust.kobalt.misc.KobaltLogger
import com.beust.kobalt.misc.countChar
import com.beust.kobalt.plugin.DefaultPlugin
import com.beust.kobalt.plugin.java.JavaPlugin
import com.beust.kobalt.plugin.kotlin.KotlinPlugin
import com.beust.kobalt.plugin.packaging.PackagingPlugin
import com.beust.kobalt.plugin.publish.PublishPlugin
import com.google.inject.Provider
import java.io.File
import java.io.FileInputStream
import java.lang.reflect.Method
import java.lang.reflect.Modifier
import java.net.URL
import java.net.URLClassLoader
import java.nio.charset.Charset
import java.nio.file.Paths
import java.util.ArrayList
import java.util.HashMap
import java.util.jar.JarInputStream
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
public class Plugins @Inject constructor (val taskManagerProvider : Provider<TaskManager>,
val files: KFiles,
val depFactory: DepFactory,
val localRepo: LocalRepo,
val executors: KobaltExecutors,
val scriptCompilerFactory: ScriptCompiler.IFactory): KobaltLogger {
companion object {
public val MANIFEST_PLUGIN_CLASS : String = "Kobalt-Plugin-Class"
private var pluginMap = hashMapOf<String, Plugin>()
private var storageMap = HashMap<String, HashMap<String, Any>>()
fun storeValue(pluginName: String, key: String, value: Any) {
var values = storageMap.get(pluginName)
if (values == null) {
values = hashMapOf<String, Any>()
storageMap.put(pluginName, values)
}
values.put(key, value)
}
fun getValue(pluginName: String, key: String) : Any? {
return storageMap.get(pluginName)?.get(key)
}
val defaultPlugin : Plugin get() = getPlugin(DefaultPlugin.NAME)!!
fun addPlugin(pluginClass : Class<out Plugin>) {
addPluginInstance(INJECTOR.getInstance(pluginClass))
}
private fun addPluginInstance(plugin: Plugin) {
pluginMap.put(plugin.name, plugin)
}
init {
arrayListOf<Class<out Plugin>>(
DefaultPlugin::class.java,
JavaPlugin::class.java,
KotlinPlugin::class.java,
PackagingPlugin::class.java,
PublishPlugin::class.java
// AptPlugin::class.java
).map {
addPluginInstance(INJECTOR.getInstance(it))
}
}
public fun getPlugin(name: String) : Plugin? = pluginMap.get(name)
public val plugins : List<Plugin>
get() = ArrayList(pluginMap.values())
/**
* The list of plugins found in the build file.
*/
public val dynamicPlugins : ArrayList<IClasspathDependency> = arrayListOf()
}
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
}
log(2, "Applying plug-in \"${plugin.name}\"")
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.equals(Any::class.java))) {
currentClass.declaredMethods.map {
Pair(it, it.getAnnotation(Task::class.java))
}.filter {
it.second != null
}.filter {
isValidTaskMethod(it.first)
}.forEach {
if (Modifier.isPrivate(it.first.modifiers)) {
throw KobaltException("A task method cannot be private: ${it.first}")
}
val annotation = it.second
log(3, "Adding MethodTask from @Task: ${it.first} $annotation")
plugin.methodTasks.add(Plugin.MethodTask(it.first, annotation))
}
currentClass = currentClass.superclass
}
// Now plugin.methodTasks contains both tasks from the build file and the plug-ins, we
// can create the whole set of tasks and set up their dependencies
plugin.methodTasks.forEach { methodTask ->
val method = methodTask.method
val annotation = methodTask.taskAnnotation
fun toTask(m: Method, project: Project, plugin: Plugin): (Project) -> TaskResult {
val result: (Project) -> TaskResult = {
m.invoke(plugin, project) as TaskResult
}
return result
}
projects.filter { plugin.accept(it) }.forEach { project ->
plugin.addTask(annotation, project, toTask(method, project, plugin))
annotation.runBefore.forEach { plugin.dependsOn(it, annotation.name) }
annotation.runAfter.forEach { plugin.dependsOn(annotation.name, it) }
plugin.apply(project, context)
}
}
}
}
/**
* 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.parameterCount != 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
}
/**
* Jar files for all the plugins.
*/
public val pluginJarFiles : ArrayList<String> = arrayListOf()
val dependencies = arrayListOf<IClasspathDependency>()
/**
* Parse the build files, locate all the plugins, download them and make them available to be
* used on the classpath of the build file.
*/
fun installDynamicPlugins(files: List<BuildFile>) {
//
// Extract all the plugin() and repos() code into a separate script (pluginCode)
//
files.forEach {
val pluginCode = arrayListOf<String>()
var parenCount = 0
it.path.toFile().forEachLine(Charset.defaultCharset()) { line ->
if (line.startsWith("import")) {
pluginCode.add(line)
}
var index = line.indexOf("plugins(")
if (index == -1) index = line.indexOf("repos(")
if (parenCount > 0 || index >= 0) {
if (index == -1) index = 0
with(line.substring(index)) {
parenCount += line countChar '('
if (parenCount > 0) {
pluginCode.add(line)
}
parenCount -= line countChar ')'
}
}
}
//
// Compile and run pluginCode, which contains all the plugins() calls extracted. This
// will add all the dynamic plugins found in this code to Plugins.dynamicPlugins
//
val pluginSourceFile = KFiles.createTempFile(".kt")
pluginSourceFile.writeText(pluginCode.join("\n"), Charset.defaultCharset())
log(2, "Saved ${pluginSourceFile.absolutePath}")
scriptCompilerFactory.create(pluginJarFiles,
{ n: String, j: File? -> instantiateClassName(n, j)
}).compile(BuildFile(Paths.get(pluginSourceFile.absolutePath), "Plugins"),
it.lastModified(),
KFiles.findBuildScriptLocation(it, "preBuildScript.jar"))
}
//
// Locate all the jar files for the dynamic plugins we just discovered
//
dependencies.addAll(dynamicPlugins.map {
pluginJarFiles.add(it.jarFile.get().absolutePath)
it
})
//
// Materialize all the jar files, instantiate their plugin main class and add it to Plugins
//
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).
depFactory.create(it.id, executor)
//
// Inspect the jar, open the manifest, instantiate the main class and add it to the plugin repo
//
var fis: FileInputStream? = null
var jis: JarInputStream? = null
try {
fis = FileInputStream(it.jarFile.get())
jis = JarInputStream(fis)
val manifest = jis.getManifest()
val mainClass = manifest.getMainAttributes().getValue(Plugins.MANIFEST_PLUGIN_CLASS) ?:
throw KobaltException("Couldn't find \"${Plugins.MANIFEST_PLUGIN_CLASS}\" in the " +
"manifest of ${it}")
val pluginClassName = mainClass.removeSuffix(" ")
val c = instantiateClassName(pluginClassName)
@Suppress("UNCHECKED_CAST")
Plugins.addPlugin(c as Class<BasePlugin>)
log(1, "Added plugin ${c}")
} finally {
jis?.close()
fis?.close()
}
}
executor.shutdown()
}
public fun instantiateClassName(className : String, buildScriptJarFile: File? = null) : Class<*> {
// fun jarToUrl(jarAbsolutePath: String) = URL("file://" + jarAbsolutePath)
fun jarToUrl(path: String) = URL("jar", "", "file:${path}!/")
// We need the jar files to be first in the url list otherwise the Build.kt files resolved
// might be Kobalt's own
val urls = arrayListOf<URL>()
buildScriptJarFile?.let {
urls.add(jarToUrl(it.absolutePath))
}
urls.add(jarToUrl(files.kobaltJar))
urls.addAll(pluginJarFiles.map { jarToUrl(it) })
val classLoader = URLClassLoader(urls.toArray(arrayOfNulls<URL>(urls.size())))
try {
log(2, "Instantiating ${className}")
return classLoader.loadClass(className)
} catch(ex: Exception) {
throw KobaltException("Couldn't instantiate ${className}: ${ex}")
}
}
val allTasks : List<PluginTask>
get() {
val result = arrayListOf<PluginTask>()
Plugins.plugins.forEach { plugin ->
result.addAll(plugin.tasks)
}
return result
}
/**
* @return the tasks accepted by at least one project
*/
fun findTasks(task: String): List<PluginTask> {
val tasks = allTasks.filter { task == it.name }
if (tasks.isEmpty()) {
throw KobaltException("Couldn't find task ${task}")
} else {
return tasks
}
}
}

View file

@ -0,0 +1,95 @@
package com.beust.kobalt
import com.beust.kobalt.api.ICompilerInfo
import com.beust.kobalt.api.Kobalt
import com.beust.kobalt.maven.Pom
import com.beust.kobalt.maven.Pom.Dependency
import com.beust.kobalt.misc.KFiles
import com.beust.kobalt.misc.KobaltLogger
import com.github.mustachejava.DefaultMustacheFactory
import java.io.BufferedReader
import java.io.File
import java.io.InputStreamReader
import java.io.PrintWriter
import java.io.StringWriter
import java.util.ArrayList
import java.util.Collections
import java.util.HashMap
/**
* Generate a new project.
*/
public class ProjectGenerator : KobaltLogger {
fun run(args: Args) {
if (File(args.buildFile).exists()) {
log(1, "Build file ${args.buildFile} already exists, not overwriting it")
return
}
val compilerInfos = detect(File("."))
if (compilerInfos.size() > 1) {
log(1, "Multi language project detected, not supported yet")
}
val map = hashMapOf<String, Any?>()
map.put("directive", if (compilerInfos.isEmpty()) "project" else compilerInfos.get(0).directive)
if (compilerInfos.size() > 0) {
compilerInfos.get(0).let {
val currentDir = File(".").absoluteFile.parentFile
map.put("name", currentDir.name)
map.put("group", "com.example")
map.put("version", "0.1")
map.put("directory", currentDir.absolutePath)
map.put("sourceDirectories", it.defaultSourceDirectories)
map.put("sourceDirectoriesTest", it.defaultTestDirectories)
map.put("imports", "import com.beust.kobalt.plugin.${it.name}.*")
map.put("directive", it.name + "Project")
}
}
var mainDeps = arrayListOf<Dependency>()
var testDeps = arrayListOf<Dependency>()
map.put("mainDependencies", mainDeps)
map.put("testDependencies", testDeps)
if(File("pom.xml").exists()) {
importPom(mainDeps, map, testDeps)
}
val fileInputStream = javaClass.classLoader.getResource("build-template.mustache").openStream()
val sw = StringWriter()
val pw = PrintWriter(sw)
var mf = DefaultMustacheFactory();
var mustache = mf.compile(InputStreamReader(fileInputStream), "kobalt");
mustache.execute(pw, map).flush();
KFiles.saveFile(File(args.buildFile), sw.toString())
}
private fun importPom(mainDeps: ArrayList<Dependency>, map: HashMap<String, Any?>, testDeps: ArrayList<Dependency>) {
var pom = Pom("imported", File("pom.xml"))
map.put("group", pom.groupId ?: "com.example")
map.put("artifactId", pom.artifactId ?: "com.example")
map.put("version", pom.version ?: "0.1")
map.put("name", pom.name ?: pom.artifactId)
val partition = pom.dependencies.groupBy { it.scope }
// .filter { it.key == null }
.flatMap { it.value }
.sortedBy { it.groupId + ":" + it.artifactId }
.partition { it.scope != "test" }
mainDeps.addAll(partition.first)
testDeps.addAll(partition.second)
}
/**
* Detect all the languages contained in this project.
*/
private fun detect(dir: File) : List<ICompilerInfo> {
val result = arrayListOf<Pair<ICompilerInfo, List<File>>>()
Kobalt.compilers.forEach {
val managedFiles = it.findManagedFiles(dir)
if (managedFiles.size() > 0) {
result.add(Pair(it, managedFiles))
}
}
Collections.sort(result, { p1, p2 -> p1.second.size().compareTo(p2.second.size()) })
return result.map { it.first }
}
}

View file

@ -0,0 +1,20 @@
package com.beust.kobalt
import java.io.BufferedReader
import java.io.PrintWriter
import java.io.Reader
public class Template(val reader: Reader, val writer: PrintWriter, val map: Map<String, Any>) {
public fun execute() {
BufferedReader(reader).let {
it.forEachLine { line ->
var replacedLine = line
map.keySet().forEach { key ->
replacedLine = replacedLine.replace("{{${key}}}", map.get(key).toString(), false)
}
writer.append(replacedLine).append("\n")
}
}
}
}

View file

@ -0,0 +1,20 @@
package com.beust.kobalt.api
import com.beust.kobalt.BasePluginTask
import com.beust.kobalt.Plugins
import com.beust.kobalt.internal.TaskManager
import com.beust.kobalt.internal.TaskResult
import java.util.ArrayList
import java.util.concurrent.Callable
import kotlin.properties.Delegates
abstract public class BasePlugin : Plugin {
override val tasks: ArrayList<PluginTask> = arrayListOf()
override var taskManager : TaskManager by Delegates.notNull()
override var methodTasks = arrayListOf<Plugin.MethodTask>()
override fun accept(project: Project) = true
var plugins : Plugins by Delegates.notNull()
}

View file

@ -0,0 +1,93 @@
package com.beust.kobalt.api
import com.beust.kobalt.misc.Topological
import com.google.common.collect.ArrayListMultimap
import java.io.File
import java.io.InputStream
import java.nio.file.Files
import java.nio.file.Paths
import java.util.*
public interface ICompilerInfo {
/** Used to detect what kind of language this project is */
fun findManagedFiles(dir: File) : List<File>
/** Used to generate the imports */
val name: String
/** Used to generate the imports */
val directive: String
val defaultSourceDirectories : ArrayList<String>
val defaultTestDirectories : ArrayList<String>
}
public class Kobalt {
companion object {
public val compilers : ArrayList<ICompilerInfo> = arrayListOf()
public fun registerCompiler(c: ICompilerInfo) {
compilers.add(c)
}
private val DEFAULT_REPOS = arrayListOf(
"http://repo1.maven.org/maven2/",
"https://repository.jboss.org/nexus/content/repositories/root_repository/",
"https://jcenter.bintray.com/"
)
val repos = ArrayList<String>(DEFAULT_REPOS)
fun addRepo(repo: String) = repos.add(repo)
private val PROPERTY_KOBALT_VERSION = "kobalt.version"
private val KOBALT_PROPERTIES = "kobalt.properties"
private val LOCAL_PROPERTIES = "local.properties"
private val properties : Properties
get() = readProperties()
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 IllegalArgumentException("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).let {
readProperties(result, it)
}
}
}
return result
}
private fun readProperties(properties: Properties, ins: InputStream) {
properties.load(ins)
ins.close()
properties.forEach { es -> System.setProperty(es.getKey().toString(), es.getValue().toString()) }
}
val version = properties.getProperty(PROPERTY_KOBALT_VERSION)
val topological = Topological<Project>()
fun declareProjectDependencies(project: Project, projects: Array<out Project>) {
topological.addEdge(project, projects)
}
/**
* @return the projects sorted topologically.
*/
fun sortProjects(allProjects: ArrayList<Project>) : List<Project>
= topological.sort(allProjects)
}
}

View file

@ -0,0 +1,6 @@
package com.beust.kobalt.api
import com.beust.kobalt.Args
public class KobaltContext(val args: Args)

View file

@ -0,0 +1,34 @@
package com.beust.kobalt.api
import com.beust.kobalt.BasePluginTask
import com.beust.kobalt.api.annotation.Task
import com.beust.kobalt.internal.TaskManager
import com.beust.kobalt.internal.TaskResult
import com.beust.kobalt.internal.TaskResult2
import java.lang.reflect.Method
import java.util.ArrayList
public interface Plugin {
val name: String
val tasks : ArrayList<PluginTask>
fun accept(project: Project) : Boolean
fun apply(project: Project, context: KobaltContext) {}
class MethodTask(val method: Method, val taskAnnotation: Task)
val methodTasks : ArrayList<MethodTask>
fun addTask(annotation: Task, project: Project, task: (Project) -> TaskResult) {
tasks.add(object : BasePluginTask(this, annotation.name, annotation.description, project) {
override fun call(): TaskResult2<PluginTask> {
val taskResult = task(project)
return TaskResult2(taskResult.success, this)
}
})
}
var taskManager : TaskManager
fun dependsOn(task1: String, task2: String) {
taskManager.dependsOn(task1, task2)
}
}

View file

@ -0,0 +1,17 @@
package com.beust.kobalt.api
import com.beust.kobalt.internal.TaskResult2
import com.beust.kobalt.misc.ToString
import java.util.concurrent.Callable
public interface PluginTask : Callable<TaskResult2<PluginTask>> {
val plugin: Plugin
val name: String
val doc: String
val project: Project
val dependsOn : List<String>
override public fun toString() : String {
return ToString("PluginTask", "id", project.name + ":" + name).s
}
}

View file

@ -0,0 +1,96 @@
package com.beust.kobalt.api
import com.beust.kobalt.api.annotation.Directive
import com.beust.kobalt.maven.MavenDependency
import com.beust.kobalt.maven.IClasspathDependency
import com.google.common.base.Preconditions
import java.util.ArrayList
open public class Project(
open var name: String? = null,
open var version: String? = null,
open var directory: String = ".",
open var buildDirectory: String? = "kobaltBuild",
open var group: String? = null,
open var artifactId: String? = null,
open var dependencies: Dependencies? = null,
open var sourceSuffix : String = "",
open var compilerInfo : ICompilerInfo ) {
var testArgs: ArrayList<String> = arrayListOf()
override fun equals(other: Any?): Boolean {
return name == (other as Project).name
}
override fun hashCode(): Int {
return name!!.hashCode()
}
//
// Directories
//
@Directive
public fun sourceDirectories(init: Sources.() -> Unit) : Sources {
val sources = Sources(this, sourceDirectories)
sources.init()
return sources
}
var sourceDirectories : ArrayList<String> = arrayListOf()
get() = if (field.isEmpty()) compilerInfo.defaultSourceDirectories else field
set(value) {
field = value
}
@Directive
public fun sourceDirectoriesTest(init: Sources.() -> Unit) : Sources {
val sources = Sources(this, sourceDirectoriesTest)
sources.init()
return sources
}
var sourceDirectoriesTest : ArrayList<String> = arrayListOf()
get() = if (field.isEmpty()) compilerInfo.defaultTestDirectories
else field
set(value) {
field = value
}
//
// Dependencies
//
@Directive
public fun dependencies(init: Dependencies.() -> Unit) : Dependencies {
dependencies = Dependencies(this, compileDependencies)
dependencies!!.init()
return dependencies!!
}
public val compileDependencies : ArrayList<IClasspathDependency> = arrayListOf()
@Directive
public fun dependenciesTest(init: Dependencies.() -> Unit) : Dependencies {
dependencies = Dependencies(this, testDependencies)
dependencies!!.init()
return dependencies!!
}
public val testDependencies : ArrayList<IClasspathDependency> = arrayListOf()
}
public class Sources(val project: Project, val sources: ArrayList<String>) {
@Directive
public fun path(vararg paths: String) {
sources.addAll(paths)
}
}
public class Dependencies(val project: Project, val dependencies: ArrayList<IClasspathDependency>) {
@Directive
fun compile(vararg dep: String) {
dep.forEach { dependencies.add(MavenDependency.create(it)) }
}
}

View file

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

View file

@ -0,0 +1,11 @@
package com.beust.kobalt.api.annotation
import kotlin.annotation.Retention
annotation class Directive
@Retention(AnnotationRetention.RUNTIME)
annotation class Task(val name: String,
val description: String,
val runBefore: Array<String> = arrayOf(),
val runAfter: Array<String> = arrayOf())

View file

@ -0,0 +1,231 @@
package com.beust.kobalt.internal
import com.beust.kobalt.misc.KobaltLogger
import com.beust.kobalt.misc.NamedThreadFactory
import com.beust.kobalt.misc.ToString
import com.google.common.collect.ArrayListMultimap
import java.util.*
import java.util.concurrent.*
open class TaskResult2<T>(success: Boolean, val value: T) : TaskResult(success) {
override fun toString() = ToString("TaskResult", "success", success, "value", value).s
}
public interface IWorker<T> : Callable<TaskResult2<T>> {
/**
* @return list of tasks this worker is working on.
*/
// val tasks : List<T>
/**
* @return the priority of this task.
*/
val priority : Int
}
public 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: List<T>) : List<IWorker<T>>
}
public class DynamicGraphExecutor<T>(val graph: DynamicGraph<T>,
val factory: IThreadWorkerFactory<T>) : KobaltLogger {
val executor = Executors.newFixedThreadPool(5, NamedThreadFactory("DynamicGraphExecutor"))
val completion = ExecutorCompletionService<TaskResult2<T>>(executor)
public fun run() {
while (graph.freeNodes.size() > 0) {
log(2, "Current count: ${graph.nodeCount}")
synchronized(graph) {
val freeNodes = graph.freeNodes
freeNodes.forEach { graph.setStatus(it, DynamicGraph.Status.RUNNING)}
log(2, "submitting free nodes ${freeNodes}")
val callables : List<IWorker<T>> = factory.createWorkers(freeNodes)
callables.forEach { completion.submit(it) }
var n = callables.size()
// When a callable ends, see if it freed a node. If not, keep looping
while (n > 0 && graph.freeNodes.size() == 0) {
try {
val future = completion.take()
val taskResult = future.get(2, TimeUnit.SECONDS)
log(2, "Received task result ${taskResult}")
n--
graph.setStatus(taskResult.value,
if (taskResult.success) DynamicGraph.Status.FINISHED else DynamicGraph.Status.ERROR)
} catch(ex: TimeoutException) {
log(2, "Time out")
}
}
}
}
executor.shutdown()
}
}
/**
* Representation of the graph of methods.
*/
public class DynamicGraph<T> : KobaltLogger {
private val DEBUG = false
private val nodesReady = linkedSetOf<T>()
private val nodesRunning = linkedSetOf<T>()
private val nodesFinished = linkedSetOf<T>()
private val nodesInError = linkedSetOf<T>()
private val nodesSkipped = linkedSetOf<T>()
private val dependedUpon = ArrayListMultimap.create<T, T>()
private val dependingOn = ArrayListMultimap.create<T, T>()
/**
* Define a comparator for the nodes of this graph, which will be used
* to order the free nodes when they are asked.
*/
public val comparator : Comparator<T>? = null
enum class Status {
READY, RUNNING, FINISHED, ERROR, SKIPPED
}
/**
* Add a node to the graph.
*/
public fun addNode(value: T) : T {
nodesReady.add(value)
return value
}
/**
* Add an edge between two nodes, which don't have to already be in the graph
* (they will be added by this method). Makes "to" depend on "from".
*/
public fun addEdge(from: T, to: T) {
val fromNode = addNode(from)
val toNode = addNode(to)
dependingOn.put(toNode, fromNode)
dependedUpon.put(fromNode, toNode)
}
/**
* @return a set of all the nodes that don't depend on any other nodes.
*/
public val freeNodes : List<T>
get() {
val result = arrayListOf<T>()
nodesReady.forEach { m ->
// A node is free if...
val du = dependedUpon.get(m)
// - no other nodes depend on it
if (! dependedUpon.containsKey(m)) {
result.add(m)
} else if (getUnfinishedNodes(du).size() == 0) {
result.add(m)
}
}
// Sort the free nodes if requested (e.g. priorities)
// if (! result.isEmpty()) {
// if (comparator != null) {
// Collections.sort(result, comparator)
// debug("Nodes after sorting:" + result.get(0))
// }
// }
log(2, "freeNodes: ${result}")
return result
}
/**
* @return a list of all the nodes that have a status other than FINISHED.
*/
private fun getUnfinishedNodes(nodes: List<T>) : Collection<T> {
val result = hashSetOf<T>()
nodes.forEach { node ->
if (nodesReady.contains(node) || nodesRunning.contains(node)) {
result.add(node);
}
}
return result;
}
/**
* Set the status for a set of nodes.
*/
public fun setStatus(nodes: Collection<T>, status: Status) {
nodes.forEach { setStatus(it, status) }
}
/**
* Mark all dependees of this node SKIPPED
*/
private fun setSkipStatus(node: T, status: Status) {
dependingOn.get(node).forEach {
if (! nodesSkipped.contains(it)) {
log(2, "Node skipped: ${it}")
nodesSkipped.add(it)
nodesReady.remove(it)
setSkipStatus(it, status)
}
}
}
/**
* Set the status for a node.
*/
public fun setStatus(node: T, status: Status) {
removeNode(node);
when(status) {
Status.READY -> nodesReady.add(node)
Status.RUNNING -> nodesRunning.add(node)
Status.FINISHED -> nodesFinished.add(node)
Status.ERROR -> {
log(2, "Node in error: ${node}")
nodesReady.remove(node)
nodesInError.add(node)
setSkipStatus(node, status)
}
else -> {
throw IllegalArgumentException()
}
}
}
private fun removeNode(node: T) {
if (! nodesReady.remove(node)) {
if (! nodesRunning.remove(node)) {
nodesFinished.remove(node)
}
}
}
/**
* @return the number of nodes in this graph.
*/
public val nodeCount: Int
get() = nodesReady.size() + nodesRunning.size() + nodesFinished.size()
override public fun toString() : String {
val result = StringBuilder("[DynamicGraph ")
result.append("\n Ready:" + nodesReady)
result.append("\n Running:" + nodesRunning)
result.append("\n Finished:" + nodesFinished)
result.append("\n Edges:\n")
// dependingOn.entrySet().forEach { es ->
// result.append(" " + es.getKey() + "\n");
// es.getValue().forEach { t ->
// result.append(" " + t + "\n");
// }
// }
result.append("]");
return result.toString();
}
}

View file

@ -0,0 +1,50 @@
package com.beust.kobalt.internal
import com.beust.kobalt.api.Project
import com.beust.kobalt.maven.IClasspathDependency
import com.beust.kobalt.misc.KFiles
import com.beust.kobalt.misc.KobaltLogger
import com.beust.kobalt.plugin.java.JavaInfo
import com.beust.kobalt.plugin.java.SystemProperties
import java.io.File
abstract class GenericTestRunner(open val project: Project, open val classpath: List<IClasspathDependency>)
: KobaltLogger {
abstract val mainClass: String
abstract val args: List<String>
protected fun findTestClasses(): List<String> {
val path = KFiles.joinDir(project.directory, project.buildDirectory!!, KFiles.TEST_CLASSES_DIR)
val result = KFiles.findRecursively(File(path),
arrayListOf(File("."))) { file -> file.endsWith("Test.class")
}.map {
it.replace("/", ".").replace(".class", "").substring(2)
}
return result
}
fun runTests() {
val jvm = JavaInfo.create(File(SystemProperties.javaBase))
val java = jvm.javaExecutable
val allArgs = arrayListOf<String>()
allArgs.add(java!!.getAbsolutePath())
allArgs.add("-classpath")
allArgs.add(classpath.map { it.jarFile.get().getAbsolutePath() }.join(File.pathSeparator))
allArgs.add(mainClass)
allArgs.addAll(args)
val pb = ProcessBuilder(allArgs)
pb.directory(File(project.directory))
pb.inheritIO()
log(1, "Running tests with classpath size ${classpath.size()}")
val process = pb.start()
val errorCode = process.waitFor()
if (errorCode == 0) {
log(1, "All tests passed")
} else {
log(1, "Test failures")
}
}
}

View file

@ -0,0 +1,17 @@
package com.beust.kobalt.internal
import com.beust.kobalt.api.Project
import com.beust.kobalt.maven.IClasspathDependency
import com.beust.kobalt.misc.KFiles
import com.beust.kobalt.misc.KobaltLogger
import com.beust.kobalt.plugin.java.JavaInfo
import com.beust.kobalt.plugin.java.SystemProperties
import java.io.File
public class JUnitRunner(override val project: Project, override val classpath: List<IClasspathDependency>)
: GenericTestRunner(project, classpath) {
override val mainClass = "org.junit.runner.JUnitCore"
override val args = findTestClasses()
}

View file

@ -0,0 +1,120 @@
package com.beust.kobalt.internal
import com.beust.kobalt.api.BasePlugin
import com.beust.kobalt.api.Project
import com.beust.kobalt.api.annotation.Directive
import com.beust.kobalt.api.annotation.Task
import com.beust.kobalt.maven.*
import com.beust.kobalt.misc.KFiles
import com.beust.kobalt.misc.KobaltExecutors
import com.beust.kobalt.misc.KobaltLogger
import java.io.File
import java.util.ArrayList
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
abstract public class JvmCompilerPlugin @Inject constructor(
open val localRepo: LocalRepo,
open val files: KFiles,
open val depFactory: DepFactory,
open val dependencyManager: DependencyManager,
open val executors: KobaltExecutors) : BasePlugin(), KobaltLogger {
companion object {
const val TASK_CLEAN = "clean"
const val TASK_TEST = "test"
const val SOURCE_SET_MAIN = "main"
const val SOURCE_SET_TEST = "test"
const val DOCS_DIRECTORY = "docs/javadoc"
}
/**
* Log with a project.
*/
protected fun lp(project: Project, s: String) {
log(1, "${project.name}: ${s}")
}
fun calculateClasspath(dependencies : List<IClasspathDependency>): List<IClasspathDependency> {
return dependencyManager.transitiveClosure(dependencies)
}
protected fun testDependencies(project: Project) : List<IClasspathDependency> {
val result = arrayListOf<IClasspathDependency>()
result.add(FileDependency(makeOutputDir(project).getAbsolutePath()))
result.add(FileDependency(makeOutputTestDir(project).getAbsolutePath()))
result.addAll(calculateClasspath(project.compileDependencies))
result.addAll(calculateClasspath(project.testDependencies))
return dependencyManager.reorderDependencies(result)
}
@Task(name = TASK_TEST, description = "Run the tests", runAfter = arrayOf("compile", "compileTest"))
fun taskTest(project: Project) : TaskResult {
lp(project, "Running tests")
if (project.testDependencies.any { it.id.contains("testng")} ) {
TestNgRunner(project, testDependencies(project)).runTests()
} else {
JUnitRunner(project, testDependencies(project)).runTests()
}
return TaskResult()
}
@Task(name = TASK_CLEAN, description = "Clean the project", runBefore = arrayOf("compile"))
fun taskClean(project : Project ) : TaskResult {
java.io.File(project.buildDirectory).deleteRecursively()
return TaskResult()
}
protected fun makeOutputDir(project: Project) : File = makeDir(project, KFiles.CLASSES_DIR)
protected fun makeOutputTestDir(project: Project) : File = makeDir(project, KFiles.TEST_CLASSES_DIR)
private fun makeDir(project: Project, suffix: String) : File {
return File(project.directory, project.buildDirectory + File.separator + suffix)
}
/**
* Copy the resources from a source directory to the build one
*/
protected fun copyResources(project: Project, sourceSet: String) {
val sourceDirs: ArrayList<String> = arrayListOf()
var outputDir: String?
if (sourceSet == "main") {
sourceDirs.addAll(project.sourceDirectories.filter { it.contains("resources") })
outputDir = KFiles.CLASSES_DIR
} else if (sourceSet == "test") {
sourceDirs.addAll(project.sourceDirectoriesTest.filter { it.contains("resources") })
outputDir = KFiles.TEST_CLASSES_DIR
} else {
throw IllegalArgumentException("Custom source sets not supported yet: ${sourceSet}")
}
if (sourceDirs.size() > 0) {
lp(project, "Copying ${sourceSet} resources")
val absOutputDir = File(KFiles.joinDir(project.directory, project.buildDirectory!!, outputDir))
sourceDirs.map { File(it) }.filter { it.exists() } .forEach {
log(2, "Copying from ${sourceDirs} to ${absOutputDir}")
KFiles.copyRecursively(it, absOutputDir)
}
} else {
lp(project, "No resources to copy for ${sourceSet}")
}
}
}
public class TestConfig(val project: Project) {
fun args(vararg arg: String) {
project.testArgs.addAll(arg)
}
}
@Directive
public fun test(project: Project, init: TestConfig.() -> Unit) : TestConfig {
val result = TestConfig(project)
result.init()
return result
}

View file

@ -0,0 +1,7 @@
package com.beust.kobalt.internal
import java.util.jar.JarInputStream
public class PluginLoader(val jarStream: JarInputStream) {
}

View file

@ -0,0 +1,9 @@
package com.beust.kobalt.internal
import com.beust.kobalt.Plugins
import com.beust.kobalt.api.BasePlugin
import com.beust.kobalt.maven.KobaltException
import java.lang.reflect.Method
import java.util.concurrent.Callable
open public class TaskResult(val success: Boolean = true, val errorMessage: String? = null)

View file

@ -0,0 +1,169 @@
package com.beust.kobalt.internal
import com.beust.kobalt.Plugins
import com.beust.kobalt.api.PluginTask
import com.beust.kobalt.api.Project
import com.beust.kobalt.api.Task
import com.beust.kobalt.misc.KobaltLogger
import com.beust.kobalt.maven.KobaltException
import com.google.common.collect.ArrayListMultimap
import com.google.common.collect.HashMultimap
import com.google.common.collect.TreeMultimap
import java.util.HashSet
import java.util.concurrent.LinkedBlockingQueue
import java.util.concurrent.TimeUnit
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
public class TaskManager @Inject constructor(val plugins: Plugins) : KobaltLogger {
private val dependentTaskMap = TreeMultimap.create<String, String>()
/**
* Called by plugins to indicate task dependencies defined at runtime. Keys depend on values.
* Declare that `task1` depends on `task2`.
*/
fun dependsOn(task1: String, task2: String) {
dependentTaskMap.put(task1, task2)
}
public fun runTargets(targets: List<String>, projects: List<Project>) {
val tasksByNames = HashMultimap.create<String, PluginTask>()
projects.forEach { project ->
log(1, "")
log(1, " Building project ${project.name}")
log(1, "")
//
// Locate all the tasks
//
plugins.allTasks.filter { it.project.name == project.name }.forEach { rt ->
tasksByNames.put(rt.name, rt)
if (rt.dependsOn.size() > 0) {
rt.dependsOn.forEach { d ->
dependentTaskMap.put(rt.name, d)
}
}
}
val freeTaskMap = hashMapOf<String, PluginTask>()
tasksByNames.keySet().forEach {
if (!dependentTaskMap.containsKey(it)) freeTaskMap.put(it, tasksByNames.get(it).elementAt(0))
}
log(2, "Free tasks: ${freeTaskMap.keySet()}")
log(2, "Dependent tasks:")
dependentTaskMap.keySet().forEach { t ->
log(2, " ${t} -> ${dependentTaskMap.get(t)}}")
}
//
// Find the tasks required to run the targets and add them to the dynamic graph
//
val transitiveClosure = hashSetOf<String>()
val seen = HashSet(targets)
val toProcess = HashSet(targets)
var done = false
while (!done) {
val newToProcess = hashSetOf<String>()
log(3, "toProcess size: " + toProcess.size())
toProcess.forEach { target ->
log(3, "Processing ${target}")
val actualTarget =
if (target.contains(":")) {
// The target specifies a project explicitly
target.split(":").let {
val projectName = it[0]
if (projectName == project.name) {
it[1]
} else {
null
}
}
} else {
target
}
if (actualTarget != null) {
transitiveClosure.add(actualTarget)
val tasks = tasksByNames.get(actualTarget)
if (tasks.isEmpty()) {
throw KobaltException("Unknown task: ${target}")
}
tasks.forEach { task ->
val dependencyNames = dependentTaskMap.get(task.name)
dependencyNames.forEach { dependencyName ->
if (!seen.contains(dependencyName)) {
newToProcess.add(dependencyName)
seen.add(dependencyName)
}
}
}
} else {
log(2, "Target ${target} specified so not running it for project ${project.name}")
}
}
done = newToProcess.isEmpty()
toProcess.clear()
toProcess.addAll(newToProcess)
}
//
// Create a dynamic graph with the transitive closure
//
val graph = DynamicGraph<PluginTask>()
freeTaskMap.values().filter { transitiveClosure.contains(it.name) } forEach { graph.addNode(it) }
dependentTaskMap.entries().filter {
transitiveClosure.contains(it.key)
}.forEach { entry ->
plugins.findTasks(entry.key).filter { it.project.name == project.name }.forEach { from ->
plugins.findTasks(entry.value).filter { it.project.name == project.name }.forEach { to ->
if (from.project.name == to.project.name) {
graph.addEdge(from, to)
}
}
}
}
//
// Run the dynamic graph
//
val factory = object : IThreadWorkerFactory<PluginTask> {
override public fun createWorkers(nodes: List<PluginTask>): List<IWorker<PluginTask>> {
val result = arrayListOf<IWorker<PluginTask>>()
nodes.forEach {
result.add(TaskWorker(arrayListOf(it)))
}
return result
}
}
val executor = DynamicGraphExecutor(graph, factory)
executor.run()
}
}
}
class TaskWorker(val tasks: List<PluginTask>) : IWorker<PluginTask>, KobaltLogger {
// override fun compareTo(other: IWorker2<PluginTask>): Int {
// return priority.compareTo(other.priority)
// }
override fun call() : TaskResult2<PluginTask> {
if (tasks.size() > 0) {
tasks.get(0).let {
log(1, "========== ${it.project.name}:${it.name}")
}
}
var success = true
tasks.forEach {
val tr = it.call()
success = success and tr.success
}
return TaskResult2(success, tasks.get(0))
}
// override val timeOut : Long = 10000
override val priority: Int = 0
}

View file

@ -0,0 +1,34 @@
package com.beust.kobalt.internal
import com.beust.kobalt.api.Project
import com.beust.kobalt.maven.IClasspathDependency
import com.beust.kobalt.misc.KFiles
import com.beust.kobalt.misc.KobaltLogger
import com.beust.kobalt.plugin.java.JavaInfo
import com.beust.kobalt.plugin.java.SystemProperties
import java.io.File
import java.util.*
public class TestNgRunner(override val project: Project, override val classpath: List<IClasspathDependency>)
: GenericTestRunner(project, classpath) {
override val mainClass = "org.testng.TestNG"
override val args: List<String>
get() {
arrayListOf<String>().let {
if (project.testArgs.size() > 0) {
it.addAll(project.testArgs)
} else {
val testngXml = File(project.directory, KFiles.joinDir("src", "test", "resources", "testng.xml"))
if (testngXml.exists()) {
it.add(testngXml.getAbsolutePath())
} else {
it.add("-testclass")
it.addAll(findTestClasses())
}
}
return it
}
}
}

View file

@ -0,0 +1,18 @@
package com.beust.kobalt.kotlin
import java.io.File
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.attribute.BasicFileAttributes
/**
* Sometimes, build files are moved to temporary files, so we give them a specific name for clarity.
*/
public class BuildFile(val path: Path, val name: String) {
public fun exists() : Boolean = Files.exists(path)
public fun lastModified() : Long = Files.readAttributes(path, BasicFileAttributes::class.java).lastModifiedTime()
.toMillis()
public val directory : File get() = path.toFile().directory
}

View file

@ -0,0 +1,129 @@
package com.beust.kobalt.kotlin
import com.beust.kobalt.Plugins
import com.beust.kobalt.api.Kobalt
import com.beust.kobalt.api.Plugin
import com.beust.kobalt.api.Project
import com.beust.kobalt.api.annotation.Task
import com.beust.kobalt.maven.KobaltException
import com.beust.kobalt.misc.KFiles
import com.beust.kobalt.misc.KobaltLogger
import com.beust.kobalt.plugin.kotlin.kotlinCompiler
import com.beust.kobalt.plugin.kotlin.kotlinCompilePrivate
import com.google.inject.assistedinject.Assisted
import java.io.File
import java.io.FileInputStream
import java.io.InputStream
import java.lang.reflect.Modifier
import java.util.jar.JarInputStream
import javax.inject.Inject
import kotlin.properties.Delegates
/**
* Compile Build.kt into a jar file
*/
public class ScriptCompiler @Inject constructor(
@Assisted("jarFiles") val jarFiles: List<String>,
@Assisted("instantiate") val instantiate: (String, File?) -> Class<*>,
val files: KFiles) : KobaltLogger {
interface IFactory {
fun create(@Assisted("jarFiles") jarFiles: List<String>,
@Assisted("instantiate") instantiate: (String, File?) -> Class<*>) : ScriptCompiler
}
private var buildScriptJarFile by Delegates.notNull<File>()
public class CompileOutput(val projects: List<Project>, val plugins: List<String>)
public fun compile(buildFile: BuildFile, lastModified: Long, jarFileName: String) : CompileOutput {
if (! buildFile.exists()) {
throw KobaltException("Couldn't find ${buildFile.name}")
}
buildScriptJarFile = File(jarFileName)
buildScriptJarFile.parentFile.mkdirs()
log(2, "Running build file ${buildFile.name} jar: ${buildScriptJarFile}")
if (buildFile.exists() && buildScriptJarFile.exists()
&& lastModified < buildScriptJarFile.lastModified()) {
log(2, "Build file is up to date")
} else {
log(2, "Need to recompile ${buildFile.name}")
generateJarFile(buildFile)
}
return CompileOutput(instantiateBuildFile(), arrayListOf<String>())
}
private fun generateJarFile(buildFile: BuildFile) {
kotlinCompilePrivate {
classpath(files.kobaltJar)
classpath(jarFiles)
sourceFiles(buildFile.path.toFile().absolutePath)
output = buildScriptJarFile.absolutePath
}.compile()
}
private fun instantiateBuildFile() : List<Project> {
val result = arrayListOf<Project>()
var stream : InputStream? = null
try {
stream = JarInputStream(FileInputStream(buildScriptJarFile))
var entry = stream.nextJarEntry
val classes = hashSetOf<Class<*>>()
while (entry != null) {
val name = entry.name;
if (name.endsWith(".class")) {
val className = name.substring(0, name.length() - 6).replace("/", ".")
var cl : Class<*>? = instantiate(className, buildScriptJarFile)
if (cl != null) {
classes.add(cl)
} else {
throw KobaltException("Couldn't instantiate ${className}")
}
}
entry = stream.nextJarEntry;
}
// Invoke all the "val" found on the _DefaultPackage class (the Build.kt file)
classes.filter { cls ->
cls.name != "_DefaultPackage"
}.forEach { cls ->
cls.methods.forEach { method ->
// Invoke vals and see if they return a Project
if (method.name.startsWith("get") && Modifier.isStatic(method.modifiers)) {
val r = method.invoke(null)
if (r is Project) {
log(2, "Found project ${r} in class ${cls}")
result.add(r)
}
} else {
val taskAnnotation = method.getAnnotation(Task::class.java)
if (taskAnnotation != null) {
// Plugins.defaultPlugin.addTask(taskAnnotation, )
Plugins.defaultPlugin.methodTasks.add(Plugin.MethodTask(method, taskAnnotation))
}
}}
// cls.methods.filter { method ->
// method.getName().startsWith("get") && Modifier.isStatic(method.getModifiers())
// }.forEach {
// val r = it.invoke(null)
// if (r is Project) {
// log(2, "Found project ${r} in class ${cls}")
// result.add(r)
// }
// }
}
} finally {
stream?.close()
}
// Now that we all the projects, sort them topologically
return Kobalt.sortProjects(result)
}
}

View file

@ -0,0 +1,102 @@
package com.beust.kobalt.maven
import com.beust.kobalt.file
import com.beust.kobalt.misc.KFiles
import com.beust.kobalt.misc.KobaltLogger
import com.beust.kobalt.plugin.packaging.JarUtils
import com.google.common.cache.CacheBuilder
import com.google.common.cache.CacheLoader
import com.google.common.cache.LoadingCache
import com.google.inject.assistedinject.Assisted
import java.io.ByteArrayOutputStream
import java.io.File
import java.security.MessageDigest
import java.util.concurrent.Callable
import java.util.concurrent.ExecutorService
import java.util.concurrent.Future
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class DownloadManager @Inject constructor(val factory: ArtifactFetcher.IFactory) {
class Key(val url: String, val fileName: String, val executor: ExecutorService) {
override fun equals(other: Any?): Boolean {
return (other as Key).url == url
}
override fun hashCode(): Int {
return url.hashCode()
}
}
private val CACHE : LoadingCache<Key, Future<File>> = CacheBuilder.newBuilder()
.build(object : CacheLoader<Key, Future<File>>() {
override fun load(key: Key): Future<File> {
return key.executor.submit(factory.create(key.url, key.fileName))
}
})
public fun download(url: String, fileName: String, executor: ExecutorService)
: Future<File> = CACHE.get(Key(url, fileName, executor))
}
/**
* Fetches an artifact (a file in a Maven repo, .jar, -javadoc.jar, ...) to the given local file.
*/
class ArtifactFetcher @Inject constructor(@Assisted("url") val url: String,
@Assisted("fileName") val fileName: String,
val files: KFiles, val http: Http) : Callable<File>, KobaltLogger {
interface IFactory {
fun create(@Assisted("url") url: String, @Assisted("fileName") fileName: String) : ArtifactFetcher
}
/** The Kotlin compiler is about 17M and downloading it with the default buffer size takes forever */
private val estimatedSize: Int
get() = if (url.contains("kotlin-compiler")) 18000000 else 1000000
private fun toMd5(bytes: ByteArray) : String {
val result = StringBuilder()
val md5 = MessageDigest.getInstance("MD5").digest(bytes)
md5.forEach {
val byte = it.toInt() and 0xff
if (byte < 16) result.append("0")
result.append(Integer.toHexString(byte))
}
return result.toString()
}
private fun getBytes(url: String) : ByteArray {
log(2, "${url}: downloading to ${fileName}")
val body = http.get(url)
if (body.code == 200) {
val buffer = ByteArrayOutputStream(estimatedSize)
body.getAsStream().copyTo(buffer, estimatedSize)
return buffer.toByteArray()
} else {
throw KobaltException("${url}: failed to download, code: ${body.code}")
}
}
override fun call() : File {
val md5Body = http.get(url + ".md5")
val remoteMd5 = if (md5Body.code == 200) {
md5Body.getAsString().trim(' ', '\t', '\n').substring(0, 32)
} else {
null
}
val file = File(fileName)
file.parentFile.mkdirs()
val bytes = getBytes(url)
if (remoteMd5 != null && remoteMd5 != toMd5(bytes)) {
throw KobaltException("MD5 not matching for ${url}")
} else {
log(2, "No md5 found for ${url}, skipping md5 check")
}
files.saveFile(file, bytes)
log(1, "${url}: DOWNLOADED")
return file
}
}

View file

@ -0,0 +1,13 @@
package com.beust.kobalt.maven
import java.util.concurrent.Future
import java.util.concurrent.TimeUnit
public class CompletedFuture<T>(val value: T) : Future<T> {
override fun cancel(mayInterruptIfRunning: Boolean) = true
override fun get(): T = value
override fun get(timeout: Long, unit: TimeUnit): T = value
override fun isCancelled(): Boolean = false
override fun isDone(): Boolean = true
}

View file

@ -0,0 +1,49 @@
package com.beust.kobalt.maven
import com.beust.kobalt.maven.DownloadManager
import com.beust.kobalt.maven.KobaltException
import com.beust.kobalt.maven.Pom
import com.beust.kobalt.misc.KobaltExecutors
import com.beust.kobalt.misc.KobaltLogger
import java.util.ArrayList
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.ExecutorService
import javax.inject.Inject
public class DepFactory @Inject constructor(val localRepo: LocalRepo,
val repoFinder: RepoFinder,
val executors: KobaltExecutors,
val downloadManager: DownloadManager,
val pomFactory: Pom.IFactory) : KobaltLogger {
/**
* Parse the id and return the correct IClasspathDependency
*/
public fun create(id: String, executor: ExecutorService,
localFirst : Boolean = true) : IClasspathDependency {
if (id.startsWith(IClasspathDependency.PREFIX_FILE)) {
return FileDependency(id.substring(IClasspathDependency.PREFIX_FILE.length()))
} else {
val c = id.split(":")
var repoResult: RepoFinder.RepoResult?
var version: String? = null
if (! MavenDependency.hasVersion(id)) {
if (localFirst) version = localRepo.findLocalVersion(c[0], c[1])
if (! localFirst || version == null) {
repoResult = repoFinder.findCorrectRepo(id)
if (!repoResult.found) {
throw KobaltException("Couldn't resolve ${id}")
} else {
version = repoResult.version
}
}
} else {
version = c[2]
}
return MavenDependency(c[0], c[1], version, executor, localRepo, repoFinder,
pomFactory, downloadManager)
}
}
}

View file

@ -0,0 +1,55 @@
package com.beust.kobalt.maven
import com.beust.kobalt.misc.KobaltExecutors
import com.google.common.collect.ArrayListMultimap
import java.util.Collections
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
public class DependencyManager @Inject constructor(val executors: KobaltExecutors,
val depFactory: DepFactory){
fun transitiveClosure(dependencies : List<IClasspathDependency>): List<IClasspathDependency> {
var executor = executors.newExecutor("JvmCompiler}", 10)
var result = hashSetOf<IClasspathDependency>()
dependencies.forEach { projectDependency ->
projectDependency.id.let {
result.add(depFactory.create(it, executor))
val downloaded = projectDependency.transitiveDependencies(executor)
result.addAll(downloaded)
}
}
val result2 = reorderDependencies(result).filter {
// Only keep existent files (nonexistent files are probably optional dependencies)
it.jarFile.get().exists()
}
executor.shutdown()
return result2
}
/**
* Reorder dependencies so that if an artifact appears several times, only the one with the higest version
* is included.
*/
public fun reorderDependencies(dependencies: Collection<IClasspathDependency>): List<IClasspathDependency> {
val result = arrayListOf<IClasspathDependency>()
val map : ArrayListMultimap<String, IClasspathDependency> = ArrayListMultimap.create()
// The multilist maps each artifact to a list of all the versions found
// (e.g. {org.testng:testng -> (6.9.5, 6.9.4, 6.1.1)}), then we return just the first one
dependencies.forEach {
map.put(it.shortId, it)
}
for (k in map.keySet()) {
val l = map.get(k)
Collections.sort(l, Collections.reverseOrder())
result.add(l.get(0))
}
return result
}
}

View file

@ -0,0 +1,81 @@
package com.beust.kobalt.maven
import com.beust.kobalt.misc.KobaltLogger
import com.squareup.okhttp.*
import java.io.File
import java.io.IOException
import java.io.InputStream
import javax.inject.Singleton
@Singleton
public class Http : KobaltLogger {
class Body(val body: ResponseBody, val code: Int) {
public fun getAsString() : String {
return body.string()
}
public fun getAsStream() : InputStream {
return body.byteStream()
}
}
public fun get(user: String?, password: String?, url: String) : Body {
val client = OkHttpClient();
val request = Request.Builder().url(url)
if (user != null) {
request.header("Authorization", Credentials.basic(user, password))
}
try {
val response = client.newCall(request.build()).execute()
return Body(response.body(), response.code())
} catch(ex: IOException) {
throw KobaltException("Could not load URL ${url}, error: " + ex.getMessage(), ex)
}
}
private val MEDIA_TYPE_BINARY = MediaType.parse("application/octet-stream")
public fun get(url: String) : Body {
return get(null, null, url)
}
private fun builder(user: String?, password: String?) : Request.Builder {
val result = Request.Builder()
user?.let {
result.header("Authorization", Credentials.basic(user, password))
}
return result
}
public fun uploadFile(user: String?, password: String?, url: String, file: File,
success: (Response) -> Unit,
error: (Response) -> Unit) {
log(2, "Uploading ${file} to ${url}")
val request = builder(user, password)
.url(url)
.put(RequestBody.create(MEDIA_TYPE_BINARY, file))
.build()
val response = OkHttpClient().newCall(request).execute()
if (! response.isSuccessful()) {
error(response)
} else {
success(response)
}
}
private val JSON = MediaType.parse("application/json; charset=utf-8")
fun post(user: String?, password: String?, url: String, payload: String) : String {
val request = builder(user, password)
.url(url)
.post(RequestBody.create(JSON, payload))
.build()
val response = OkHttpClient().newCall(request).execute()
return response.body().string()
}
}
class KobaltException(s: String? = null, ex: Throwable? = null) : RuntimeException(s, ex) {
constructor(ex: Throwable?) : this(null, ex)
}

View file

@ -0,0 +1,75 @@
package com.beust.kobalt.maven
import java.io.File
import java.util.*
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.ExecutorService
import java.util.concurrent.Future
interface IClasspathDependency {
companion object {
val PREFIX_FILE: String = "file:"
}
/** Identifier for this dependency */
val id: String
/** Absolute path to the jar file on the local file system */
val jarFile: Future<File>
/** Convert to a Maven <dependency> model tag */
fun toMavenDependencies() : org.apache.maven.model.Dependency
/** The list of dependencies for this element (not the transitive closure */
fun directDependencies(): List<IClasspathDependency>
/** Used to only keep the most recent version for an artifact if no version was specified */
val shortId: String
fun transitiveDependencies(executor: ExecutorService) : List<IClasspathDependency> {
/**
* All the dependencies we have already downloaded.
*/
val seen = ConcurrentHashMap<String, String>()
val thisDep = MavenDependency.create(id, executor)
var result = ArrayList<IClasspathDependency>(transitiveDependencies(thisDep, seen, executor))
result.add(thisDep)
return result
}
private fun transitiveDependencies(dep: IClasspathDependency, seen: ConcurrentHashMap<String, String>,
executor: ExecutorService) : List<IClasspathDependency> {
val result = arrayListOf<IClasspathDependency>()
seen.put(dep.id, dep.id)
dep.directDependencies().filter {
! seen.containsKey(it.id)
}.forEach {
seen.put(it.id, it.id)
val thisDep = MavenDependency.create(it.id, executor)
result.add(thisDep)
result.addAll(transitiveDependencies(thisDep, seen, executor))
}
return result
}
}
open public class FileDependency(open val fileName: String) : IClasspathDependency, Comparable<FileDependency> {
override val id = IClasspathDependency.PREFIX_FILE + fileName
override val jarFile = CompletedFuture(File(fileName))
override fun toMavenDependencies(): org.apache.maven.model.Dependency {
with(org.apache.maven.model.Dependency()) {
setSystemPath(jarFile.get().getAbsolutePath())
return this
}
}
override val shortId = fileName
override fun directDependencies() = arrayListOf<IClasspathDependency>()
override fun compareTo(other: FileDependency) = fileName.compareTo(other.fileName)
}

View file

@ -0,0 +1,19 @@
package com.beust.kobalt.maven
import com.beust.kobalt.maven.CompletedFuture
import com.beust.kobalt.misc.Strings
import java.io.File
import java.util.concurrent.Future
import kotlin.properties.Delegates
open public class LocalDep(override val groupId: String, override val artifactId: String,
override val version: String,
open val localRepo: LocalRepo) : SimpleDep(groupId, artifactId, version) {
fun toAbsoluteJarFilePath(v: String) = localRepo.toFullPath(toJarFile(v))
fun toAbsolutePomFile(v: String): String {
return localRepo.toFullPath(Strings.Companion.join("/", arrayListOf(toPomFile(v))))
}
}

View file

@ -0,0 +1,61 @@
package com.beust.kobalt.maven
import com.beust.kobalt.misc.KFiles
import com.beust.kobalt.misc.KobaltLogger
import com.beust.kobalt.misc.Versions
import com.beust.kobalt.plugin.java.SystemProperties
import java.io.File
import java.util.Collections
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
open public class LocalRepo(open val localRepo: String = KFiles.localRepo) : KobaltLogger {
init {
val l = File(localRepo)
if (! l.exists()) {
l.mkdirs()
}
}
fun existsPom(d: LocalDep, v: String) : Boolean {
return File(d.toAbsolutePomFile(v)).exists()
}
fun existsJar(d: LocalDep, v: String) : Boolean {
return File(d.toAbsoluteJarFilePath(v)).exists()
}
/**
* If the dependency is local, return the correct version for it
*/
fun findLocalVersion(groupId: String, artifactId: String) : String? {
// No version: look at all the directories under group/artifactId, pick the latest and see
// if it contains a maven and jar file
val dir = toFullPath(KFiles.joinDir(groupId.replace(".", File.separator), artifactId))
val files = File(dir).listFiles()
if (files != null) {
val directories = files.filter { it.isDirectory }
if (directories.size() > 0) {
Collections.sort(directories, { f1, f2 ->
val v1 = Versions.toLongVersion(f1.name)
val v2 = Versions.toLongVersion(f2.name)
v2.compareTo(v1) // we want the most recent at position 0
})
val result = directories.get(0).name
val newDep = LocalDep(groupId, artifactId, result, this)
if (existsPom(newDep, result) && existsJar(newDep, result)) {
return result
}
}
}
return null
}
fun toFullPath(path: String) : String {
return localRepo + File.separatorChar + path
}
}

View file

@ -0,0 +1,99 @@
package com.beust.kobalt.maven
import com.beust.kobalt.INJECTOR
import com.beust.kobalt.misc.*
import com.google.common.base.CharMatcher
import com.google.inject.Key
import com.google.inject.assistedinject.Assisted
import java.io.File
import java.util.concurrent.ExecutorService
import java.util.concurrent.Future
import javax.inject.Inject
import kotlin.properties.Delegates
public class MavenDependency @Inject constructor(override @Assisted("groupId") val groupId : String,
override @Assisted("artifactId") val artifactId : String,
override @Assisted("version") val version : String,
val executor: ExecutorService,
override val localRepo: LocalRepo,
val repoFinder: RepoFinder,
val pomFactory: Pom.IFactory,
val downloadManager: DownloadManager)
: LocalDep(groupId, artifactId, version, localRepo), KobaltLogger, IClasspathDependency,
Comparable<MavenDependency> {
override var jarFile: Future<File> by Delegates.notNull()
var pomFile: Future<File> by Delegates.notNull()
init {
val jar = File(localRepo.toFullPath(toJarFile(version)))
val pom = File(localRepo.toFullPath(toPomFile(version)))
if (jar.exists() && pom.exists()) {
jarFile = CompletedFuture(jar)
pomFile = CompletedFuture(pom)
} else {
val repoResult = repoFinder.findCorrectRepo(toId(groupId, artifactId, version))
if (repoResult.found) {
jarFile = downloadManager.download(repoResult.repoUrl + toJarFile(repoResult), jar.absolutePath,
executor)
pomFile = downloadManager.download(repoResult.repoUrl + toPomFile(repoResult), pom.absolutePath,
executor)
} else {
throw KobaltException("Couldn't resolve ${toId(groupId, artifactId, version)}")
}
}
}
// interface IFactory {
// fun _create(@Assisted("groupId") groupId: String,
// @Assisted("artifactId") artifactId: String,
// @Assisted("version") version: String = "",
// executor: ExecutorService) : MavenDependency
// }
companion object {
val executor = INJECTOR.getInstance(Key.get(ExecutorService::class.java, DependencyExecutor::class.java))
val depFactory = INJECTOR.getInstance(DepFactory::class.java)
fun create(id: String, ex: ExecutorService = executor) : IClasspathDependency {
return depFactory.create(id, ex)
}
fun hasVersion(id: String) : Boolean {
val c = id.split(":")
return c.size() == 3 && !Strings.isEmpty(c[2])
}
fun toId(g: String, a: String, v: String) = "$g:$a:$v"
}
public override fun toString() = toId(groupId, artifactId, version)
override val id = toId(groupId, artifactId, version)
override fun toMavenDependencies(): org.apache.maven.model.Dependency {
with(org.apache.maven.model.Dependency()) {
setGroupId(groupId)
setArtifactId(artifactId)
setVersion(version)
return this
}
}
override fun compareTo(other: MavenDependency): Int {
return Versions.toLongVersion(version).compareTo(Versions.toLongVersion(other.version))
}
override val shortId = groupId + ":" + artifactId
override fun directDependencies() : List<IClasspathDependency> {
val result = arrayListOf<IClasspathDependency>()
pomFactory.create(id, pomFile.get()).dependencies.filter {
it.mustDownload && it.isValid
}.forEach {
result.add(create(toId(it.groupId, it.artifactId, it.version)))
}
return result
}
}

View file

@ -0,0 +1,95 @@
package com.beust.kobalt.maven
import com.beust.kobalt.misc.KobaltLogger
import com.beust.kobalt.misc.ToString
import com.google.inject.assistedinject.Assisted
import org.w3c.dom.Element
import org.w3c.dom.NodeList
import org.w3c.dom.Text
import org.xml.sax.InputSource
import java.io.FileReader
import javax.xml.xpath.XPathConstants
import kotlin.dom.get
import kotlin.dom.toXmlString
public class Pom @javax.inject.Inject constructor(@Assisted val id: String,
@Assisted documentFile: java.io.File) : KobaltLogger {
val XPATH_FACTORY = javax.xml.xpath.XPathFactory.newInstance();
val XPATH = XPATH_FACTORY.newXPath();
var groupId: String? = null
var artifactId: String? = null
var version: String? = null
var name: String? = null
var properties = sortedMapOf<String, String>()
public interface IFactory {
fun create(@Assisted id: String, @Assisted documentFile : java.io.File) : Pom
}
data public class Dependency(val groupId: String, val artifactId: String, val version: String,
val optional: Boolean = false, val scope: String? = null) : KobaltLogger {
/** When a variable is used in a maven file, e.g. ${version} */
private val VAR = "$" + "{"
val mustDownload: Boolean
get() = ! optional && "provided" != scope && "test" != scope
val isValid : Boolean
get() {
var result = false
if (version.contains(VAR)) {
log(3, "Skipping variable version ${this}")
} else if (groupId.contains(VAR)) {
log(3, "Skipping variable groupId ${this}")
} else if (artifactId.contains(VAR)) {
log(3, "Skipping variable artifactId ${this}")
} else {
result = true
}
return result
}
val id: String = "${groupId}:${artifactId}:${version}"
}
var dependencies = arrayListOf<Dependency>()
init {
val DEPENDENCIES = XPATH.compile("/project/dependencies/dependency")
val document = kotlin.dom.parseXml(InputSource(FileReader(documentFile)))
groupId = XPATH.compile("/project/groupId").evaluate(document)
artifactId = XPATH.compile("/project/artifactId").evaluate(document)
version = XPATH.compile("/project/version").evaluate(document)
name = XPATH.compile("/project/name").evaluate(document)
val deps = DEPENDENCIES.evaluate(document, XPathConstants.NODESET) as NodeList
for (i in 0..deps.getLength() - 1) {
val d = deps.item(i) as NodeList
var groupId: String? = null
var artifactId: String? = null
var version: String = ""
var optional: Boolean? = false
var scope: String? = null
for (j in 0..d.getLength() - 1) {
val e = d.item(j)
if (e is Element) {
when (e.getTagName()) {
"groupId" -> groupId = e.getTextContent ()
"artifactId" -> artifactId = e.getTextContent()
"version" -> version = e.getTextContent()
"optional" -> optional = "true".equals(e.getTextContent(), true)
"scope" -> scope = e.getTextContent()
}
}
}
log(3, "Done parsing: ${groupId} ${artifactId} ${version}")
val tmpDependency = Dependency(groupId!!, artifactId!!, version, optional!!, scope)
dependencies.add(tmpDependency)
}
}
override public fun toString() = ToString("Pom", id, "id").s
}

View file

@ -0,0 +1,51 @@
package com.beust.kobalt.maven
import com.beust.kobalt.api.Project
import com.beust.kobalt.misc.KobaltLogger
import com.beust.kobalt.plugin.java.SystemProperties
import com.google.common.base.Preconditions
import com.google.inject.assistedinject.Assisted
import org.apache.maven.model.Developer
import org.apache.maven.model.Model
import org.apache.maven.model.io.xpp3.MavenXpp3Writer
import java.io.File
import java.io.StringWriter
import java.nio.charset.Charset
import javax.inject.Inject
public class PomGenerator @Inject constructor(@Assisted val project: Project) : KobaltLogger {
interface IFactory {
fun create(project: Project) : PomGenerator
}
fun generate() {
Preconditions.checkNotNull(project.version, "version mandatory on project ${project.name}")
Preconditions.checkNotNull(project.artifactId, "artifactId mandatory on project ${project.name}")
val m = Model().apply {
name = project.name
artifactId = project.artifactId
groupId = project.group
version = project.version
}
with(Developer()) {
name = SystemProperties.username
m.addDeveloper(this)
}
val dependencies = arrayListOf<org.apache.maven.model.Dependency>()
m.dependencies = dependencies
project.compileDependencies.forEach { dep ->
dependencies.add(dep.toMavenDependencies())
}
val s = StringWriter()
MavenXpp3Writer().write(s, m)
val buildDir = com.beust.kobalt.misc.KFiles.makeDir(project.directory, project.buildDirectory!!)
val outputDir = com.beust.kobalt.misc.KFiles.makeDir(buildDir.path, "libs")
val pomFile = SimpleDep(project.group!!, project.artifactId!!, project.version!!).toPomFileName()
val outputFile = File(outputDir, pomFile)
outputFile.writeText(s.toString(), Charset.defaultCharset())
log(1, "Wrote ${outputFile}")
}
}

View file

@ -0,0 +1,145 @@
package com.beust.kobalt.maven
import com.beust.kobalt.api.Kobalt
import com.beust.kobalt.misc.KobaltExecutors
import com.beust.kobalt.misc.KobaltLogger
import com.beust.kobalt.misc.Strings
import com.google.common.cache.CacheBuilder
import com.google.common.cache.CacheLoader
import com.google.common.cache.LoadingCache
import java.util.concurrent.Callable
import java.util.concurrent.ExecutorCompletionService
import java.util.concurrent.TimeUnit
import javax.inject.Inject
import javax.xml.xpath.XPathConstants
import javax.xml.xpath.XPathFactory
import kotlin.dom.parseXml
/**
* Find the repo that contains the given dependency among a list of repos. Searches are performed in parallel and
* cached so we never make a network call for the same dependency more than once.
*/
public class RepoFinder @Inject constructor(val http: Http, val executors: KobaltExecutors) : KobaltLogger {
public fun findCorrectRepo(id: String): RepoResult {
return FOUND_REPOS.get(id)
}
data class RepoResult(val repoUrl: String, val found: Boolean, val version: String,
val snapshotVersion: String = "")
private val FOUND_REPOS: LoadingCache<String, RepoResult> = CacheBuilder.newBuilder()
.build(object : CacheLoader<String, RepoResult>() {
override fun load(key: String): RepoResult {
return loadCorrectRepo(key)
}})
/**
* Schedule an HTTP request to each repo in its own thread.
*/
private fun loadCorrectRepo(id: String): RepoResult {
val executor = executors.newExecutor("RepoFinder-${id}", Kobalt.repos.size())
val cs = ExecutorCompletionService<RepoResult>(executor)
try {
log(2, "Looking for ${id}")
Kobalt.repos.forEach { cs.submit(RepoFinderCallable(id, it)) }
for (i in 0..Kobalt.repos.size() - 1) {
try {
val result = cs.take().get(2000, TimeUnit.MILLISECONDS)
log(2, "Result for repo #${i}: ${result}")
if (result.found) {
log(2, "Located ${id} in ${result.repoUrl}")
return result
}
} catch(ex: Exception) {
warn("Error: ${ex}")
}
}
return RepoResult("", false, id)
} finally {
executor.shutdownNow()
}
}
/**
* Execute a single HTTP request to one repo.
*/
inner class RepoFinderCallable(val id: String, val repoUrl: String) : Callable<RepoResult> {
override fun call(): RepoResult {
log(2, "Checking ${repoUrl} for ${id}")
val c = id.split(":")
if (! MavenDependency.hasVersion(id)) {
val ud = UnversionedDep(c[0], c[1])
val foundVersion = findCorrectVersionRelease(ud.toMetadataXmlPath(false), repoUrl)
if (foundVersion != null) {
return RepoResult(repoUrl, true, foundVersion)
} else {
return RepoResult(repoUrl, false, "")
}
} else {
if (c[2].contains("SNAPSHOT")) {
val dep = SimpleDep(c[0], c[1], c[2])
val snapshotVersion = findSnapshotVersion(dep.toMetadataXmlPath(false), repoUrl)
if (snapshotVersion != null) {
return RepoResult(repoUrl, true, c[2], snapshotVersion)
} else {
return RepoResult(repoUrl, false, "")
}
} else {
val dep = SimpleDep(c[0], c[1], c[2])
val url = repoUrl + "/" + dep.toJarFile(dep.version)
val body = http.get(url)
log(2, "Result for ${repoUrl} for ${id}: ${body.code == 200}")
return RepoResult(repoUrl, body.code == 200, dep.version)
}
}
}
}
val XPATH_FACTORY = XPathFactory.newInstance();
val XPATH = XPATH_FACTORY.newXPath();
fun findCorrectVersionRelease(metadataPath: String, repoUrl: String): String? {
val XPATHS = arrayListOf(
XPATH.compile("/metadata/version"),
XPATH.compile("/metadata/versioning/latest"),
XPATH.compile("/metadata/versioning/release"))
// No version in this dependency, find out the most recent one by parsing maven-metadata.xml, if it exists
val url = repoUrl + metadataPath
try {
val doc = parseXml(url)
arrayListOf(XPATHS.forEach {
val result = it.evaluate(doc, XPathConstants.STRING) as String
if (! result.isEmpty()) {
return result
}
})
} catch(ex: Exception) {
log(2, "Couldn't find metadata at ${url}")
}
return null
}
fun findSnapshotVersion(metadataPath: String, repoUrl: String): String? {
val timestamp = XPATH.compile("/metadata/versioning/snapshot/timestamp")
val buildNumber = XPATH.compile("/metadata/versioning/snapshot/buildNumber")
// No version in this dependency, find out the most recent one by parsing maven-metadata.xml, if it exists
val url = repoUrl + metadataPath
try {
val doc = parseXml(url)
val ts = timestamp.evaluate(doc, XPathConstants.STRING)
val bn = buildNumber.evaluate(doc, XPathConstants.STRING)
if (! Strings.isEmpty(ts.toString()) && ! Strings.isEmpty(bn.toString())) {
return ts.toString() + "-" + bn.toString()
}
} catch(ex: Exception) {
log(2, "Couldn't find metadata at ${url}")
}
return null
}
}

View file

@ -0,0 +1,33 @@
package com.beust.kobalt.maven
import com.beust.kobalt.misc.Strings
import com.google.common.base.CharMatcher
import java.io.File
import kotlin.properties.Delegates
open public class SimpleDep(override val groupId: String, override val artifactId: String,
open val version: String) : UnversionedDep(groupId, artifactId) {
companion object {
fun create(id: String) = id.split(":").let { SimpleDep(it[0], it[1], it[2])}
}
override public fun toMetadataXmlPath(fileSystem: Boolean): String {
return toDirectory(version, fileSystem) + "/maven-metadata.xml"
}
private fun toFile(v: String, s: String, suffix: String) : String {
val fv = if (v.contains("SNAPSHOT")) v.replace("SNAPSHOT", "") else v
return Strings.join("/", arrayListOf(toDirectory(v),
artifactId + "-" + fv + s + suffix))
}
fun toPomFile(v: String) = toFile(v, "", ".pom")
fun toPomFile(r: RepoFinder.RepoResult) = toFile(r.version, r.snapshotVersion, ".pom")
fun toJarFile(v: String) = toFile(v, "", ".jar")
fun toJarFile(r: RepoFinder.RepoResult) = toFile(r.version, r.snapshotVersion, ".jar")
fun toPomFileName() = "${artifactId}-${version}.pom"
}

View file

@ -0,0 +1,24 @@
package com.beust.kobalt.maven
import com.beust.kobalt.misc.Strings
import java.io.File
/**
* Represents a dependency that doesn't have a version: "org.testng:testng:". Such dependencies
* eventually resolve to the latest version of the artifact.
*/
open public class UnversionedDep(open val groupId: String, open val artifactId: String) {
open public fun toMetadataXmlPath(fileSystem: Boolean = true): String {
return toDirectory("", fileSystem) + "/maven-metadata.xml"
}
/**
* Turn this dependency to a directory. If fileSystem is true, use the file system
* dependent path separator, otherwise, use '/' (used to create URL's)
*/
public fun toDirectory(v: String, fileSystem: Boolean = true): String {
val sep = if (fileSystem) File.separator else "/"
val l = listOf(groupId.replace(".", sep), artifactId, v)
return Strings.Companion.join(sep, l)
}
}

View file

@ -0,0 +1,8 @@
package com.beust.kobalt.misc
public fun benchmark(message: String, run: () -> Unit) {
val start = System.currentTimeMillis()
run()
println("############# Time to ${message}: ${System.currentTimeMillis() - start} ms")
}

View file

@ -0,0 +1,44 @@
package com.beust.kobalt.misc
import com.beust.kobalt.api.Kobalt
import com.beust.kobalt.api.Project
import com.beust.kobalt.maven.DepFactory
import com.beust.kobalt.maven.MavenDependency
import com.google.inject.Inject
/**
* Find out if any newer versions of the dependencies are available.
*/
public class CheckVersions @Inject constructor(val depFactory : DepFactory,
val executors : KobaltExecutors) : KobaltLogger {
fun run(projects: List<Project>) {
val executor = executors.newExecutor("CheckVersions", 5)
val newVersions = hashSetOf<String>()
projects.forEach {
val kobaltDependency = arrayListOf(
MavenDependency.create("com.beust:kobalt:" + Kobalt.version, executor))
arrayListOf(kobaltDependency, it.compileDependencies, it.testDependencies).forEach { cd ->
cd.forEach {
val dep = depFactory.create(it.shortId, executor, false /* go remote */)
if (dep is MavenDependency && it is MavenDependency) {
if (dep.id != it.id
&& Versions.toLongVersion(dep.version)
> Versions.toLongVersion(it.version)) {
newVersions.add(dep.id)
}
}
}
}
}
if (newVersions.size() > 0) {
log(1, "New versions found:")
newVersions.forEach { log(1, " ${it}") }
} else {
log(1, "All dependencies up to date")
}
executor.shutdown()
}
}

View file

@ -0,0 +1,181 @@
package com.beust.kobalt.misc
import com.beust.kobalt.api.Kobalt
import com.beust.kobalt.homeDir
import com.beust.kobalt.kotlin.BuildFile
import com.beust.kobalt.maven.KobaltException
import com.beust.kobalt.plugin.java.SystemProperties
import java.io.File
import java.io.IOException
import java.io.InputStream
import java.io.OutputStream
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.StandardCopyOption
public class KFiles {
val kobaltJar : String
get() = joinDir(distributionsDir, Kobalt.version, "kobalt/wrapper/kobalt-" + Kobalt.version + ".jar")
init {
File(KOBALT_DOT_DIR).mkdirs()
}
companion object {
private const val KOBALT_DOT_DIR : String = ".kobalt"
const val KOBALT_DIR : String = "kobalt"
// Directories under ~/.kobalt
public val localRepo = homeDir(KOBALT_DOT_DIR, "repository")
/** Where all the .zip files are extracted */
public val distributionsDir = homeDir(KOBALT_DOT_DIR, "wrapper", "dist")
// Directories under ./.kobalt
public val SCRIPT_BUILD_DIR : String = "build"
public val CLASSES_DIR : String = "classes"
/** Where build file and support source files go, under KOBALT_DIR */
private val SRC = "src"
public val TEST_CLASSES_DIR : String = "test-classes"
public fun joinDir(vararg ts: String): String = ts.toArrayList().join(File.separator)
public fun makeDir(dir: String, s: String) : File {
val result = File(dir, s)
result.mkdirs()
return result
}
public fun findRecursively(rootDir: File) : List<String> =
findRecursively(rootDir, arrayListOf(), { s -> true })
public fun findRecursively(rootDir: File, directories: List<File>,
function: Function1<String, Boolean>): List<String> {
var result = arrayListOf<String>()
val allDirs = arrayListOf<File>()
if (directories.isEmpty()) {
allDirs.add(rootDir)
} else {
allDirs.addAll(directories.map { File(rootDir, it.getPath()) })
}
allDirs.forEach {
if (! it.exists()) {
KobaltLogger.log(2, "Couldn't find directory ${it}")
} else {
result.addAll(findRecursively(it, function))
}
}
// Return files relative to rootDir
val r = result.map { it.substring(rootDir.getAbsolutePath().length() + 1)}
return r
}
public fun findRecursively(directory: File, function: Function1<String, Boolean>): List<String> {
var result = arrayListOf<String>()
directory.listFiles().forEach {
if (it.isFile && function(it.absolutePath)) {
result.add(it.absolutePath)
} else if (it.isDirectory) {
result.addAll(findRecursively(it, function))
}
}
return result
}
fun copyRecursively(from: File, to: File) {
// Need to wait until copyRecursively supports an overwrite: Boolean = false parameter
// Until then, wipe everything first
to.deleteRecursively()
to.mkdirs()
from.copyRecursively(to)
}
fun findDotDir(startDir: File) : File {
var result = startDir
while (result != null && ! File(result, KOBALT_DOT_DIR).exists()) {
result = result.parentFile
}
if (result == null) {
throw IllegalArgumentException("Couldn't locate ${KOBALT_DOT_DIR} in ${startDir}")
}
return File(result, KOBALT_DOT_DIR)
}
/**
* The build location for build scripts is .kobalt/build
*/
fun findBuildScriptLocation(buildFile: BuildFile, jarFile: String) : String {
val result = joinDir(findDotDir(buildFile.directory).absolutePath, KFiles.SCRIPT_BUILD_DIR, jarFile)
KobaltLogger.log(2, "Script jar file: ${result}")
return result
}
public fun saveFile(file: File, text: String) {
file.absoluteFile.parentFile.mkdirs()
file.delete()
KobaltLogger.log(2, "Wrote ${file}")
file.appendText(text)
}
private fun isWindows() = System.getProperty("os.name").contains("Windows");
public fun copy(from: Path?, to: Path?, option: StandardCopyOption) {
if (isWindows() && to!!.toFile().exists()) {
KobaltLogger.log(2, "Windows detected, not overwriting ${to!!}")
} else {
try {
Files.copy(from, to, option)
} catch(ex: IOException) {
// Windows is anal about this
KobaltLogger.log(1, "Couldn't copy ${from} to ${to}: ${ex.getMessage()}")
}
}
}
public fun copy(from: InputStream, to: OutputStream, bufSize: Int): Long {
val buf = ByteArray(bufSize)
var total: Long = 0
while (true) {
val r = from.read(buf, 0, buf.size())
if (r == -1) {
break
}
to.write(buf, 0, r)
total += r.toLong()
}
return total
}
public fun createTempFile(suffix : String = "", deleteOnExit: Boolean = false) : File =
File.createTempFile("kobalt", suffix, File(SystemProperties.tmpDir)).let {
if (deleteOnExit) it.deleteOnExit()
return it
}
fun src(filePath: String): String = KFiles.joinDir(KOBALT_DIR, SRC, filePath)
}
public fun findRecursively(directory: File, function: Function1<String, Boolean>): List<String> {
return KFiles.findRecursively(directory, function)
}
public fun findRecursively(rootDir: File, directories: List<File>,
function: Function1<String, Boolean>): List<String> {
return KFiles.findRecursively(rootDir, directories, function)
}
public fun saveFile(file: File, bytes: ByteArray) {
file.parentFile.mkdirs()
val os = file.outputStream()
try {
os.write(bytes)
} finally {
os.close()
}
}
}

View file

@ -0,0 +1,84 @@
package com.beust.kobalt.misc
import com.beust.kobalt.maven.KobaltException
import com.google.inject.Provides
import java.util.concurrent.*
import javax.inject.Singleton
import kotlin.properties.Delegates
class NamedThreadFactory(val n: String) : ThreadFactory {
private val PREFIX = "K-"
public val name: String
get() = PREFIX + n
override
public fun newThread(r: Runnable) : Thread {
val result = Thread(r)
result.setName(name + "-" + result.getId())
return result
}
}
class KobaltExecutor(name: String, threadCount: Int)
: KobaltLogger, ThreadPoolExecutor(threadCount, threadCount, 5L, TimeUnit.SECONDS,
LinkedBlockingQueue<Runnable>(), NamedThreadFactory(name)) {
override protected fun afterExecute(r: Runnable, t: Throwable?) {
super<ThreadPoolExecutor>.afterExecute(r, t)
var ex : Throwable? = null
if (t == null && r is Future<*>) {
try {
if (r.isDone()) r.get();
} catch (ce: CancellationException) {
ex = ce;
} catch (ee: ExecutionException) {
ex = ee.getCause();
} catch (ie: InterruptedException) {
Thread.currentThread().interrupt(); // ignore/reset
}
}
if (ex != null) {
error(if (ex.getMessage() != null) ex.getMessage()!! else ex.javaClass.toString())
}
}
}
public class KobaltExecutors : KobaltLogger {
public fun newExecutor(name: String, threadCount: Int) : ExecutorService
= KobaltExecutor(name, threadCount)
var dependencyExecutor = newExecutor("Dependency", 5)
public fun shutdown() {
dependencyExecutor.shutdown()
}
public fun <T> completionService(name: String, threadCount: Int,
maxMs: Long, tasks: List<Callable<T>>) : List<T> {
val result = arrayListOf<T>()
val executor = newExecutor(name, threadCount)
val cs = ExecutorCompletionService<T>(executor)
tasks.map { cs.submit(it) }
var remainingMs = maxMs
var i = 0
while (i < tasks.size() && remainingMs >= 0) {
var start = System.currentTimeMillis()
val r = cs.take().get(remainingMs, TimeUnit.MILLISECONDS)
result.add(r)
remainingMs -= (System.currentTimeMillis() - start)
log(2, "Received ${r}, remaining: ${remainingMs} ms")
i++
}
if (remainingMs < 0) {
warn("Didn't receive all the results in time: ${i} / ${tasks.size()}")
} else {
log(2, "Received all results in ${maxMs - remainingMs} ms")
}
executor.shutdown()
return result
}
}

View file

@ -0,0 +1,48 @@
package com.beust.kobalt.misc
import org.slf4j.Logger
import org.slf4j.LoggerFactory
public interface KobaltLogger {
val logger : Logger
get() = LoggerFactory.getLogger(javaClass.getSimpleName())
// private fun log(method: Function1<String, Void>, message: String) =
// method.invoke(message)
companion object {
public var LOG_LEVEL : Int = 1
fun log(level: Int, s: String) {
if (level <= LOG_LEVEL) {
LoggerFactory.getLogger(KobaltLogger::class.java.getSimpleName()).info(s)
}
}
fun warn(s: String, e: Throwable? = null) {
LoggerFactory.getLogger(KobaltLogger::class.java.getSimpleName()).warn(s, e)
}
}
final fun log(level: Int = 1, message: String) {
// Compiler crashing if I use LOG_LEVEL here
// Caused by: java.lang.VerifyError: Bad invokespecial instruction: current class isn't
// assignable to reference class.
if (level <= LOG_LEVEL) {
logger.info(message)
}
}
final fun debug(message: String) {
logger.debug(message)
}
final fun error(message: String, e: Throwable? = null) {
logger.error("***** ${message}", e)
}
final fun warn(message: String, e: Throwable? = null) {
logger.warn(message, e)
}
}

View file

@ -0,0 +1,67 @@
package com.beust.kobalt.misc
import com.beust.kobalt.kotlin.ScriptCompiler
import com.beust.kobalt.maven.ArtifactFetcher
import com.beust.kobalt.maven.LocalRepo
import com.beust.kobalt.maven.Pom
import com.beust.kobalt.maven.PomGenerator
import com.beust.kobalt.plugin.publish.JCenterApi
import com.google.inject.AbstractModule
import com.google.inject.BindingAnnotation
import com.google.inject.TypeLiteral
import com.google.inject.assistedinject.FactoryModuleBuilder
import java.lang.annotation.RetentionPolicy
import java.util.concurrent.ExecutorService
//@Singleton
//class TaskManagerProvider @Inject constructor(val plugins: Plugins) : Provider<TaskManager> {
// override fun get(): TaskManager? {
// return TaskManager(plugins)
// }
//}
@BindingAnnotation
@Retention(AnnotationRetention.RUNTIME)
annotation class DependencyExecutor
public open class MainModule : AbstractModule() {
val executors = KobaltExecutors()
open fun configureTest() {
bind(LocalRepo::class.java)
}
override fun configure() {
configureTest()
val builder = FactoryModuleBuilder()
arrayListOf(
PomGenerator.IFactory::class.java,
JCenterApi.IFactory::class.java,
Pom.IFactory::class.java,
ScriptCompiler.IFactory::class.java,
ArtifactFetcher.IFactory::class.java)
.forEach {
install(builder.build(it))
}
// bind(javaClass<TaskManager>()).toProvider(javaClass<TaskManagerProvider>())
// .`in`(Scopes.SINGLETON)
bind(object: TypeLiteral<KobaltExecutors>() {}).toInstance(executors)
bind(object: TypeLiteral<ExecutorService>() {}).annotatedWith(DependencyExecutor::class.java)
.toInstance(executors.dependencyExecutor)
// bindListener(Matchers.any(), object: TypeListener {
// override fun <I> hear(typeLiteral: TypeLiteral<I>?, typeEncounter: TypeEncounter<I>?) {
// val bean = object: InjectionListener<I> {
// override public fun afterInjection(injectee: I) {
// if (Scopes.isCircularProxy(injectee)) {
// println("CYCLE: " + typeLiteral?.getRawType()?.getName());
// }
// }
// }
// typeEncounter?.register(bean)
// }
// })
}
}

View file

@ -0,0 +1,26 @@
package com.beust.kobalt.misc
public data class Node<T>(val value: T) {
val children = arrayListOf<Node<T>>()
var parent: Node<T>? = null
public fun addChildren(values: List<Node<T>>) {
values.forEach {
it.parent = this
children.add(it)
}
}
private fun p(s: String) {
println(s)
}
public fun dump(r: T, children: List<Node<T>>, indent: Int) {
p(" ".repeat(indent) + r)
children.forEach { dump(it.value, it.children, indent + 2) }
}
public fun dump(indent: Int = 0) {
dump(value, children, indent)
}
}

View file

@ -0,0 +1,31 @@
package com.beust.kobalt.misc
import com.google.common.base.CharMatcher
public class Strings {
companion object {
fun <T> join(separator: String, strings: List<T>) : String {
var result = StringBuffer()
var i = 0
strings.forEach {
if (i++ > 0) {
result.append(separator)
}
result.append(it)
}
return result.toString()
}
fun isEmpty(s: String?): Boolean {
return s == null || s.isEmpty()
}
}
}
/**
* @Return the number of times the given character occurs in the string
*/
public fun String.countChar(c: Char) : Int {
return CharMatcher.`is`(c).countIn(this)
}

View file

@ -0,0 +1,14 @@
package com.beust.kobalt.misc
public class ToString<T>(val name: String, vararg o: T) {
val sb = StringBuffer()
init {
for (i in 0..o.size() - 1 step 2) {
if (i > 0) sb.append(", ")
sb.append(o.get(i).toString() + ":" + o.get(i + 1))
}
}
val s : String get() = "{${name} ${sb}}"
}

View file

@ -0,0 +1,44 @@
package com.beust.kobalt.misc
import com.google.common.collect.ArrayListMultimap
import com.google.common.collect.HashMultimap
import java.util.*
/**
* Sort items topologically. These items need to have overridden hashCode() and equals().
*/
class Topological<T> {
private val dependingOn = ArrayListMultimap.create<T, T>()
fun addEdge(t: T, other: T) {
dependingOn.put(t, other)
}
fun addEdge(t: T, others: Array<out T>) {
dependingOn.putAll(t, others.toArrayList())
}
/**
* @return the Ts sorted topologically.
*/
fun sort(all: ArrayList<T>) : List<T> {
val result = arrayListOf<T>()
var dependMap = HashMultimap.create<T, T>()
dependingOn.keySet().forEach { dependMap.putAll(it, dependingOn.get(it))}
while (all.size() > 0) {
val freeNodes = all.filter {
dependMap.get(it).isEmpty()
}
result.addAll(freeNodes)
all.removeAll(freeNodes)
val newMap = HashMultimap.create<T, T>()
dependMap.keySet().forEach {
val l = dependingOn.get(it)
l.removeAll(freeNodes)
newMap.putAll(it, l)
}
dependMap = newMap
}
return result
}
}

View file

@ -0,0 +1,34 @@
package com.beust.kobalt.misc
import com.google.common.base.CharMatcher
public class Versions {
companion object {
/**
* Turn "6.9.4" into 600090004
*/
public fun toLongVersion(version: String) : Long {
val count = version.countChar('.')
val normalizedVersion = if (count == 2) version else if (count == 1) version + ".0"
else version + ".0.0"
fun parseLong(s: String, radix: Int) : Long {
try {
return java.lang.Long.parseLong(s, radix)
} catch(ex: NumberFormatException) {
KobaltLogger.warn("Couldn't parse version \"${version}\"")
return 0L
}
}
return normalizedVersion
.split(".")
.take(3)
.map {
val s = CharMatcher.inRange('0', '9').or(CharMatcher.`is`('.')).retainFrom(it)
parseLong(s, 10)
}
.fold(0L, { n, s -> s + n * 10000 })
}
}
}

View file

@ -0,0 +1,23 @@
package com.beust.kobalt.plugin
import com.beust.kobalt.Plugins
import com.beust.kobalt.api.BasePlugin
import com.beust.kobalt.api.Dependencies
import com.beust.kobalt.api.Project
import com.beust.kobalt.api.annotation.Directive
import com.beust.kobalt.api.annotation.Task
import com.beust.kobalt.internal.TaskResult
import com.beust.kobalt.misc.KobaltLogger
import javax.inject.Singleton
/**
* This plugin is used to gather tasks defined in build files, since these tasks don't really belong to any plugin.
*/
@Singleton
public class DefaultPlugin : BasePlugin(), KobaltLogger {
companion object {
public val NAME = "Default"
}
override val name: String get() = NAME
}

View file

@ -0,0 +1,37 @@
package com.beust.kobalt.plugin.apt
import com.beust.kobalt.Plugins
import com.beust.kobalt.api.BasePlugin
import com.beust.kobalt.api.Dependencies
import com.beust.kobalt.api.Project
import com.beust.kobalt.api.annotation.Directive
import com.beust.kobalt.api.annotation.Task
import com.beust.kobalt.internal.TaskResult
import com.beust.kobalt.misc.KobaltLogger
import javax.inject.Singleton
@Singleton
public class AptPlugin : BasePlugin(), KobaltLogger {
companion object {
public const val TASK_APT: String = "runApt"
}
override val name = "apt"
@Task(name = TASK_APT, description = "Run apt", runBefore = arrayOf("compile"))
fun taskApt(project: Project) : TaskResult {
log(1, "apt called on ${project} with processors ${processors}")
return TaskResult()
}
private val processors = arrayListOf<String>()
fun addApt(dep: String) {
processors.add(dep)
}
}
@Directive
public fun Dependencies.apt(dep: String) {
(Plugins.getPlugin("apt") as AptPlugin).addApt(dep)
}

View file

@ -0,0 +1,24 @@
package com.beust.kobalt.plugin.java
import com.beust.kobalt.api.ICompilerInfo
import com.beust.kobalt.misc.KFiles
import com.google.inject.Singleton
import java.io.File
@Singleton
public class JavaCompilerInfo : ICompilerInfo {
override val name = "java"
override fun findManagedFiles(dir: File) : List<File> {
val result = KFiles.findRecursively(dir, { it.endsWith(".java") })
.map { File(it) }
return result
}
override val defaultSourceDirectories = arrayListOf("src/main/java", "src/main/resources")
override val defaultTestDirectories = arrayListOf("src/test/java", "src/test/resources")
override val directive = "javaProject"
}

View file

@ -0,0 +1,30 @@
package com.beust.kobalt.plugin.java
import java.io.File
abstract public class JavaInfo {
public var javaExecutable: File? = null
get() = findExecutable("java")
public var javacExecutable: File? = null
get() = findExecutable("javac")
public var javadocExecutable: File? = null
get() = findExecutable("javadoc")
abstract public var javaHome: File?
abstract public var runtimeJar: File?
abstract public var toolsJar: File?
abstract public fun findExecutable(command: String) : File
companion object {
fun create(javaBase: File?): Jvm {
val vendor = System.getProperty("java.vm.vendor")
if (vendor.toLowerCase().startsWith("apple inc.")) {
return AppleJvm(OperatingSystem.Companion.current(), javaBase!!)
}
if (vendor.toLowerCase().startsWith("ibm corporation")) {
return IbmJvm(OperatingSystem.Companion.current(), javaBase!!)
}
return Jvm(OperatingSystem.Companion.current(), javaBase)
}
}
}

View file

@ -0,0 +1,138 @@
package com.beust.kobalt.plugin.java
import com.beust.kobalt.api.ICompilerInfo
import com.beust.kobalt.api.Kobalt
import com.beust.kobalt.api.Project
import com.beust.kobalt.api.annotation.Directive
import com.beust.kobalt.api.annotation.Task
import com.beust.kobalt.internal.JvmCompilerPlugin
import com.beust.kobalt.internal.TaskResult
import com.beust.kobalt.maven.*
import com.beust.kobalt.misc.KFiles
import com.beust.kobalt.misc.KobaltExecutors
import com.beust.kobalt.misc.KobaltLogger
import java.io.File
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
public class JavaPlugin @Inject constructor(
override val localRepo: LocalRepo,
override val files: KFiles,
override val depFactory: DepFactory,
override val dependencyManager: DependencyManager,
override val executors: KobaltExecutors)
: JvmCompilerPlugin(localRepo, files, depFactory, dependencyManager, executors), KobaltLogger {
init {
Kobalt.registerCompiler(JavaCompilerInfo())
}
companion object {
public const val TASK_COMPILE : String = "compile"
public const val TASK_JAVADOC : String = "javadoc"
public const val TASK_COMPILE_TEST: String = "compileTest"
}
override val name = "java"
override fun accept(project: Project) = project is JavaProject
private fun compilePrivate(project: Project, cpList: List<IClasspathDependency>, sourceFiles: List<String>,
outputDirectory: File): TaskResult {
outputDirectory.mkdirs()
val jvm = JavaInfo.create(File(SystemProperties.javaBase))
val javac = jvm.javacExecutable
val args = arrayListOf(
javac!!.absolutePath,
"-d", outputDirectory.absolutePath)
if (cpList.size() > 0) {
args.add("-classpath")
args.add(cpList.map { it.jarFile.get().absolutePath }.join(File.pathSeparator))
}
args.addAll(sourceFiles)
val pb = ProcessBuilder(args)
pb.directory(File(project.directory))
pb.inheritIO()
// pb.redirectErrorStream(true)
// pb.redirectError(File("/tmp/kobalt-err"))
// pb.redirectOutput(File("/tmp/kobalt-out"))
val line = args.join(" ")
lp(project, "Compiling ${sourceFiles.size()} files with classpath size ${cpList.size()}")
log(2, "Compiling ${project}:\n${line}")
val process = pb.start()
val errorCode = process.waitFor()
return if (errorCode == 0) TaskResult(true, "Compilation succeeded")
else TaskResult(false, "There were errors")
}
@Task(name = TASK_JAVADOC, description = "Run Javadoc")
fun taskJavadoc(project: Project) : TaskResult {
val projectDir = File(project.directory)
val outputDir = File(projectDir,
project.buildDirectory + File.separator + JvmCompilerPlugin.DOCS_DIRECTORY)
outputDir.mkdirs()
val jvm = JavaInfo.create(File(SystemProperties.javaBase))
val javadoc = jvm.javadocExecutable
val sourceFiles = files.findRecursively(projectDir, project.sourceDirectories.map { File(it) })
{ it: String -> it.endsWith(".java") }
.map { File(projectDir, it).absolutePath }
val classpath = calculateClasspath(project.compileDependencies)
val args = arrayListOf(
javadoc!!.absolutePath,
"-classpath", classpath.map { it.jarFile.get().absolutePath }.join(File.pathSeparator),
"-d", outputDir.absolutePath)
args.addAll(sourceFiles)
val pb = ProcessBuilder(args)
pb.directory(File(project.directory))
pb.inheritIO()
val process = pb.start()
val errorCode = process.waitFor()
return if (errorCode == 0) TaskResult(true, "Compilation succeeded")
else TaskResult(false, "There were errors")
}
@Task(name = TASK_COMPILE, description = "Compile the project")
fun taskCompile(project: Project) : TaskResult {
copyResources(project, JvmCompilerPlugin.SOURCE_SET_MAIN)
val projectDir = File(project.directory)
val buildDir = File(projectDir,
project.buildDirectory + File.separator + "classes")
val sourceFiles = files.findRecursively(projectDir, project.sourceDirectories.map { File(it) })
{ it: String -> it.endsWith(".java") }
.map { File(projectDir, it).absolutePath }
val classpath = calculateClasspath(project.compileDependencies)
return compilePrivate(project, classpath, sourceFiles, buildDir)
}
@Task(name = TASK_COMPILE_TEST, description = "Compile the tests", runAfter = arrayOf("compile"))
fun taskCompileTest(project: Project): TaskResult {
copyResources(project, JvmCompilerPlugin.SOURCE_SET_TEST)
val projectDir = File(project.directory)
val absoluteSourceFiles = files.findRecursively(projectDir, project.sourceDirectoriesTest.map { File(it) })
{ it: String -> it.endsWith(".java") }
.map { File(projectDir, it).absolutePath }
return compilePrivate(project,
testDependencies(project),
absoluteSourceFiles,
makeOutputTestDir(project))
}
}
@Directive
public fun javaProject(init: JavaProject.() -> Unit): JavaProject {
val pd = JavaProject()
pd.init()
return pd
}

View file

@ -0,0 +1,31 @@
package com.beust.kobalt.plugin.java
import com.beust.kobalt.api.Dependencies
import com.beust.kobalt.api.Project
import com.beust.kobalt.api.annotation.Directive
import com.beust.kobalt.misc.KFiles
import com.beust.kobalt.misc.ToString
import java.io.File
public class JavaProject(
@Directive
override var name: String? = null,
@Directive
override var version: String? = null,
/** The absolute directory location of this project */
@Directive
override var directory: String = ".",
/** The build directory, relative to the project directory */
@Directive
override var buildDirectory: String? = "kobaltBuild",
@Directive
override var group: String? = null,
@Directive
override var artifactId: String? = null,
@Directive
override var dependencies: Dependencies? = null)
: Project(name, version, directory, buildDirectory, group, artifactId, dependencies, ".java", JavaCompilerInfo()) {
override public fun toString() = ToString("JavaProject", name!!, "name").s
}

View file

@ -0,0 +1,158 @@
package com.beust.kobalt.plugin.java
import com.beust.kobalt.misc.KobaltLogger
import com.beust.kobalt.maven.KobaltException
import java.io.File
import java.io.IOException
import java.util.HashMap
public open class Jvm constructor(
val os: com.beust.kobalt.plugin.java.OperatingSystem,
var javaBase: File? = null) : JavaInfo(), KobaltLogger {
private var _javaHome: File? = null
override public var javaHome: File? = null
get() = _javaHome!!
override public var runtimeJar: File? = null
private fun findRuntimeJar() : File? {
var runtimeJar = File(javaBase, "lib/rt.jar")
if (runtimeJar.exists()) {
return runtimeJar
}
runtimeJar = File(javaBase, "jre/lib/rt.jar")
return if (runtimeJar.exists()) runtimeJar else null
}
override public var toolsJar: File? = null
private var userSupplied: Boolean? = false
private var javaVersion: String? = null
init {
if (javaBase == null) {
//discover based on what's in the sys. property
try {
javaBase = File(System.getProperty("java.home")).getCanonicalFile()
} catch (e: IOException) {
throw KobaltException(e)
}
_javaHome = findJavaHome(javaBase!!)
javaVersion = SystemProperties.Companion.javaVersion
userSupplied = false
} else {
//precisely use what the user wants and validate strictly further on
_javaHome = javaBase!!
userSupplied = true
javaVersion = null
}
toolsJar = findToolsJar(javaBase!!)
runtimeJar = findRuntimeJar()
}
private fun findJavaHome(javaBase: File): File {
val toolsJar = findToolsJar(javaBase)
if (toolsJar != null) {
return toolsJar.getParentFile().getParentFile()
} else if (javaBase.getName().equals("jre", true) && File(javaBase.getParentFile(),
"bin/java").exists()) {
return javaBase.getParentFile()
} else {
return javaBase
}
}
private fun findToolsJar(jh: File): File? {
javaHome = jh
var toolsJar = File(javaHome, "lib/tools.jar")
if (toolsJar.exists()) {
return toolsJar
}
if (javaHome!!.getName().equals("jre", true)) {
javaHome = javaHome!!.parentFile
toolsJar = File(javaHome, "lib/tools.jar")
if (toolsJar.exists()) {
return toolsJar
}
}
if (os.isWindows()) {
val version = SystemProperties.Companion.javaVersion
if (javaHome!!.name.toRegex().matches("jre\\d+")
|| javaHome!!.getName() == "jre${version}") {
javaHome = File(javaHome!!.parentFile, "jdk${version}")
toolsJar = File(javaHome, "lib/tools.jar")
if (toolsJar.exists()) {
return toolsJar
}
}
}
return null
}
// open public fun isIbmJvm(): Boolean {
// return false
// }
override public fun findExecutable(command: String): File {
val exec = File(javaHome, "bin/" + command)
val executable = java.io.File(os.getExecutableName(exec.getAbsolutePath()))
if (executable.isFile()) {
return executable
}
// if (userSupplied) {
// //then we want to validate strictly
// throw JavaHomeException(String.format("The supplied javaHome seems to be invalid." + " I cannot find the %s executable. Tried location: %s", command, executable.getAbsolutePath()))
// }
val pathExecutable = os.findInPath(command)
if (pathExecutable != null) {
log(1, "Unable to find the ${command} executable using home: " +
"%{javaHome}. We found it on the PATH: ${pathExecutable}.")
return pathExecutable
}
warn("Unable to find the ${command} executable. Tried the java home: ${javaHome}" +
" and the PATH. We will assume the executable can be ran in the current " +
"working folder.")
return java.io.File(os.getExecutableName(command))
}
}
class AppleJvm : Jvm {
override var runtimeJar: File? = File(javaHome!!.getParentFile(), "Classes/classes.jar")
override var toolsJar: File? = File(javaHome!!.getParentFile(), "Classes/tools.jar")
constructor(os: OperatingSystem) : super(os) {
}
constructor(current: OperatingSystem, javaHome: File) : super(current, javaHome) {
}
/**
* {@inheritDoc}
*/
// fun getInheritableEnvironmentVariables(envVars: Map<String, *>): Map<String, *> {
// val vars = HashMap<String, Any>()
// for (entry in envVars.entrySet()) {
// if (entry.getKey().toRegex().matches("APP_NAME_\\d+") ||
// entry.getKey().toRegex().matches("JAVA_MAIN_CLASS_\\d+")) {
// continue
// }
// vars.put(entry.getKey(), entry.getValue())
// }
// return vars
// }
}
class IbmJvm(os: OperatingSystem, suppliedJavaBase: File) : Jvm(os, suppliedJavaBase) {
override var runtimeJar: File? = throw IllegalArgumentException("Not implemented")
override var toolsJar: File? = throw IllegalArgumentException("Not implemented")
// override fun isIbmJvm(): Boolean {
// return true
// }
}

View file

@ -0,0 +1,304 @@
package com.beust.kobalt.plugin.java
import java.io.File
import java.util.*
import java.util.regex.Pattern
public abstract class OperatingSystem {
override fun toString(): String {
return getName() + " " + getVersion() + " " + System.getProperty("os.arch")
}
public fun getName(): String {
return System.getProperty("os.name")
}
public fun getVersion(): String {
return System.getProperty("os.version")
}
public open fun isWindows(): Boolean {
return false
}
public open fun isUnix(): Boolean {
return false
}
public open fun isMacOsX(): Boolean {
return false
}
public open fun isLinux(): Boolean {
return false
}
public abstract fun getNativePrefix(): String
public abstract fun getScriptName(scriptPath: String): String
public abstract fun getExecutableName(executablePath: String): String
public abstract fun getSharedLibraryName(libraryName: String): String
public abstract fun getStaticLibraryName(libraryName: String): String
public abstract fun getFamilyName(): String
/**
* Locates the given executable in the system path. Returns null if not found.
*/
public fun findInPath(name: String): File? {
val exeName = getExecutableName(name)
if (exeName.contains(File.separator)) {
val candidate = File(exeName)
if (candidate.isFile()) {
return candidate
}
return null
}
for (dir in getPath()) {
val candidate = File(dir, exeName)
if (candidate.isFile()) {
return candidate
}
}
return null
}
public fun findAllInPath(name: String): List<File> {
val all = LinkedList<File>()
for (dir in getPath()) {
val candidate = File(dir, name)
if (candidate.isFile()) {
all.add(candidate)
}
}
return all
}
public fun getPath(): List<File> {
val path = System.getenv(getPathVar()) ?: return emptyList<File>()
val entries = ArrayList<File>()
for (entry in path.split(Pattern.quote(File.pathSeparator))) {
entries.add(File(entry))
}
return entries
}
public open fun getPathVar(): String {
return "PATH"
}
class Windows : OperatingSystem() {
override fun isWindows(): Boolean {
return true
}
override fun getFamilyName(): String {
return "windows"
}
override fun getScriptName(scriptPath: String): String {
return withSuffix(scriptPath, ".bat")
}
override fun getExecutableName(executablePath: String): String {
return withSuffix(executablePath, ".exe")
}
override fun getSharedLibraryName(libraryName: String): String {
return withSuffix(libraryName, ".dll")
}
override fun getStaticLibraryName(libraryName: String): String {
return withSuffix(libraryName, ".lib")
}
override fun getNativePrefix(): String {
var arch = System.getProperty("os.arch")
if ("i386" == arch) {
arch = "x86"
}
return "win32-" + arch
}
private fun withSuffix(executablePath: String, extension: String): String {
if (executablePath.toLowerCase().endsWith(extension)) {
return executablePath
}
return removeExtension(executablePath) + extension
}
private fun removeExtension(executablePath: String): String {
val fileNameStart = Math.max(executablePath.lastIndexOf('/'), executablePath.lastIndexOf('\\'))
val extensionPos = executablePath.lastIndexOf('.')
if (extensionPos > fileNameStart) {
return executablePath.substring(0, extensionPos)
}
return executablePath
}
override fun getPathVar(): String {
return "Path"
}
}
open class Unix : OperatingSystem() {
override fun getScriptName(scriptPath: String): String {
return scriptPath
}
override fun getFamilyName(): String {
return "unknown"
}
override fun getExecutableName(executablePath: String): String {
return executablePath
}
override fun getSharedLibraryName(libraryName: String): String {
return getLibraryName(libraryName, getSharedLibSuffix())
}
private fun getLibraryName(libraryName: String, suffix: String): String {
if (libraryName.endsWith(suffix)) {
return libraryName
}
val pos = libraryName.lastIndexOf('/')
if (pos >= 0) {
return libraryName.substring(0, pos + 1) + "lib" + libraryName.substring(pos + 1) + suffix
} else {
return "lib" + libraryName + suffix
}
}
protected open fun getSharedLibSuffix(): String {
return ".so"
}
override fun getStaticLibraryName(libraryName: String): String {
return getLibraryName(libraryName, ".a")
}
override fun isUnix(): Boolean {
return true
}
override fun getNativePrefix(): String {
val arch = getArch()
var osPrefix = getOsPrefix()
osPrefix += "-" + arch
return osPrefix
}
protected open fun getArch(): String {
var arch = System.getProperty("os.arch")
if ("x86" == arch) {
arch = "i386"
}
if ("x86_64" == arch) {
arch = "amd64"
}
if ("powerpc" == arch) {
arch = "ppc"
}
return arch
}
protected open fun getOsPrefix(): String {
var osPrefix = getName().toLowerCase()
val space = osPrefix.indexOf(" ")
if (space != -1) {
osPrefix = osPrefix.substring(0, space)
}
return osPrefix
}
}
class MacOs : Unix() {
override fun isMacOsX(): Boolean {
return true
}
override fun getFamilyName(): String {
return "os x"
}
override fun getSharedLibSuffix(): String {
return ".dylib"
}
override fun getNativePrefix(): String {
return "darwin"
}
}
class Linux : Unix() {
override fun isLinux(): Boolean {
return true
}
override fun getFamilyName(): String {
return "linux"
}
}
class FreeBSD : Unix()
class Solaris : Unix() {
override fun getFamilyName(): String {
return "solaris"
}
override fun getOsPrefix(): String {
return "sunos"
}
override fun getArch(): String {
val arch = System.getProperty("os.arch")
if (arch == "i386" || arch == "x86") {
return "x86"
}
return super.getArch()
}
}
companion object {
public val WINDOWS: Windows = Windows()
public val MAC_OS: MacOs = MacOs()
public val SOLARIS: Solaris = Solaris()
public val LINUX: Linux = Linux()
public val FREE_BSD: FreeBSD = FreeBSD()
public val UNIX: Unix = Unix()
public fun current(): OperatingSystem {
return forName(System.getProperty("os.name"))
}
public fun forName(os: String): OperatingSystem {
val osName = os.toLowerCase()
if (osName.contains("windows")) {
return WINDOWS
} else if (osName.contains("mac os x") || osName.contains("darwin") || osName.contains("osx")) {
return MAC_OS
} else if (osName.contains("sunos") || osName.contains("solaris")) {
return SOLARIS
} else if (osName.contains("linux")) {
return LINUX
} else if (osName.contains("freebsd")) {
return FREE_BSD
} else {
// Not strictly true
return UNIX
}
}
}
}

View file

@ -0,0 +1,16 @@
package com.beust.kobalt.plugin.java
import java.util.concurrent.locks.ReentrantLock
import javax.inject.Inject
public class SystemProperties {
companion object {
val javaBase = System.getenv("JAVA_HOME") ?: throw IllegalArgumentException("JAVA_HOME not defined")
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

@ -0,0 +1,87 @@
package com.beust.kobalt.plugin.kotlin;
import com.beust.kobalt.INJECTOR
import com.beust.kobalt.internal.JvmCompilerPlugin
import com.beust.kobalt.internal.TaskResult
import com.beust.kobalt.maven.*
import com.beust.kobalt.misc.KobaltExecutors
import com.beust.kobalt.misc.KobaltLogger
import org.jetbrains.kotlin.cli.jvm.K2JVMCompiler
import java.io.File
import javax.inject.Inject
import javax.inject.Singleton
import kotlin.properties.Delegates
/**
* @author Cedric Beust <cedric@beust.com>
* @since 08 03, 2015
*/
@Singleton
private class KotlinCompiler @Inject constructor(override val localRepo : LocalRepo,
override val files: com.beust.kobalt.misc.KFiles,
override val depFactory: DepFactory,
override val dependencyManager: DependencyManager,
override val executors: KobaltExecutors)
: JvmCompilerPlugin(localRepo, files, depFactory, dependencyManager, executors), KobaltLogger {
private val KOTLIN_VERSION = "0.14.449"
override val name = "kotlin"
private fun getKotlinCompilerJar(name: String) : String {
return com.beust.kobalt.misc.KFiles.joinDir(localRepo.toFullPath(""),
File("org/jetbrains/kotlin/${name}").path,
File("${KOTLIN_VERSION}/${name}-${KOTLIN_VERSION}.jar").getPath())
}
fun compile(compileDependencies: List<IClasspathDependency>, otherClasspath: List<String>,
source: List<String>, output: String, args: List<String>) : TaskResult {
val executor = executors.newExecutor("KotlinCompiler", 10)
val compilerDep = depFactory.create("org.jetbrains.kotlin:kotlin-compiler-embeddable:${KOTLIN_VERSION}",
executor)
val deps = compilerDep.transitiveDependencies(executor)
deps.forEach { it.jarFile.get() }
val classpathList = arrayListOf(
getKotlinCompilerJar("kotlin-stdlib"),
getKotlinCompilerJar("kotlin-compiler-embeddable"))
classpathList.addAll(otherClasspath)
classpathList.addAll(calculateClasspath(compileDependencies).map { it.id })
log(2, "Compiling ${source.size()} files with classpath:\n " + classpathList.join("\n "))
K2JVMCompiler.main(arrayOf(
"-d", output,
"-classpath", classpathList.join(File.pathSeparator), *source.toTypedArray(),
*args.toTypedArray()))
executor.shutdown()
return TaskResult()
}
}
class KConfiguration @Inject constructor(val compiler: KotlinCompiler){
val classpath = arrayListOf<String>()
val dependencies = arrayListOf<IClasspathDependency>()
var source = arrayListOf<String>()
var output: String by Delegates.notNull()
val args = arrayListOf<String>()
fun sourceFiles(s: String) = source.add(s)
fun sourceFiles(s: List<String>) = source.addAll(s)
fun classpath(s: String) = classpath.add(s)
fun classpath(s: List<String>) = classpath.addAll(s)
fun compilerArgs(s: List<String>) = args.addAll(s)
public fun compile() : TaskResult {
return compiler.compile(dependencies, classpath, source, output, args)
}
}
fun kotlinCompilePrivate(ini: KConfiguration.() -> Unit) : KConfiguration {
val result = INJECTOR.getInstance(KConfiguration::class.java)
result.ini()
return result
}

View file

@ -0,0 +1,22 @@
package com.beust.kobalt.plugin.kotlin
import com.beust.kobalt.api.ICompilerInfo
import com.beust.kobalt.misc.KFiles
import java.io.File
public class KotlinCompilerInfo : ICompilerInfo {
override val name = "kotlin"
override fun findManagedFiles(dir: File): List<File> {
val result = KFiles.findRecursively(dir, { it.endsWith(".kt") })
.map { File(it) }
return result
}
override val defaultSourceDirectories = arrayListOf("src/main/kotlin", "src/main/resources")
override val defaultTestDirectories = arrayListOf("src/test/kotlin", "src/test/resources")
override val directive = "javaProject"
}

View file

@ -0,0 +1,123 @@
package com.beust.kobalt.plugin.kotlin
import com.beust.kobalt.Plugins
import com.beust.kobalt.api.ICompilerInfo
import com.beust.kobalt.api.Kobalt
import com.beust.kobalt.api.Project
import com.beust.kobalt.api.annotation.Directive
import com.beust.kobalt.api.annotation.Task
import com.beust.kobalt.internal.JvmCompilerPlugin
import com.beust.kobalt.internal.TaskResult
import com.beust.kobalt.maven.*
import com.beust.kobalt.misc.KobaltExecutors
import com.beust.kobalt.misc.KobaltLogger
import com.beust.kobalt.plugin.java.JavaProject
import java.io.File
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
public class KotlinPlugin @Inject constructor(
override val localRepo: LocalRepo,
override val files: com.beust.kobalt.misc.KFiles,
override val depFactory: DepFactory,
override val dependencyManager: DependencyManager,
override val executors: KobaltExecutors)
: JvmCompilerPlugin(localRepo, files, depFactory, dependencyManager, executors), KobaltLogger {
init {
Kobalt.registerCompiler(KotlinCompilerInfo())
}
companion object {
public const val TASK_COMPILE: String = "compile"
public const val TASK_COMPILE_TEST: String = "compileTest"
}
override val name = "kotlin"
override fun accept(project: Project) = project is KotlinProject
private val compilerArgs = arrayListOf<String>()
@Task(name = TASK_COMPILE, description = "Compile the project")
fun taskCompile(project: Project): TaskResult {
copyResources(project, JvmCompilerPlugin.SOURCE_SET_MAIN)
val classpath = calculateClasspath(project.compileDependencies)
val projectDirectory = java.io.File(project.directory)
val buildDirectory = File(projectDirectory, project.buildDirectory + File.separator + "classes")
buildDirectory.mkdirs()
val sourceFiles = files.findRecursively(projectDirectory,
project.sourceDirectories.map { File(it) }, { it.endsWith(".kt") })
val absoluteSourceFiles = sourceFiles.map {
File(projectDirectory, it).absolutePath
}
compilePrivate(classpath, absoluteSourceFiles, buildDirectory.getAbsolutePath())
lp(project, "Compilation succeeded")
return TaskResult()
}
fun addCompilerArgs(vararg args: String) {
compilerArgs.addAll(args)
}
@Task(name = TASK_COMPILE_TEST, description = "Compile the tests", runAfter = arrayOf(TASK_COMPILE))
fun taskCompileTest(project: Project): TaskResult {
copyResources(project, JvmCompilerPlugin.SOURCE_SET_TEST)
val projectDir = File(project.directory)
val absoluteSourceFiles = files.findRecursively(projectDir, project.sourceDirectoriesTest.map { File(it) })
{ it: String -> it.endsWith(".kt") }
.map { File(projectDir, it).getAbsolutePath() }
compilePrivate(testDependencies(project),
absoluteSourceFiles,
makeOutputTestDir(project).absolutePath)
lp(project, "Compilation of tests succeeded")
return TaskResult()
}
private fun compilePrivate(cpList: List<IClasspathDependency>, sources: List<String>,
outputDirectory: String): TaskResult {
File(outputDirectory).mkdirs()
// lp(project, "Compiling ${sources.size()} files with classpath size ${cpList.size()}")
return kotlinCompilePrivate {
classpath(cpList.map { it.jarFile.get().absolutePath })
sourceFiles(sources)
compilerArgs(compilerArgs)
output = outputDirectory
}.compile()
}
}
/**
* @param project: the list of projects that need to be built before this one.
*/
@Directive
public fun kotlinProject(vararg project: Project, init: KotlinProject.() -> Unit): KotlinProject {
with(KotlinProject()) {
init()
Kobalt.declareProjectDependencies(this, project)
return this
}
}
class KotlinCompilerConfig {
fun args(vararg options: String) {
(Plugins.getPlugin("kotlin") as KotlinPlugin).addCompilerArgs(*options)
}
}
@Directive
fun kotlinCompiler(init: KotlinCompilerConfig.() -> Unit) : KotlinCompilerConfig {
with (KotlinCompilerConfig()) {
init()
return this
}
}

View file

@ -0,0 +1,32 @@
package com.beust.kobalt.plugin.kotlin
import com.beust.kobalt.api.Dependencies
import com.beust.kobalt.api.ICompilerInfo
import com.beust.kobalt.api.Project
import com.beust.kobalt.api.annotation.Directive
import com.beust.kobalt.misc.KFiles
import com.beust.kobalt.misc.ToString
import java.io.File
public class KotlinProject(
@Directive
override var name: String? = null,
@Directive
override var version: String? = null,
/** The absolute directory location of this project */
@Directive
override var directory: String = ".",
/** The build directory, relative to the project directory */
@Directive
override var buildDirectory: String? = "kobaltBuild",
@Directive
override var group: String? = null,
@Directive
override var artifactId: String? = name,
@Directive
override var dependencies: Dependencies? = null)
: Project(name, version, directory, buildDirectory, group, artifactId, dependencies, ".kt",
KotlinCompilerInfo()) {
override public fun toString() = ToString("KotlinProject", "name", name!!).s
}

View file

@ -0,0 +1,167 @@
package com.beust.kobalt.plugin.packaging
import com.beust.kobalt.IFileSpec
import com.beust.kobalt.misc.KobaltLogger
import java.io.*
import java.util.jar.JarEntry
import java.util.jar.JarFile
import java.util.jar.JarInputStream
import java.util.jar.JarOutputStream
import java.util.zip.ZipEntry
import java.util.zip.ZipOutputStream
public class JarUtils : KobaltLogger {
companion object {
// private fun isExcluded(entryName: String) : Boolean {
// val isAuth = entryName.startsWith("META-INF") and (
// entryName.endsWith(".SF") or entryName.endsWith(".DSA") or entryName.endsWith("RSA"))
// val isSource = entryName.endsWith(".java")
// return isAuth or isSource
// }
/**
* Add the content of a jar file to another jar file.
*/
// private fun addJarFile(inFile: File, outStream: ZipOutputStream, seen: HashSet<String>) {
// val inJarFile = JarFile(inFile)
// val stream = JarInputStream(FileInputStream(inFile))
// var entry = stream.getNextEntry()
// // Quick and dirty benchmarks to assess the impact of the byte array size (milliseconds):
// // 10000: 7883 7873
// // 50000: 7947
// // 20000: 7858 7737 7730
// // 10000: 7939 7924
// // Probably need to do this more formally but it doesn't seem to matter that much
// val buf = ByteArray(20000)
//
// while (entry != null) {
// if (! entry.isDirectory()) {
// val entryName = entry.getName()
// if (! seen.contains(entryName) && ! isExcluded(entryName)) {
// seen.add(entryName)
// outStream.putNextEntry(entry)
// val zis = inJarFile.getInputStream(entry)
//
// var len = zis.read(buf)
// while (len >= 0) {
// outStream.write(buf, 0, len);
// len = zis.read(buf)
// }
// }
// }
// entry = stream.getNextJarEntry()
// }
//
// stream.close()
// }
val defaultHandler: (Exception) -> Unit = { ex: Exception ->
// Ignore duplicate entry exceptions
if (! ex.getMessage()?.contains("duplicate")!!) {
throw ex
}
}
public fun addFiles(directory: String, files: List<IncludedFile>, target: ZipOutputStream,
expandJarFiles: Boolean,
onError: (Exception) -> Unit = defaultHandler) {
files.forEach {
addSingleFile(directory, it, target, expandJarFiles, onError)
}
}
public fun addSingleFile(directory: String, file: IncludedFile, outputStream: ZipOutputStream,
expandJarFiles: Boolean, onError: (Exception) -> Unit = defaultHandler) {
file.specs.forEach { spec ->
val fromPath = (file.from + "/" + spec).replace("\\", "/")
val path = spec.toString()
spec.toFiles(directory).forEach { source ->
// Remove the "from" from the path
// val path = fixedPath.substring(file.from.length())
if (source.isDirectory) {
// Directory
var name = path
if (!name.isEmpty()) {
if (!name.endsWith("/")) name += "/"
val entry = JarEntry(name)
entry.time = source.lastModified()
outputStream.putNextEntry(entry)
outputStream.closeEntry()
}
val fileSpecs: List<IFileSpec> = source.listFiles().map { IFileSpec.FileSpec(it.name) }
val subFiles = IncludedFile(From(file.from), To(file.to), fileSpecs)
addSingleFile(directory, subFiles, outputStream, expandJarFiles)
} else {
if (expandJarFiles and source.name.endsWith(".jar")) {
KobaltLogger.log(2, "Writing contents of jar file ${source}")
val stream = JarInputStream(FileInputStream(source))
var entry = stream.nextEntry
while (entry != null) {
if (!entry.isDirectory) {
val ins = JarFile(source).getInputStream(entry)
addEntry(ins, JarEntry(entry), outputStream, path, onError)
}
entry = stream.nextEntry
}
} else {
val entry = JarEntry((file.to + path).replace("\\", "/"))
entry.time = source.lastModified()
val entryFile = File(directory, fromPath)
if (! entryFile.exists()) {
throw AssertionError("File should exist: ${entryFile}")
}
addEntry(FileInputStream(entryFile), entry, outputStream, path, onError)
}
}
}
}
}
private fun addEntry(inputStream: InputStream, entry: ZipEntry, outputStream: ZipOutputStream,
path: String,
onError: (Exception) -> Unit = defaultHandler) {
// This jar file is not shaded and includes its own copy of guava, so don't add the guava
// files from there
// if (entry.name.contains("com/google/common") && path.contains("kotlin-compiler")) {
// return
// }
var bis: BufferedInputStream? = null
try {
outputStream.putNextEntry(entry)
bis = BufferedInputStream(inputStream)
val buffer = ByteArray(50 * 1024)
while (true) {
val count = bis.read(buffer)
if (count == -1) break
outputStream.write(buffer, 0, count)
}
outputStream.closeEntry()
} catch(ex: Exception) {
onError(ex)
} finally {
bis?.close()
}
}
fun removeDuplicateEntries(fromJarFile: File, toFile: File) {
val fromFile = JarFile(fromJarFile)
var entries = fromFile.entries()
val os = JarOutputStream(FileOutputStream(toFile))
val seen = hashSetOf<String>()
while (entries.hasMoreElements()) {
val entry = entries.nextElement()
if (! seen.contains(entry.name)) {
val ins = fromFile.getInputStream(entry)
addEntry(ins, JarEntry(entry), os, fromFile.name)
}
seen.add(entry.name)
}
os.close()
KobaltLogger.log(1, "Deduplicated $fromFile.name")
}
}
}

View file

@ -0,0 +1,356 @@
package com.beust.kobalt.plugin.packaging
import com.beust.kobalt.IFileSpec.FileSpec
import com.beust.kobalt.IFileSpec.Glob
import com.beust.kobalt.IFileSpec
import com.beust.kobalt.Plugins
import com.beust.kobalt.api.BasePlugin
import com.beust.kobalt.api.Project
import com.beust.kobalt.api.annotation.Directive
import com.beust.kobalt.api.annotation.Task
import com.beust.kobalt.glob
import com.beust.kobalt.internal.JvmCompilerPlugin
import com.beust.kobalt.internal.TaskResult
import com.beust.kobalt.maven.DependencyManager
import com.beust.kobalt.misc.KFiles
import com.beust.kobalt.misc.KobaltExecutors
import com.beust.kobalt.misc.KobaltLogger
import com.beust.kobalt.misc.ToString
import com.beust.kobalt.plugin.java.JavaPlugin
import java.io.File
import java.io.FileOutputStream
import java.io.OutputStream
import java.nio.file.FileSystems
import java.nio.file.PathMatcher
import java.nio.file.Paths
import java.util.ArrayList
import java.util.jar.JarOutputStream
import java.util.zip.ZipOutputStream
import javax.inject.Inject
import javax.inject.Singleton
@Directive
public fun assemble(project: Project, init: Package.(p: Project) -> Unit): Package {
val pd = Package(project)
pd.init(project)
return pd
}
@Singleton
public class PackagingPlugin @Inject constructor(val dependencyManager : DependencyManager,
val executors: KobaltExecutors) : BasePlugin(), KobaltLogger {
companion object {
public const val TASK_ASSEMBLE : String = "assemble"
}
override val name = "packaging"
private val packages = arrayListOf<Package>()
@Task(name = TASK_ASSEMBLE, description = "Package the artifacts", runAfter = arrayOf(JavaPlugin.TASK_COMPILE))
fun taskAssemble(project: Project) : TaskResult {
packages.filter { it.project.name == project.name }.forEach { pkg ->
pkg.jars.forEach { generateJar(pkg.project, it) }
pkg.zips.forEach { generateZip(pkg.project, it) }
}
return TaskResult()
}
private fun isExcluded(file: File, excludes: List<Glob>) : Boolean {
if (excludes.isEmpty()) {
return false
} else {
val ex = arrayListOf<PathMatcher>()
excludes.forEach {
ex.add(FileSystems.getDefault().getPathMatcher("glob:${it.spec}"))
}
ex.forEach {
if (it.matches(Paths.get(file.getName()))) {
log(2, "Excluding ${file}")
return true
}
}
}
return false
}
private fun generateJar(project: Project, jar: Jar) : File {
//
// Add all the applicable files for the current project
//
val buildDir = KFiles.makeDir(project.directory, project.buildDirectory!!)
val allFiles = arrayListOf<IncludedFile>()
val classesDir = KFiles.makeDir(buildDir.getPath(), "classes")
if (jar.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.absolutePath + "/"))
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 { ! isExcluded(it, jar.excludes) }
val fileSpecs = arrayListOf<IFileSpec>()
filesNotExcluded.forEach {
fileSpecs.add(FileSpec(it.path.toString().substring(prefixPath.toString().length() + 1)))
}
allFiles.add(IncludedFile(From(prefixPath.toString() + "/"), To(""), fileSpecs))
} else {
allFiles.addAll(findIncludedFiles(project.directory, jar.includedFiles, jar.excludes))
}
//
// If fatJar is true, add all the transitive dependencies too
//
if (jar.fatJar) {
log(2, "Creating fat jar")
val allDependencies = dependencyManager.transitiveClosure(project.compileDependencies)
allDependencies.map { it.jarFile.get() }.forEach {
if (! isExcluded(it, jar.excludes)) {
allFiles.add(IncludedFile(arrayListOf(FileSpec(it.path))))
}
}
}
//
// Generate the manifest
//
val manifest = java.util.jar.Manifest()//FileInputStream(mf))
jar.attributes.forEach { attribute ->
manifest.mainAttributes.putValue(attribute.first, attribute.second)
}
val jarFactory = { os:OutputStream -> JarOutputStream(os, manifest) }
return generateArchive(project, jar.name, ".jar", allFiles,
true /* expandJarFiles */, jarFactory)
}
private fun findIncludedFiles(directory: String, files: List<IncludedFile>, excludes: List<IFileSpec.Glob>)
: List<IncludedFile> {
val result = arrayListOf<IncludedFile>()
files.forEach { includedFile ->
val includedSpecs = arrayListOf<IFileSpec>()
includedFile.specs.forEach { spec ->
val fromPath = directory + "/" + includedFile.from
if (File(fromPath).exists()) {
spec.toFiles(fromPath).forEach { file ->
if (!File(fromPath, file.path).exists()) {
throw AssertionError("File should exist: ${file}")
}
if (!isExcluded(file, excludes)) {
includedSpecs.add(FileSpec(file.path))
} else {
log(2, "Not adding ${file.path} to jar file because it's excluded")
}
}
} else {
warn("Directory ${fromPath} doesn't exist, not including it in the jar")
}
}
if (includedSpecs.size() > 0) {
log(3, "Including specs ${includedSpecs}")
result.add(IncludedFile(From(includedFile.from), To(includedFile.to), includedSpecs))
}
}
return result
}
private fun generateZip(project: Project, zip: Zip) {
val allFiles = findIncludedFiles(project.directory, zip.includedFiles, zip.excludes)
generateArchive(project, zip.name, ".zip", allFiles)
}
private val DEFAULT_STREAM_FACTORY = { os : OutputStream -> ZipOutputStream(os) }
private fun generateArchive(project: Project, archiveName: String?, suffix: String,
includedFiles: List<IncludedFile>,
expandJarFiles : Boolean = false,
outputStreamFactory: (OutputStream) -> ZipOutputStream = DEFAULT_STREAM_FACTORY) : File {
val buildDir = KFiles.makeDir(project.directory, project.buildDirectory!!)
val archiveDir = KFiles.makeDir(buildDir.path, "libs")
val fullArchiveName = archiveName ?: arrayListOf(project.name!!, project.version!!).join("-") + suffix
val result = File(archiveDir.path, fullArchiveName)
val outStream = outputStreamFactory(FileOutputStream(result))
log(2, "Creating ${result}")
JarUtils.addFiles(project.directory, includedFiles, outStream, expandJarFiles)
log(2, "Added ${includedFiles.size()} files to ${result}")
outStream.flush()
outStream.close()
log(1, "Created ${result}")
return result
}
fun addPackage(p: Package) {
packages.add(p)
}
}
class Package(val project: Project) : AttributeHolder {
val jars = arrayListOf<Jar>()
val zips = arrayListOf<Zip>()
init {
(Plugins.getPlugin("packaging") as PackagingPlugin).addPackage(this)
}
@Directive
fun jar(init: Jar.(p: Jar) -> Unit) : Jar {
val jar = Jar()
jar.init(jar)
jars.add(jar)
return jar
}
@Directive
fun zip(init: Zip.(p: Zip) -> Unit) : Zip {
val zip = Zip()
zip.init(zip)
zips.add(zip)
return zip
}
/**
* Package all the jar files necessary for a maven repo: classes, sources, javadocs.
*/
public fun mavenJars(init: MavenJars.(p: MavenJars) -> Unit) : MavenJars {
val m = MavenJars(this)
m.init(m)
val mainJar = jar {
fatJar = m.fatJar
}
jar {
name = "${project.name}-${project.version}-sources.jar"
project.sourceDirectories.forEach {
include(from(it), to(""), glob("**${project.sourceSuffix}"))
}
}
jar {
name = "${project.name}-${project.version}-javadoc.jar"
include(from(project.buildDirectory + "/" + JvmCompilerPlugin.DOCS_DIRECTORY), to(""), glob("**"))
}
mainJarAttributes.forEach {
mainJar.addAttribute(it.first, it.second)
}
return m
}
val mainJarAttributes = arrayListOf<Pair<String, String>>()
override fun addAttribute(k: String, v: String) {
mainJarAttributes.add(Pair(k, v))
}
class MavenJars(val ah: AttributeHolder, var fatJar: Boolean = false, var manifest: Manifest? = null) :
AttributeHolder by ah {
public fun manifest(init: Manifest.(p: Manifest) -> Unit) : Manifest {
val m = Manifest(this)
m.init(m)
return m
}
}
}
open class Zip(open var name: String? = null) {
// internal val includes = arrayListOf<IFileSpec>()
internal val excludes = arrayListOf<Glob>()
@Directive
public fun from(s: String) = From(s)
@Directive
public fun to(s: String) = To(s)
@Directive
public fun exclude(vararg files: String) {
files.forEach { excludes.add(Glob(it)) }
}
@Directive
public fun exclude(vararg specs: Glob) {
specs.forEach { excludes.add(it) }
}
@Directive
public fun include(vararg files: String) {
includedFiles.add(IncludedFile(files.map { FileSpec(it) }))
}
@Directive
public fun include(from: From, to: To, vararg specs: String) {
includedFiles.add(IncludedFile(from, to, specs.map { FileSpec(it) }))
}
@Directive
public fun include(from: From, to: To, vararg specs: Glob) {
includedFiles.add(IncludedFile(from, to, listOf(*specs)))
}
/**
* 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>()
}
private open class Direction(open val p: String) {
override public fun toString() = path
public val path: String get() = if (p.isEmpty() or p.endsWith("/")) p else p + "/"
}
class From(override val p: String) : Direction(p)
class To(override val p: String) : Direction(p)
class IncludedFile(val fromOriginal: From, val toOriginal: To, val specs: List<IFileSpec>) {
constructor(specs: List<IFileSpec>) : this(From(""), To(""), specs)
public val from: String get() = fromOriginal.path.replace("\\", "/")
public val to: String get() = toOriginal.path.replace("\\", "/")
override public fun toString() = ToString("IncludedFile",
"files", specs.map { it.toString() }.join(", "),
"from", from,
"to", to)
.s
}
interface AttributeHolder {
fun addAttribute(k: String, v: String)
}
/**
* A jar is exactly like a zip with the addition of a manifest and an optional fatJar boolean.
*/
class Jar(override var name: String? = null, var fatJar: Boolean = false) : Zip(name), AttributeHolder {
@Directive
public 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
val attributes = arrayListOf(Pair("Manifest-Version", "1.0"))
override fun addAttribute(k: String, v: String) {
attributes.add(Pair(k, v))
}
}
class Pom {
}
class Manifest(val jar: AttributeHolder) {
@Directive
public fun attributes(k: String, v: String) {
jar.addAttribute(k, v)
}
}

View file

@ -0,0 +1,134 @@
package com.beust.kobalt.plugin.publish
import com.beust.klaxon.*
import com.beust.kobalt.api.Project
import com.beust.kobalt.internal.TaskResult
import com.beust.kobalt.maven.Http
import com.beust.kobalt.maven.KobaltException
import com.beust.kobalt.misc.KobaltLogger
import com.google.inject.assistedinject.Assisted
import com.squareup.okhttp.Response
import org.jetbrains.annotations.Nullable
import java.io.ByteArrayInputStream
import java.io.File
import java.nio.charset.Charset
import javax.inject.Inject
data class JCenterPackage(val jo: JsonObject) {
val latestPublishedVersion = (jo.get("versions") as JsonArray<String>).get(0)
}
open public class UnauthenticatedJCenterApi @Inject constructor(open val http: Http){
companion object {
const val BINTRAY_URL_API = "https://api.bintray.com"
const val BINTRAY_URL_API_CONTENT = BINTRAY_URL_API + "/content"
}
fun parseResponse(response: String) : JsonObject {
return Parser().parse(ByteArrayInputStream(response.toByteArray(Charset.defaultCharset()))) as JsonObject
}
fun getPackage(name: String) : JCenterPackage {
val url = arrayListOf(BINTRAY_URL_API, "packages", "cbeust", "maven", "kobalt").join("/")
val response = http.get(url).getAsString()
val result = parseResponse(response)
return JCenterPackage(result)
}
val kobaltPackage : JCenterPackage
get() = getPackage("kobalt")
}
public class JCenterApi @Inject constructor (@Nullable @Assisted("username") val username: String?,
@Nullable @Assisted("password") val password: String?,
override val http: Http) : UnauthenticatedJCenterApi(http), KobaltLogger {
interface IFactory {
fun create(@Nullable @Assisted("username") username: String?,
@Nullable @Assisted("password") password: String?) : JCenterApi
}
fun packageExists(packageName: String) : Boolean {
val url = arrayListOf(UnauthenticatedJCenterApi.BINTRAY_URL_API, "packages", username!!, "maven", packageName)
.join("/")
val response = http.get(username, password, url).getAsString()
val jo = parseResponse(response)
return jo.string("name") == packageName
}
fun createPackage(packageName: String) : String {
val url = arrayListOf(UnauthenticatedJCenterApi.BINTRAY_URL_API, "packages", username!!, "maven").join("/")
val jo = json {
obj("name" to packageName)
obj("license" to array("Apache 2.0"))
}
return http.post(username, password, url, jo.toJsonString())
}
fun uploadMaven(project: Project, files: List<File>, configuration : JCenterConfiguration?) : TaskResult {
if (! packageExists(project.name!!)) {
throw KobaltException("Couldn't find a package called ${project.name} on bintray, please create one first" +
" as explained at https://bintray.com/docs/usermanual/uploads/uploads_creatinganewpackage.html")
}
val fileToPath: (File) -> String = { f: File ->
arrayListOf(
UnauthenticatedJCenterApi.BINTRAY_URL_API_CONTENT,
username!!,
"maven",
project.name!!,
project.version!!,
project.group!!.replace(".", "/"),
project.artifactId!!,
project.version!!,
f.getName())
.join("/")
}
return upload(files, configuration, fileToPath)
}
fun uploadFile(file: File, url: String, configuration: JCenterConfiguration) =
upload(arrayListOf(file), configuration, {
f: File -> "${UnauthenticatedJCenterApi.BINTRAY_URL_API_CONTENT}/${username}/generic/${url}"
})
private fun upload(files: List<File>, configuration : JCenterConfiguration?, fileToPath: (File) -> String)
: TaskResult {
val successes = arrayListOf<File>()
val failures = hashMapOf<File, String>()
files.forEach {
var path = fileToPath(it)
// Apply the configurations for this project, if any
val options = arrayListOf<String>()
if (configuration?.publish == true) options.add("publish=1")
// This actually needs to be done
// options.add("list_in_downloads=1")
path += "?" + options.join("&")
http.uploadFile(username, password, path, it,
{ r: Response -> successes.add(it) },
{ r: Response ->
val jo = parseResponse(r.body().string())
failures.put(it, jo.string("message") ?: "No message found")
})
}
val result: TaskResult
if (successes.size() == files.size()) {
log(1, "All artifacts successfully uploaded")
result = TaskResult(true)
} else {
result = TaskResult(false, failures.values().join(" "))
error("Failed to upload ${failures.size()} files:")
failures.forEach { k, v ->
error(" - ${k} : ${v}")
}
}
return result
}
}

View file

@ -0,0 +1,128 @@
package com.beust.kobalt.plugin.publish
import com.beust.klaxon.string
import com.beust.kobalt.Plugins
import com.beust.kobalt.api.BasePlugin
import com.beust.kobalt.api.Project
import com.beust.kobalt.api.annotation.Directive
import com.beust.kobalt.api.annotation.Task
import com.beust.kobalt.internal.TaskResult
import com.beust.kobalt.maven.Http
import com.beust.kobalt.maven.KobaltException
import com.beust.kobalt.misc.KobaltLogger
import com.google.common.base.Preconditions
import org.jetbrains.kotlin.utils.sure
import java.io.File
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
public class PublishPlugin @Inject constructor(val http: Http, val files: com.beust.kobalt.misc.KFiles,
val factory: com.beust.kobalt.maven.PomGenerator.IFactory,
val jcenterFactory: JCenterApi.IFactory)
: BasePlugin(), KobaltLogger {
override val name = "publish"
companion object {
private const val TASK_UPLOAD_JCENTER = "uploadJcenter"
private const val TASK_GENERATE_POM = "generatePom"
private const val PROPERTY_BINTRAY_USER = "bintray.user"
private const val PROPERTY_BINTRAY_PASSWORD = "bintray.apikey"
}
@Task(name = TASK_GENERATE_POM, description = "Generate the .pom file", runAfter = arrayOf("assemble"))
fun taskGeneratePom(project: Project): TaskResult {
factory.create(project).generate()
return TaskResult()
}
private fun validateProject(project: Project) {
Preconditions.checkNotNull(project.name, "Project ${project} should have a name")
Preconditions.checkNotNull(project.version, "Project ${project} should have a version")
Preconditions.checkNotNull(project.group, "Project ${project} should have a group")
Preconditions.checkNotNull(project.artifactId, "Project ${project} should have a artifactId")
}
private val VALID = arrayListOf(".jar", ".pom")
private fun findArtifactFiles(project: Project) : List<File> {
val result = files.findRecursively(File(project.directory, project.buildDirectory)) { file ->
VALID.any { file.endsWith(it)} and file.contains(project.version!!)
}.map { it -> File(it) }
log(1, "${project.name}: Found ${result.size()} artifacts to upload")
return result
}
private fun checkAuthentication(value: String, key: String) {
Preconditions.checkNotNull(value, "Couldn't find user in property ${key}, make sure you specified" +
"your credentials in local.properties")
}
@Task(name = TASK_UPLOAD_JCENTER, description = "Upload the artifacts to JCenter",
runAfter = arrayOf(TASK_GENERATE_POM))
fun taskUploadJcenter(project: Project): TaskResult {
val user = System.getProperty(PROPERTY_BINTRAY_USER)
val password = System.getProperty(PROPERTY_BINTRAY_PASSWORD)
checkAuthentication(user, PROPERTY_BINTRAY_USER)
checkAuthentication(password, PROPERTY_BINTRAY_PASSWORD)
validateProject(project)
val jcenter = jcenterFactory.create(user, password)
val configuration = configurations.get(project.name)
//
// Upload to Maven
//
val trMaven = jcenter.uploadMaven(project, findArtifactFiles(project), configuration)
var success = trMaven.success
val messages = arrayListOf<String>()
if (! success) messages.add(trMaven.errorMessage!!)
//
// Upload individual files, if applicable
//
configuration?.let { conf : JCenterConfiguration ->
conf.files.forEach {
val taskResult = jcenter.uploadFile(File(project.directory, it.first), it.second /* url */,
conf)
success = success and taskResult.success
if (!taskResult.success) {
messages.add(taskResult.errorMessage!!)
}
}
}
return TaskResult(success, messages.join("\n "))
}
/**
* Map of project name -> JCenterConfiguration
*/
private val configurations = hashMapOf<String, JCenterConfiguration>()
fun addConfiguration(projectName: String, config: JCenterConfiguration) {
configurations.put(projectName, config)
}
}
data class JCenterConfiguration(val project: Project) {
var publish: Boolean = false
val files = arrayListOf<Pair<String, String>>()
@Directive
public fun file(filePath: String, url: String) {
files.add(Pair(filePath, url))
}
}
@Directive
public fun jcenter(project: Project, ini: JCenterConfiguration.() -> Unit)
: JCenterConfiguration {
val pd = JCenterConfiguration(project)
pd.ini()
(Plugins.getPlugin("publish") as PublishPlugin).addConfiguration(project.name!!, pd)
return pd
}

View file

@ -0,0 +1,56 @@
package com.beust.kobalt.wrapper
import java.net.URL
import java.net.URLClassLoader
/**
* A parent-last classloader that will try the child classloader first and then the parent.
* Used by the wrapper to launch a new Kobalt with not interferences from its own classes.
* Will probably be made obsolete by making the wrapper a standalone module instead of
* being inside Kobalt itself.
*/
public class ParentLastClassLoader(val classpath: List<URL>)
: ClassLoader(Thread.currentThread().getContextClassLoader()) {
private val childClassLoader: ChildURLClassLoader
init {
val urls : Array<URL> = classpath.toTypedArray()
childClassLoader = ChildURLClassLoader(urls, FindClassClassLoader(this.getParent()) )
}
/**
* This class allows me to call findClass on a classloader
*/
private class FindClassClassLoader(parent: ClassLoader) : ClassLoader(parent) {
override public fun findClass(name: String) = super.findClass(name)
}
/**
* This class delegates (child then parent) for the findClass method for a URLClassLoader.
* We need this because findClass is protected in URLClassLoader
*/
private class ChildURLClassLoader(urls: Array<URL>, val realParent: FindClassClassLoader)
: URLClassLoader(urls, null) {
override public fun findClass(name: String) : Class<*> {
try {
// first try to use the URLClassLoader findClass
return super.findClass(name)
} catch(e: ClassNotFoundException) {
// if that fails, we ask our real parent classloader to load the class (we give up)
return realParent.loadClass(name)
}
}
}
override public @Synchronized fun loadClass(name: String, resolve: Boolean) : Class<*> {
try {
// first we try to find a class inside the child classloader
return childClassLoader.findClass(name)
} catch(e: ClassNotFoundException) {
// didn't find it, try the parent
return super.loadClass(name, resolve)
}
}
}

View file

@ -0,0 +1,177 @@
package com.beust.kobalt.wrapper
import com.beust.kobalt.maven.Http
import com.beust.kobalt.misc.KFiles
import com.beust.kobalt.misc.KobaltLogger
import com.beust.kobalt.misc.benchmark
import com.beust.kobalt.plugin.java.JavaInfo
import com.beust.kobalt.plugin.java.SystemProperties
import java.io.File
import java.io.FileReader
import java.io.IOException
import java.io.InputStream
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.Paths
import java.util.Properties
import java.util.zip.ZipFile
public fun main(argv: Array<String>) {
Wrapper().installAndLaunchMain(argv)
}
/**
* Download and install a new wrapper if requested.
*/
public class Wrapper : KobaltLogger {
// kobalt.properties
private val KOBALT_PROPERTIES = "kobalt.properties"
private val KOBALTW = "kobaltw"
private val WRAPPER_DIR = KFiles.KOBALT_DIR + "/wrapper"
private val KOBALT_WRAPPER_PROPERTIES = "kobalt-wrapper.properties"
private val PROPERTY_VERSION = "kobalt.version"
val URL = "https://dl.bintray.com/cbeust/generic/"
val FILE_NAME = "kobalt"
private val properties = Properties()
public fun installAndLaunchMain(argv: Array<String>) {
val kobaltJarFile = install()
launchMain(kobaltJarFile, argv)
}
private fun readProperties(properties: Properties, ins: InputStream) {
properties.load(ins)
ins.close()
properties.forEach { es -> System.setProperty(es.getKey().toString(), es.getValue().toString()) }
}
private fun maybeCreateProperties() : Properties {
val result = Properties()
// kobalt.properties is internal to Kobalt
val url = javaClass.classLoader.getResource(KOBALT_PROPERTIES)
if (url != null) {
readProperties(result, url.openConnection().inputStream)
} else {
throw IllegalArgumentException("Couldn't find ${KOBALT_PROPERTIES}")
}
return result
}
private fun initWrapperFile(version: String) {
val config = File(WRAPPER_DIR, KOBALT_WRAPPER_PROPERTIES)
if (! config.exists()) {
KFiles.saveFile(config, "${PROPERTY_VERSION}=${version}")
}
properties.load(FileReader(config))
}
private val wrapperVersion : String
get() {
return properties.getProperty(PROPERTY_VERSION)
}
/**
* Install a new version if requested in .kobalt/wrapper/kobalt-wrapper.properties
*
* @return the path to the Kobalt jar file
*/
public fun install() : Path {
val properties = maybeCreateProperties()
val version = properties.getProperty(PROPERTY_VERSION)
initWrapperFile(version)
log(2, "Wrapper version: ${wrapperVersion}")
val fileName = "${FILE_NAME}-${wrapperVersion}.zip"
File(KFiles.distributionsDir).mkdirs()
val localZipFile = Paths.get(KFiles.distributionsDir, fileName)
val zipOutputDir = KFiles.distributionsDir + "/" + wrapperVersion
val kobaltJarFile = Paths.get(zipOutputDir, "kobalt/wrapper/${FILE_NAME}-${wrapperVersion}.jar")
if (!Files.exists(localZipFile) || !Files.exists(kobaltJarFile)) {
log(1, "Downloading ${fileName}")
val fullUrl = "${URL}/${fileName}"
val body = Http().get(fullUrl)
if (body.code == 200) {
if (!Files.exists(localZipFile)) {
val target = localZipFile.toAbsolutePath()
val ins = body.getAsStream()
benchmark("Download .zip file") {
// This takes about eight seconds for a 21M file because of the extra copying, not good.
// Should use Okio.sink(file) to create a Sink and then call readAll(fileSink) on
// the BufferedSource returned in the ResponseBody
Files.copy(ins, target)
}
log(2, "${localZipFile} downloaded, extracting it")
} else {
log(2, "${localZipFile} already exists, extracting it")
}
//
// Extract all the zip files
//
val zipFile = ZipFile(localZipFile.toFile())
val entries = zipFile.entries()
val outputDirectory = File(KFiles.distributionsDir)
outputDirectory.mkdirs()
while (entries.hasMoreElements()) {
val entry = entries.nextElement()
val entryFile = File(entry.name)
if (entry.isDirectory) {
entryFile.mkdirs()
} else {
val dest = Paths.get(zipOutputDir, entryFile.path)
log(2, " Writing ${entry.name} to ${dest}")
Files.createDirectories(dest.parent)
Files.copy(zipFile.getInputStream(entry),
dest,
java.nio.file.StandardCopyOption.REPLACE_EXISTING)
}
}
log(2, "${localZipFile} extracted")
} else {
error("Couldn't download ${URL}")
}
}
//
// Copy the wrapper files in the current kobalt/wrapper directory
//
log(2, "Copying the wrapper files...")
arrayListOf(KOBALTW, "kobalt/wrapper/${FILE_NAME}-wrapper.jar").forEach {
val from = Paths.get(zipOutputDir, it)
val to = Paths.get(File(".").absolutePath, it)
KFiles.copy(from, to, java.nio.file.StandardCopyOption.REPLACE_EXISTING)
}
File(KOBALTW).setExecutable(true)
return kobaltJarFile
}
/**
* Launch kobalt-xxx.jar
*
* Note: currently launching it in a separate VM because both this jar file and the wrapper contain
* the same classes, so the old classes will be run. Once wrapper.jar contains only the
* wrapper class and nothing else from the Kobalt distribution, we can just invoke main from the same JVM here,
* which will speed up the start up
*/
private fun launchMain(kobaltJarFile: Path, argv: Array<String>) {
val jvm = JavaInfo.create(File(SystemProperties.javaBase))
val java = jvm.javaExecutable
val args = arrayListOf(
java!!.absolutePath,
"-jar", kobaltJarFile.toFile().absolutePath)
args.addAll(argv)
val pb = ProcessBuilder(args)
pb.inheritIO()
log(1, "Launching\n ${args.join(" ")}")
val process = pb.start()
process.waitFor()
}
}

View file

@ -0,0 +1,2 @@
plugin-class=com.beust.kobalt.plugin.apt.AptPlugin

View file

@ -0,0 +1,42 @@
import com.beust.kobalt.*
import com.beust.kobalt.plugin.packaging.assemble
{{imports}}
val p = {{directive}} {
name = "{{name}}"
group = "{{group}}"
artifactId = name
version = "{{version}}"
sourceDirectories {
{{#sourceDirectories}}
path("{{toString}}")
{{/sourceDirectories}}
}
sourceDirectoriesTest {
{{#sourceDirectoriesTest}}
path("{{toString}}")
{{/sourceDirectoriesTest}}
}
dependencies {
// compile("com.beust:jcommander:1.48")
{{#mainDependencies}}
compile("{{groupId}}:{{artifactId}}:{{version}}")
{{/mainDependencies}}
}
dependenciesTest {
// compile("org.testng:testng:6.9.5")
{{#testDependencies}}
compile("{{groupId}}:{{artifactId}}:{{version}}")
{{/testDependencies}}
}
}
val packProject = assemble(p) {
jar {
}
}

View file

@ -0,0 +1 @@
kobalt.version=0.144

View file

@ -0,0 +1,21 @@
package com.beust.kobalt
import org.testng.Assert
import org.testng.annotations.Test
import java.util.Properties
@Test
public class ResourceTest {
val fileName = "kobalt.properties"
fun shouldLoadResources() {
val properties = Properties()
val res = ClassLoader.getSystemResource(fileName)
if (res != null) {
properties.load(res.openStream())
Assert.assertTrue(properties.get("foo") == "bar")
} else {
Assert.fail("Couldn't load ${fileName}")
}
}
}

View file

@ -0,0 +1,15 @@
package com.beust.kobalt
import com.beust.kobalt.maven.LocalRepo
import com.beust.kobalt.plugin.java.SystemProperties
import com.google.inject.Scopes
import java.io.File
class TestLocalRepo: LocalRepo(localRepo = SystemProperties.homeDir + File.separatorChar + ".kobalt-test")
public class TestModule : com.beust.kobalt.misc.MainModule() {
override fun configureTest() {
bind(LocalRepo::class.java).to(TestLocalRepo::class.java).`in`(Scopes.SINGLETON)
}
}

View file

@ -0,0 +1,138 @@
package com.beust.kobalt.internal
import com.beust.kobalt.maven.KobaltException
import com.beust.kobalt.misc.KobaltLogger
import com.beust.kobalt.misc.Topological
import javafx.concurrent.Worker
import org.testng.Assert
import org.testng.annotations.Test
import java.util.ArrayList
import java.util.HashSet
import java.util.concurrent.LinkedBlockingQueue
import java.util.concurrent.TimeUnit
public class DynamicGraphTest {
private fun <T> assertFreeNodesEquals(graph: DynamicGraph<T>, expected: Array<T>) {
val h = HashSet(graph.freeNodes)
val e = HashSet(expected.toList())
Assert.assertEquals(h, e)
}
private fun <T> createFactory(runNodes: ArrayList<T>, errorFunction: (T) -> Boolean) : IThreadWorkerFactory<T> {
return object: IThreadWorkerFactory<T> {
override fun createWorkers(nodes: List<T>): List<IWorker<T>> {
val result = arrayListOf<IWorker<T>>()
nodes.forEach { result.add(Worker(runNodes, it, errorFunction)) }
return result
}
}
}
public class Worker<T>(val runNodes: ArrayList<T>, val n: T,
val errorFunction: (T) -> Boolean) : IWorker<T>, KobaltLogger {
override val priority = 0
override fun call() : TaskResult2<T> {
log(2, "Running node $n")
runNodes.add(n)
return TaskResult2(errorFunction(n), n)
}
}
@Test
public fun testExecutor() {
val dg = DynamicGraph<String>();
dg.addEdge("compile", "runApt")
dg.addEdge("compile", "generateVersion")
val runNodes = arrayListOf<String>()
val factory = createFactory(runNodes, { true })
DynamicGraphExecutor(dg, factory).run()
Assert.assertEquals(runNodes.size(), 3)
}
@Test
private fun testExecutorWithSkip() {
val g = DynamicGraph<Int>()
// 2 and 3 depend on 1, 4 depend on 3, 10 depends on 4
// 3 will blow up, which should make 4 and 10 skipped
g.addEdge(2, 1)
g.addEdge(3, 1)
g.addEdge(4, 3)
g.addEdge(10, 4)
g.addEdge(5, 2)
val runNodes = arrayListOf<Int>()
val factory = createFactory(runNodes, { n -> n != 3 })
val ex = DynamicGraphExecutor(g, factory)
ex.run()
Thread.yield()
Assert.assertEquals(runNodes, listOf(1, 2, 3, 5))
}
@Test
public fun test8() {
val dg = DynamicGraph<String>();
dg.addEdge("b1", "a1")
dg.addEdge("b1", "a2")
dg.addEdge("b2", "a1")
dg.addEdge("b2", "a2")
dg.addEdge("c1", "b1")
dg.addEdge("c1", "b2")
dg.addNode("x")
dg.addNode("y")
val freeNodes = dg.freeNodes
assertFreeNodesEquals(dg, arrayOf("a1", "a2", "y", "x"))
dg.setStatus(freeNodes, DynamicGraph.Status.RUNNING)
dg.setStatus("a1", DynamicGraph.Status.FINISHED)
assertFreeNodesEquals(dg, arrayOf<String>())
dg.setStatus("a2", DynamicGraph.Status.FINISHED)
assertFreeNodesEquals(dg, arrayOf("b1", "b2"))
dg.setStatus("b2", DynamicGraph.Status.RUNNING)
dg.setStatus("b1", DynamicGraph.Status.FINISHED)
assertFreeNodesEquals(dg, arrayOf<String>())
dg.setStatus("b2", DynamicGraph.Status.FINISHED)
assertFreeNodesEquals(dg, arrayOf("c1"))
}
@Test
public fun test2() {
val dg = DynamicGraph<String>()
dg.addEdge("b1", "a1")
dg.addEdge("b1", "a2")
dg.addNode("x")
val freeNodes = dg.freeNodes
assertFreeNodesEquals(dg, arrayOf("a1", "a2", "x" ))
dg.setStatus(freeNodes, DynamicGraph.Status.RUNNING)
dg.setStatus("a1", DynamicGraph.Status.FINISHED)
assertFreeNodesEquals(dg, arrayOf<String>())
dg.setStatus("a2", DynamicGraph.Status.FINISHED)
assertFreeNodesEquals(dg, arrayOf("b1"))
dg.setStatus("b2", DynamicGraph.Status.RUNNING)
dg.setStatus("b1", DynamicGraph.Status.FINISHED)
assertFreeNodesEquals(dg, arrayOf<String>())
}
@Test
fun topologicalSort() {
val dg = Topological<String>()
dg.addEdge("b1", "a1")
dg.addEdge("b1", "a2")
dg.addEdge("b2", "a1")
dg.addEdge("b2", "a2")
dg.addEdge("c1", "b1")
dg.addEdge("c1", "b2")
val sorted = dg.sort(arrayListOf("a1", "a2", "b1", "b2", "c1", "x", "y"))
Assert.assertEquals(sorted, arrayListOf("a1", "a2", "x", "y", "b1", "b2", "c1"))
}
}

View file

@ -0,0 +1,48 @@
package com.beust.kobalt.maven
import com.beust.kobalt.TestModule
import com.beust.kobalt.misc.KobaltExecutors
import com.beust.kobalt.misc.Versions
import org.testng.Assert
import org.testng.annotations.*
import java.util.concurrent.ExecutorService
import javax.inject.Inject
import kotlin.properties.Delegates
@Guice(modules = arrayOf(TestModule::class))
public class DependencyTest @Inject constructor(val depFactory: DepFactory,
val executors: KobaltExecutors) {
@DataProvider
fun dpVersions(): Array<Array<out Any>> {
return arrayOf(
arrayOf("6.9.4", "6.9.5"),
arrayOf("1.7", "1.38"),
arrayOf("1.70", "1.380"),
arrayOf("3.8.1", "4.5"),
arrayOf("18.0-rc1", "19.0"),
arrayOf("3.0.5.RELEASE", "3.0.6")
)
}
private var executor: ExecutorService by Delegates.notNull()
@BeforeClass
public fun bc() {
executor = executors.newExecutor("DependencyTest", 5)
}
@AfterClass
public fun ac() {
executor.shutdown()
}
@Test(dataProvider = "dpVersions")
public fun versionSorting(k: String, v: String) {
val dep1 = Versions.toLongVersion(k)
val dep2 = Versions.toLongVersion(v)
Assert.assertTrue(dep1.compareTo(dep2) < 0)
Assert.assertTrue(dep2.compareTo(dep1) > 0)
}
}

View file

@ -0,0 +1,80 @@
package com.beust.kobalt.maven
import com.beust.kobalt.maven.CompletedFuture
import com.beust.kobalt.maven.DepFactory
import com.beust.kobalt.maven.LocalRepo
import com.beust.kobalt.misc.KobaltExecutors
import com.beust.kobalt.misc.MainModule
import com.beust.kobalt.TestModule
import com.google.inject.Module
import com.google.inject.util.Modules
import org.testng.Assert
import org.testng.IModuleFactory
import org.testng.ITestContext
import org.testng.annotations.BeforeClass
import org.testng.annotations.Guice
import org.testng.annotations.Test
import java.io.File
import java.util.concurrent.ExecutorService
import javax.inject.Inject
import kotlin.properties.Delegates
/**
* TODO: test snapshots https://repository.jboss.org/nexus/content/repositories/root_repository//commons-lang/commons-lang/2.7-SNAPSHOT/commons-lang-2.7-SNAPSHOT.jar
*/
@Guice(modules = arrayOf(TestModule::class))
public class DownloadTest @Inject constructor(
val depFactory: DepFactory,
val localRepo: LocalRepo,
val executors: KobaltExecutors) {
var executor: ExecutorService by Delegates.notNull()
@BeforeClass
public fun bc() {
executor = executors.newExecutor("DependentTest", 5)
}
@Test
public fun shouldDownloadWithVersion() {
File(localRepo.toFullPath("org/testng/testng")).deleteRecursively()
arrayListOf("org.testng:testng:6.9.4", "org.testng:testng:6.9.5").forEach {
val dep = depFactory.create(it, executor)
val future = dep.jarFile
Assert.assertFalse(future is CompletedFuture)
val file = future.get()
Assert.assertTrue(file.exists())
}
}
@Test
public fun shouldDownloadNoVersion() {
File(localRepo.toFullPath("org/testng/testng")).deleteRecursively()
val dep = depFactory.create("org.testng:testng:", executor)
val future = dep.jarFile
val file = future.get()
Assert.assertFalse(future is CompletedFuture)
Assert.assertEquals(file.getName(), "testng-6.9.6.jar")
Assert.assertTrue(file.exists())
}
@Test(dependsOnMethods = arrayOf("shouldDownloadWithVersion"))
public fun shouldFindLocalJar() {
val dep = depFactory.create("org.testng:testng:6.9.6", executor)
val future = dep.jarFile
Assert.assertTrue(future is CompletedFuture)
val file = future.get()
Assert.assertTrue(file.exists())
}
@Test(dependsOnMethods = arrayOf("shouldDownloadWithVersion"))
public fun shouldFindLocalJarNoVersion() {
val dep = depFactory.create("org.testng:testng:", executor)
val future = dep.jarFile
val file = future.get()
Assert.assertEquals(file.getName(), "testng-6.9.6.jar")
Assert.assertTrue(file.exists())
}
}

View file

@ -0,0 +1,10 @@
package com.beust.kobalt.maven
//import org.junit.Test
//
//public class JUnitTest {
// @Test
// public fun simpleTestForJUnit() {
// println("Works")
// }
//}

View file

@ -0,0 +1,30 @@
package com.beust.kobalt.maven
import com.beust.kobalt.TestModule
import com.beust.kobalt.misc.DependencyExecutor
import com.beust.kobalt.misc.MainModule
import com.google.inject.Guice
import org.testng.Assert
import org.testng.annotations.Test
import java.util.concurrent.ExecutorService
import javax.inject.Inject
@org.testng.annotations.Guice(modules = arrayOf(TestModule::class))
public class RemoteRepoTest @Inject constructor(val repoFinder: RepoFinder,
@DependencyExecutor val executor: ExecutorService){
val INJECTOR = Guice.createInjector(MainModule())
@Test
public fun mavenMetadata() {
val dep = MavenDependency.create("org.codehaus.groovy:groovy-all:")
Assert.assertEquals(dep.id.split(":")[2], "2.4.4")
}
@Test
public fun metadataForSnapshots() {
val jar = MavenDependency.create("org.apache.maven.wagon:wagon-provider-test:2.10-SNAPSHOT", executor)
.jarFile
Assert.assertTrue(jar.get().exists())
}
}

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