mirror of
https://github.com/ethauvin/kobalt.git
synced 2025-04-24 23:47:11 -07:00
First commit
This commit is contained in:
commit
c061e7df85
102 changed files with 6717 additions and 0 deletions
10
.gitignore
vendored
Normal file
10
.gitignore
vendored
Normal file
|
@ -0,0 +1,10 @@
|
|||
.gradle
|
||||
annotations
|
||||
.idea
|
||||
build
|
||||
buildScript
|
||||
kobaltBuild
|
||||
test-output
|
||||
.kobalt/dist
|
||||
local.properties
|
||||
|
3
README.md
Normal file
3
README.md
Normal 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
43
TODO
Normal 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
17
build-apt-plugin
Normal 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
101
build.gradle
Normal 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) }
|
||||
}
|
||||
}
|
||||
|
1
gradle/buildWithTravis.sh
Normal file
1
gradle/buildWithTravis.sh
Normal file
|
@ -0,0 +1 @@
|
|||
../gradlew check
|
58
gradle/publishing.gradle
Normal file
58
gradle/publishing.gradle
Normal 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
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
Binary file not shown.
6
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
6
gradle/wrapper/gradle-wrapper.properties
vendored
Normal 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
164
gradlew
vendored
Normal 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
90
gradlew.bat
vendored
Normal 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
42
kobalt.iml
Normal 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
153
kobalt/src/Build.kt
Normal 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()
|
||||
//}
|
||||
//
|
BIN
kobalt/wrapper/kobalt-wrapper.jar
Normal file
BIN
kobalt/wrapper/kobalt-wrapper.jar
Normal file
Binary file not shown.
1
kobalt/wrapper/kobalt-wrapper.properties
Normal file
1
kobalt/wrapper/kobalt-wrapper.properties
Normal file
|
@ -0,0 +1 @@
|
|||
kobalt.version=0.144
|
1
kobaltw
Normal file
1
kobaltw
Normal file
|
@ -0,0 +1 @@
|
|||
java -jar kobalt/wrapper/kobalt-wrapper.jar $*
|
3
kobaltw-windows
Normal file
3
kobaltw-windows
Normal file
|
@ -0,0 +1,3 @@
|
|||
jar=`ls -ltr $HOME/kotlin/kobalt/build/libs/*jar|grep -v sources|awk '{print $9}'`
|
||||
java -jar $jar $*
|
||||
|
260
modules/wrapper/src/main/java/com/beust/kobalt/wrapper/Main.java
Normal file
260
modules/wrapper/src/main/java/com/beust/kobalt/wrapper/Main.java
Normal 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();
|
||||
|
||||
}
|
||||
|
||||
}
|
11
modules/wrapper/wrapper.iml
Normal file
11
modules/wrapper/wrapper.iml
Normal 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>
|
25
src/main/kotlin/com/beust/kobalt/Args.kt
Normal file
25
src/main/kotlin/com/beust/kobalt/Args.kt
Normal 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
|
||||
}
|
||||
|
25
src/main/kotlin/com/beust/kobalt/Banner.kt
Normal file
25
src/main/kotlin/com/beust/kobalt/Banner.kt
Normal 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()))
|
||||
}
|
||||
}
|
||||
|
13
src/main/kotlin/com/beust/kobalt/BasePluginTask.kt
Normal file
13
src/main/kotlin/com/beust/kobalt/BasePluginTask.kt
Normal 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>()
|
||||
}
|
40
src/main/kotlin/com/beust/kobalt/BuildScript.kt
Normal file
40
src/main/kotlin/com/beust/kobalt/BuildScript.kt
Normal 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)
|
39
src/main/kotlin/com/beust/kobalt/FileSpec.kt
Normal file
39
src/main/kotlin/com/beust/kobalt/FileSpec.kt
Normal 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
|
||||
}
|
||||
|
||||
}
|
180
src/main/kotlin/com/beust/kobalt/Main.kt
Normal file
180
src/main/kotlin/com/beust/kobalt/Main.kt
Normal 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")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
319
src/main/kotlin/com/beust/kobalt/Plugins.kt
Normal file
319
src/main/kotlin/com/beust/kobalt/Plugins.kt
Normal 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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
95
src/main/kotlin/com/beust/kobalt/ProjectGenerator.kt
Normal file
95
src/main/kotlin/com/beust/kobalt/ProjectGenerator.kt
Normal 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 }
|
||||
}
|
||||
}
|
20
src/main/kotlin/com/beust/kobalt/Template.kt
Normal file
20
src/main/kotlin/com/beust/kobalt/Template.kt
Normal 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")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
20
src/main/kotlin/com/beust/kobalt/api/BasePlugin.kt
Normal file
20
src/main/kotlin/com/beust/kobalt/api/BasePlugin.kt
Normal 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()
|
||||
|
||||
}
|
93
src/main/kotlin/com/beust/kobalt/api/Kobalt.kt
Normal file
93
src/main/kotlin/com/beust/kobalt/api/Kobalt.kt
Normal 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)
|
||||
}
|
||||
}
|
6
src/main/kotlin/com/beust/kobalt/api/KobaltContext.kt
Normal file
6
src/main/kotlin/com/beust/kobalt/api/KobaltContext.kt
Normal file
|
@ -0,0 +1,6 @@
|
|||
package com.beust.kobalt.api
|
||||
|
||||
import com.beust.kobalt.Args
|
||||
|
||||
public class KobaltContext(val args: Args)
|
||||
|
34
src/main/kotlin/com/beust/kobalt/api/Plugin.kt
Normal file
34
src/main/kotlin/com/beust/kobalt/api/Plugin.kt
Normal 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)
|
||||
}
|
||||
}
|
17
src/main/kotlin/com/beust/kobalt/api/PluginTask.kt
Normal file
17
src/main/kotlin/com/beust/kobalt/api/PluginTask.kt
Normal 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
|
||||
}
|
||||
}
|
96
src/main/kotlin/com/beust/kobalt/api/Project.kt
Normal file
96
src/main/kotlin/com/beust/kobalt/api/Project.kt
Normal 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)) }
|
||||
}
|
||||
}
|
||||
|
9
src/main/kotlin/com/beust/kobalt/api/Task.kt
Normal file
9
src/main/kotlin/com/beust/kobalt/api/Task.kt
Normal 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
|
||||
}
|
||||
}
|
|
@ -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())
|
231
src/main/kotlin/com/beust/kobalt/internal/DynamicGraph.kt
Normal file
231
src/main/kotlin/com/beust/kobalt/internal/DynamicGraph.kt
Normal 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();
|
||||
}
|
||||
}
|
||||
|
50
src/main/kotlin/com/beust/kobalt/internal/GenericRunner.kt
Normal file
50
src/main/kotlin/com/beust/kobalt/internal/GenericRunner.kt
Normal 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")
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
17
src/main/kotlin/com/beust/kobalt/internal/JUnitRunner.kt
Normal file
17
src/main/kotlin/com/beust/kobalt/internal/JUnitRunner.kt
Normal 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()
|
||||
}
|
||||
|
120
src/main/kotlin/com/beust/kobalt/internal/JvmCompilerPlugin.kt
Normal file
120
src/main/kotlin/com/beust/kobalt/internal/JvmCompilerPlugin.kt
Normal 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
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
package com.beust.kobalt.internal
|
||||
|
||||
import java.util.jar.JarInputStream
|
||||
|
||||
public class PluginLoader(val jarStream: JarInputStream) {
|
||||
|
||||
}
|
|
@ -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)
|
169
src/main/kotlin/com/beust/kobalt/internal/TaskManager.kt
Normal file
169
src/main/kotlin/com/beust/kobalt/internal/TaskManager.kt
Normal 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
|
||||
}
|
34
src/main/kotlin/com/beust/kobalt/internal/TestNgRunner.kt
Normal file
34
src/main/kotlin/com/beust/kobalt/internal/TestNgRunner.kt
Normal 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
|
||||
}
|
||||
|
||||
}
|
||||
}
|
18
src/main/kotlin/com/beust/kobalt/kotlin/BuildFile.kt
Normal file
18
src/main/kotlin/com/beust/kobalt/kotlin/BuildFile.kt
Normal 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
|
||||
}
|
129
src/main/kotlin/com/beust/kobalt/kotlin/ScriptCompiler.kt
Normal file
129
src/main/kotlin/com/beust/kobalt/kotlin/ScriptCompiler.kt
Normal 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)
|
||||
}
|
||||
}
|
||||
|
102
src/main/kotlin/com/beust/kobalt/maven/ArtifactFetcher.kt
Normal file
102
src/main/kotlin/com/beust/kobalt/maven/ArtifactFetcher.kt
Normal 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
|
||||
}
|
||||
}
|
13
src/main/kotlin/com/beust/kobalt/maven/CompletedFuture.kt
Normal file
13
src/main/kotlin/com/beust/kobalt/maven/CompletedFuture.kt
Normal 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
|
||||
}
|
||||
|
49
src/main/kotlin/com/beust/kobalt/maven/DepFactory.kt
Normal file
49
src/main/kotlin/com/beust/kobalt/maven/DepFactory.kt
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
55
src/main/kotlin/com/beust/kobalt/maven/DependencyManager.kt
Normal file
55
src/main/kotlin/com/beust/kobalt/maven/DependencyManager.kt
Normal 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
|
||||
}
|
||||
}
|
81
src/main/kotlin/com/beust/kobalt/maven/Http.kt
Normal file
81
src/main/kotlin/com/beust/kobalt/maven/Http.kt
Normal 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)
|
||||
}
|
|
@ -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)
|
||||
}
|
19
src/main/kotlin/com/beust/kobalt/maven/LocalDep.kt
Normal file
19
src/main/kotlin/com/beust/kobalt/maven/LocalDep.kt
Normal 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))))
|
||||
}
|
||||
|
||||
}
|
61
src/main/kotlin/com/beust/kobalt/maven/LocalRepo.kt
Normal file
61
src/main/kotlin/com/beust/kobalt/maven/LocalRepo.kt
Normal 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
|
||||
}
|
||||
}
|
||||
|
||||
|
99
src/main/kotlin/com/beust/kobalt/maven/MavenDependency.kt
Normal file
99
src/main/kotlin/com/beust/kobalt/maven/MavenDependency.kt
Normal 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
|
||||
}
|
||||
}
|
||||
|
95
src/main/kotlin/com/beust/kobalt/maven/Pom.kt
Normal file
95
src/main/kotlin/com/beust/kobalt/maven/Pom.kt
Normal 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
|
||||
}
|
51
src/main/kotlin/com/beust/kobalt/maven/PomGenerator.kt
Normal file
51
src/main/kotlin/com/beust/kobalt/maven/PomGenerator.kt
Normal 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}")
|
||||
}
|
||||
}
|
145
src/main/kotlin/com/beust/kobalt/maven/RepoFinder.kt
Normal file
145
src/main/kotlin/com/beust/kobalt/maven/RepoFinder.kt
Normal 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
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
33
src/main/kotlin/com/beust/kobalt/maven/SimpleDep.kt
Normal file
33
src/main/kotlin/com/beust/kobalt/maven/SimpleDep.kt
Normal 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"
|
||||
}
|
24
src/main/kotlin/com/beust/kobalt/maven/UnversionedDep.kt
Normal file
24
src/main/kotlin/com/beust/kobalt/maven/UnversionedDep.kt
Normal 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)
|
||||
}
|
||||
}
|
8
src/main/kotlin/com/beust/kobalt/misc/Benchmarks.kt
Normal file
8
src/main/kotlin/com/beust/kobalt/misc/Benchmarks.kt
Normal 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")
|
||||
}
|
||||
|
44
src/main/kotlin/com/beust/kobalt/misc/CheckVersions.kt
Normal file
44
src/main/kotlin/com/beust/kobalt/misc/CheckVersions.kt
Normal 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()
|
||||
}
|
||||
}
|
181
src/main/kotlin/com/beust/kobalt/misc/KFiles.kt
Normal file
181
src/main/kotlin/com/beust/kobalt/misc/KFiles.kt
Normal 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()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
84
src/main/kotlin/com/beust/kobalt/misc/KobaltExecutors.kt
Normal file
84
src/main/kotlin/com/beust/kobalt/misc/KobaltExecutors.kt
Normal 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
|
||||
}
|
||||
}
|
48
src/main/kotlin/com/beust/kobalt/misc/KobaltLogger.kt
Normal file
48
src/main/kotlin/com/beust/kobalt/misc/KobaltLogger.kt
Normal 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)
|
||||
}
|
||||
}
|
67
src/main/kotlin/com/beust/kobalt/misc/MainModule.kt
Normal file
67
src/main/kotlin/com/beust/kobalt/misc/MainModule.kt
Normal 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)
|
||||
// }
|
||||
// })
|
||||
}
|
||||
}
|
26
src/main/kotlin/com/beust/kobalt/misc/Node.kt
Normal file
26
src/main/kotlin/com/beust/kobalt/misc/Node.kt
Normal 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)
|
||||
}
|
||||
}
|
31
src/main/kotlin/com/beust/kobalt/misc/Strings.kt
Normal file
31
src/main/kotlin/com/beust/kobalt/misc/Strings.kt
Normal 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)
|
||||
}
|
14
src/main/kotlin/com/beust/kobalt/misc/ToString.kt
Normal file
14
src/main/kotlin/com/beust/kobalt/misc/ToString.kt
Normal 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}}"
|
||||
}
|
44
src/main/kotlin/com/beust/kobalt/misc/Topological.kt
Normal file
44
src/main/kotlin/com/beust/kobalt/misc/Topological.kt
Normal 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
|
||||
}
|
||||
}
|
34
src/main/kotlin/com/beust/kobalt/misc/Versions.kt
Normal file
34
src/main/kotlin/com/beust/kobalt/misc/Versions.kt
Normal 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 })
|
||||
}
|
||||
}
|
||||
}
|
23
src/main/kotlin/com/beust/kobalt/plugin/DefaultPlugin.kt
Normal file
23
src/main/kotlin/com/beust/kobalt/plugin/DefaultPlugin.kt
Normal 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
|
||||
}
|
37
src/main/kotlin/com/beust/kobalt/plugin/apt/AptPlugin.kt
Normal file
37
src/main/kotlin/com/beust/kobalt/plugin/apt/AptPlugin.kt
Normal 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)
|
||||
}
|
|
@ -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"
|
||||
|
||||
}
|
30
src/main/kotlin/com/beust/kobalt/plugin/java/JavaInfo.kt
Normal file
30
src/main/kotlin/com/beust/kobalt/plugin/java/JavaInfo.kt
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
138
src/main/kotlin/com/beust/kobalt/plugin/java/JavaPlugin.kt
Normal file
138
src/main/kotlin/com/beust/kobalt/plugin/java/JavaPlugin.kt
Normal 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
|
||||
}
|
31
src/main/kotlin/com/beust/kobalt/plugin/java/JavaProject.kt
Normal file
31
src/main/kotlin/com/beust/kobalt/plugin/java/JavaProject.kt
Normal 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
|
||||
}
|
||||
|
158
src/main/kotlin/com/beust/kobalt/plugin/java/Jvm.kt
Normal file
158
src/main/kotlin/com/beust/kobalt/plugin/java/Jvm.kt
Normal 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
|
||||
// }
|
||||
}
|
||||
|
304
src/main/kotlin/com/beust/kobalt/plugin/java/OperatingSystem.kt
Normal file
304
src/main/kotlin/com/beust/kobalt/plugin/java/OperatingSystem.kt
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
|
123
src/main/kotlin/com/beust/kobalt/plugin/kotlin/KotlinPlugin.kt
Normal file
123
src/main/kotlin/com/beust/kobalt/plugin/kotlin/KotlinPlugin.kt
Normal 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
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
167
src/main/kotlin/com/beust/kobalt/plugin/packaging/JarUtils.kt
Normal file
167
src/main/kotlin/com/beust/kobalt/plugin/packaging/JarUtils.kt
Normal 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")
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
134
src/main/kotlin/com/beust/kobalt/plugin/publish/JCenterApi.kt
Normal file
134
src/main/kotlin/com/beust/kobalt/plugin/publish/JCenterApi.kt
Normal 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
|
||||
}
|
||||
}
|
128
src/main/kotlin/com/beust/kobalt/plugin/publish/PublishPlugin.kt
Normal file
128
src/main/kotlin/com/beust/kobalt/plugin/publish/PublishPlugin.kt
Normal 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
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
177
src/main/kotlin/com/beust/kobalt/wrapper/Wrapper.kt
Normal file
177
src/main/kotlin/com/beust/kobalt/wrapper/Wrapper.kt
Normal 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()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
plugin-class=com.beust.kobalt.plugin.apt.AptPlugin
|
||||
|
42
src/main/resources/build-template.mustache
Normal file
42
src/main/resources/build-template.mustache
Normal 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 {
|
||||
}
|
||||
}
|
1
src/main/resources/kobalt.properties
Normal file
1
src/main/resources/kobalt.properties
Normal file
|
@ -0,0 +1 @@
|
|||
kobalt.version=0.144
|
21
src/test/kotlin/com/beust/kobalt/ResourceTest.kt
Normal file
21
src/test/kotlin/com/beust/kobalt/ResourceTest.kt
Normal 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}")
|
||||
}
|
||||
}
|
||||
}
|
15
src/test/kotlin/com/beust/kobalt/TestModule.kt
Normal file
15
src/test/kotlin/com/beust/kobalt/TestModule.kt
Normal 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)
|
||||
}
|
||||
|
||||
}
|
138
src/test/kotlin/com/beust/kobalt/internal/DynamicGraphTest.kt
Normal file
138
src/test/kotlin/com/beust/kobalt/internal/DynamicGraphTest.kt
Normal 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"))
|
||||
}
|
||||
}
|
48
src/test/kotlin/com/beust/kobalt/maven/DependencyTest.kt
Normal file
48
src/test/kotlin/com/beust/kobalt/maven/DependencyTest.kt
Normal 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)
|
||||
}
|
||||
}
|
||||
|
80
src/test/kotlin/com/beust/kobalt/maven/DownloadTest.kt
Normal file
80
src/test/kotlin/com/beust/kobalt/maven/DownloadTest.kt
Normal 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())
|
||||
}
|
||||
}
|
10
src/test/kotlin/com/beust/kobalt/maven/JUnitTest.kt
Normal file
10
src/test/kotlin/com/beust/kobalt/maven/JUnitTest.kt
Normal file
|
@ -0,0 +1,10 @@
|
|||
package com.beust.kobalt.maven
|
||||
|
||||
//import org.junit.Test
|
||||
//
|
||||
//public class JUnitTest {
|
||||
// @Test
|
||||
// public fun simpleTestForJUnit() {
|
||||
// println("Works")
|
||||
// }
|
||||
//}
|
30
src/test/kotlin/com/beust/kobalt/maven/RemoteRepoTest.kt
Normal file
30
src/test/kotlin/com/beust/kobalt/maven/RemoteRepoTest.kt
Normal 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
Loading…
Add table
Add a link
Reference in a new issue