diff --git a/kobalt/wrapper/kobalt-wrapper.properties b/kobalt/wrapper/kobalt-wrapper.properties index aea11e8a..f92e14d5 100644 --- a/kobalt/wrapper/kobalt-wrapper.properties +++ b/kobalt/wrapper/kobalt-wrapper.properties @@ -1 +1 @@ -kobalt.version=1.0.17 \ No newline at end of file +kobalt.version=1.0.18 \ No newline at end of file diff --git a/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/ResolveDependency.kt b/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/ResolveDependency.kt index b1a0fb2a..7c848461 100644 --- a/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/ResolveDependency.kt +++ b/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/ResolveDependency.kt @@ -9,6 +9,7 @@ import com.beust.kobalt.maven.aether.KobaltMavenResolver import com.beust.kobalt.misc.KobaltExecutors import com.beust.kobalt.misc.Node import com.beust.kobalt.misc.kobaltLog +import com.beust.kobalt.misc.warn import com.google.inject.Inject import org.eclipse.aether.artifact.DefaultArtifact import org.eclipse.aether.graph.DependencyNode @@ -104,7 +105,12 @@ class ResolveDependency @Inject constructor( kobaltLog(2, "Found dependency ${dep.dep.id} level: ${dep.level}") result.add(node) seen.add(it.id) - node.addChildren(findChildren(node, seen)) + try { + node.addChildren(findChildren(node, seen)) + } catch(ex: Exception) { + if (! it.optional) warn("Couldn't resolve " + node) + // else don't warn about missing optional dependencies + } } } kobaltLog(2, "Children for ${root.value.dep.id}: ${result.size}") diff --git a/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/api/IClasspathDependency.kt b/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/api/IClasspathDependency.kt index a24344ad..527e6f13 100644 --- a/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/api/IClasspathDependency.kt +++ b/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/api/IClasspathDependency.kt @@ -36,4 +36,6 @@ interface IClasspathDependency { /** Used to only keep the most recent version for an artifact if no version was specified */ val shortId: String + + val excluded: ArrayList } \ No newline at end of file diff --git a/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/api/IDependencyManager.kt b/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/api/IDependencyManager.kt index c2e7fdb9..e272e3d5 100644 --- a/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/api/IDependencyManager.kt +++ b/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/api/IDependencyManager.kt @@ -1,8 +1,11 @@ package com.beust.kobalt.api -import com.beust.kobalt.maven.aether.Filters +import com.beust.kobalt.maven.aether.Filters.EXCLUDE_OPTIONAL_FILTER +import com.beust.kobalt.maven.aether.KobaltMavenResolver import com.beust.kobalt.maven.aether.Scope +import com.beust.kobalt.misc.kobaltLog import org.eclipse.aether.graph.DependencyFilter +import org.eclipse.aether.graph.DependencyNode /** * Manage the creation of dependencies and also provide dependencies for projects. @@ -38,7 +41,48 @@ interface IDependencyManager { * allDependencies is typically either compileDependencies or testDependencies */ fun calculateDependencies(project: Project?, context: KobaltContext, - dependencyFilter: DependencyFilter = Filters.EXCLUDE_OPTIONAL_FILTER, + dependencyFilter: DependencyFilter = + createDependencyFilter(project, project?.compileDependencies ?: emptyList()), scopes: List = listOf(Scope.COMPILE), vararg passedDependencies: List): List + + /** + * Create an Aether dependency filter that uses the dependency configuration included in each + * IClasspathDependency. + */ + fun createDependencyFilter(project: Project?, dependencies: List) : DependencyFilter { + return DependencyFilter { p0, p1 -> + fun isNodeExcluded(node: DependencyNode, passedDep: IClasspathDependency) : Boolean { + val dep = create(KobaltMavenResolver.artifactToId(node.artifact)) + return passedDep.excluded.any { ex -> ex.isExcluded(dep)} + } + fun isDepExcluded(node: DependencyNode, excluded: List?) : Boolean { + val dep = create(KobaltMavenResolver.artifactToId(node.artifact)) + return excluded?.map { it.id }?.contains(dep.id) ?: false + } + + val accept = dependencies.any { + // Is this dependency excluded? + val isExcluded = isNodeExcluded(p0, it) || isDepExcluded(p0, project?.excludedDependencies) + + // Is the parent dependency excluded? + val isParentExcluded = + if (p1.any()) { + isNodeExcluded(p1[0], it) || isDepExcluded(p1[0], project?.excludedDependencies) + } else { + false + } + + // Only accept if no exclusions were found + ! isExcluded && ! isParentExcluded + } + + if (! accept) { + kobaltLog(2, "Excluding $p0") + } + + if (accept) EXCLUDE_OPTIONAL_FILTER.accept(p0, p1) + else accept + } + } } \ No newline at end of file diff --git a/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/api/Project.kt b/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/api/Project.kt index 5e45e7cb..e6796a9a 100644 --- a/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/api/Project.kt +++ b/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/api/Project.kt @@ -3,6 +3,7 @@ package com.beust.kobalt.api import com.beust.kobalt.TestConfig import com.beust.kobalt.api.annotation.Directive import com.beust.kobalt.maven.DependencyManager +import com.beust.kobalt.maven.aether.AetherDependency import com.beust.kobalt.maven.aether.KobaltMavenResolver import com.beust.kobalt.misc.KFiles import com.beust.kobalt.misc.kobaltLog @@ -11,6 +12,7 @@ import java.io.File import java.util.* import java.util.concurrent.Future import java.util.concurrent.FutureTask +import java.util.regex.Pattern open class Project( @Directive open var name: String = "", @@ -145,7 +147,7 @@ class Dependencies(val project: Project, * future tasks receive a get(), the repos will be correct. */ private fun addToDependencies(project: Project, dependencies: ArrayList, - dep: Array, optional: Boolean = false): List> + dep: Array, optional: Boolean = false, excludeConfig: ExcludeConfig? = null): List> = with(dep.map { val resolved = if (KobaltMavenResolver.isRangeVersion(it)) { @@ -160,12 +162,70 @@ class Dependencies(val project: Project, DependencyManager.create(resolved, optional, project.directory) }) { dependencies.addAll(this) + if (excludeConfig != null) { + this.forEach { it.excluded.add(excludeConfig) } + } + this.map { FutureTask { it.jarFile.get() } } } @Directive fun compile(vararg dep: String) = addToDependencies(project, dependencies, dep) + class ExcludeConfig { + val ids = arrayListOf() + + @Directive + fun exclude(vararg passedIds: String) = ids.addAll(passedIds) + + class ArtifactConfig( + var groupId: String? = null, + var artifactId: String? = null, + var version: String? = null + ) + + val artifacts = arrayListOf() + + @Directive + fun exclude(groupId: String? = null, artifactId: String? = null, version: String? = null) + = artifacts.add(ArtifactConfig(groupId, artifactId, version)) + + fun match(pattern: String?, id: String) : Boolean { + return pattern == null || Pattern.compile(pattern).matcher(id).matches() + } + + /** + * @return true if the dependency is excluded with any of the exclude() directives. The matches + * are performed by a regular expression match against the dependency. + */ + fun isExcluded(dep: IClasspathDependency) : Boolean { + // Straight id match + var result = ids.any { match(it, dep.id) } + + // Match on any combination of (groupId, artifactId, version) + if (! result && dep.isMaven) { + val mavenDep = dep as AetherDependency + val artifact = mavenDep.artifact + result = artifacts.any { + val match1 = it.groupId == null || match(it.groupId, artifact.groupId) + val match2 = it.artifactId == null || match(it.artifactId, artifact.artifactId) + val match3 = it.version == null || match(it.version, artifact.version) + match1 && match2 && match3 + } + } + + return result + } + } + + @Directive + fun compile(dep: String, init: ExcludeConfig.() -> Unit) { + val excludeConfig = ExcludeConfig().apply { + init() + } + addToDependencies(project, dependencies, arrayOf(dep), excludeConfig = excludeConfig) + } + @Directive fun compileOptional(vararg dep: String) { addToDependencies(project, optionalDependencies, dep, optional = true) diff --git a/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/internal/JvmCompiler.kt b/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/internal/JvmCompiler.kt index 25294934..58f65776 100644 --- a/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/internal/JvmCompiler.kt +++ b/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/internal/JvmCompiler.kt @@ -23,8 +23,8 @@ class JvmCompiler @Inject constructor(val dependencyManager: DependencyManager) flags: List): TaskResult { // Dependencies - val allDependencies = (info.dependencies - + dependencyManager.calculateDependencies(project, context!!, passedDependencies = info.dependencies)) + val allDependencies = (info.dependencies + dependencyManager.calculateDependencies(project, context!!, + passedDependencies = info.dependencies)) .distinct() // Plugins that add flags to the compiler diff --git a/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/internal/JvmCompilerPlugin.kt b/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/internal/JvmCompilerPlugin.kt index c27e26bb..15cd7fc3 100644 --- a/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/internal/JvmCompilerPlugin.kt +++ b/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/internal/JvmCompilerPlugin.kt @@ -88,6 +88,7 @@ open class JvmCompilerPlugin @Inject constructor( if (testContributor != null && testContributor.affinity(project, context) > 0) { // val td1 = dependencyManager.testDependencies(project, context) val testDependencies = dependencyManager.calculateDependencies(project, context, + dependencyFilter = dependencyManager.createDependencyFilter(project, project.testDependencies), scopes = listOf(Scope.TEST)) val compileDependencies = dependencyManager.calculateDependencies(project, context, scopes = listOf(Scope.COMPILE)) diff --git a/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/internal/TestNgRunner.kt b/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/internal/TestNgRunner.kt index 568287f5..f43be88c 100644 --- a/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/internal/TestNgRunner.kt +++ b/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/internal/TestNgRunner.kt @@ -48,79 +48,4 @@ class TestNgRunner : GenericTestRunner() { addAll(testConfig.testArgs) } } - -// fun _runTests(project: Project, context: KobaltContext, classpath: List, -// configName: String): Boolean { -// var result = false -// val port = 2345 -// -// val classpath = listOf(homeDir("java/jcommander/kobaltBuild/classes"), -// homeDir(".kobalt/cache/org/testng/testng/6.10/testng-6.10.jar"), -// homeDir(".kobalt/cache/com/beust/jcommander/1.66/jcommander-1.66.jar"), -// homeDir(".kobalt/cache/org/yaml/snakeyaml/1.17/snakeyaml-1.17.jar"), -// homeDir(".kobalt/cache/com/google/code/findbugs/jsr305/3.0.1/jsr305-3.0.1.jar"), -// homeDir("java/jcommander/kobaltBuild/test-classes"), -// homeDir("java/jcommander/src/test/resources/testng.xml"), -// homeDir("kotlin/kobalt/lib/testng-remote-1.3.0-SNAPSHOT.jar"), -// homeDir("kotlin/kobalt/lib/testng-remote6_10-1.3.0-SNAPSHOT.jar") -// ).joinToString(File.pathSeparator) -// val passedArgs = listOf( -// "-classpath", -// classpath, -// "org.testng.remote.RemoteTestNG", -// "-serport", port.toString(), -// "-version", "6.10", -// "-dontexit", -// RemoteArgs.PROTOCOL, -// "json", -// "src/test/resources/testng.xml") -// -// Thread { -// val exitCode = runCommand { -// command = "java" -// directory = File(homeDir("java/jcommander")) -// args = passedArgs -// } -// }.start() -// -//// Thread { -//// val args2 = arrayOf("-serport", port.toString(), "-dontexit", RemoteArgs.PROTOCOL, "json", -//// "-version", "6.10", -//// "src/test/resources/testng.xml") -//// RemoteTestNG.main(args2) -//// }.start() -// -// val mh = MessageHub(JsonMessageSender("localhost", port, true)) -// mh.setDebug(true) -// mh.initReceiver() -// val passed = arrayListOf() -// data class FailedTest(val method: String, val cls: String, val stackTrace: String) -// val failed = arrayListOf() -// var skipped = arrayListOf() -// try { -// var message = mh.receiveMessage() -// println("") -// while (message != null) { -// message = mh.receiveMessage() -// if (message is TestResultMessage) { -// when(message.result) { -// MessageHelper.PASSED_TEST -> passed.add(message.name) -// MessageHelper.FAILED_TEST -> failed.add(FailedTest(message.testClass, -// message.method, message.stackTrace)) -// MessageHelper.SKIPPED_TEST -> skipped.add(message.name) -// } -// } -// print("\r" + String.format("%4d / %4d / %4d", passed.size, failed.size, skipped.size)) -//// Thread.sleep(200) -// } -// } catch(ex: IOException) { -// println("Exception: ${ex.message}") -// } -// println("\nPassed: " + passed.size + ", Failed: " + failed.size + ", Skipped: " + skipped.size) -// failed.forEach { -// val top = it.stackTrace.substring(0, it.stackTrace.indexOf("\n")) -// println(" " + it.cls + "." + it.method + "\n " + top) -// } -// return result -// } } diff --git a/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/maven/DependencyManager.kt b/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/maven/DependencyManager.kt index 9ff45407..14ae2c3d 100644 --- a/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/maven/DependencyManager.kt +++ b/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/maven/DependencyManager.kt @@ -140,7 +140,7 @@ class DependencyManager @Inject constructor(val executors: KobaltExecutors, * formally by groupId or artifactId. */ fun isDependencyExcluded(dep: IClasspathDependency, excluded: List): Boolean { - excluded.any { excluded -> dep.id.startsWith(excluded.id) }.let { result -> + excluded.any { excluded -> dep.id == excluded.id }.let { result -> if (result) { context.logger.log(project?.name ?: "", 2, " Excluding dependency $dep") } @@ -178,11 +178,11 @@ class DependencyManager @Inject constructor(val executors: KobaltExecutors, dependencyFilter: DependencyFilter? = null, requiredBy: String? = null): List { val result = arrayListOf() - dependencies.forEach { - result.add(it) - if (it.isMaven) { - val resolved = resolver.resolveToIds(it.id, null, dependencyFilter) - result.addAll(resolved.map { create(it) }) + dependencies.forEach { dependency -> + result.add(dependency) + if (dependency.isMaven) { + val resolved = resolver.resolveToIds(dependency.id, null, dependencyFilter).map { create(it) } + result.addAll(resolved) } } val reordered = reorderDependencies(result) diff --git a/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/maven/aether/AetherDependency.kt b/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/maven/aether/AetherDependency.kt index 7ec9bfe0..5cd532fe 100644 --- a/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/maven/aether/AetherDependency.kt +++ b/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/maven/aether/AetherDependency.kt @@ -1,5 +1,6 @@ package com.beust.kobalt.maven.aether +import com.beust.kobalt.api.Dependencies import com.beust.kobalt.api.IClasspathDependency import com.beust.kobalt.api.Kobalt import com.beust.kobalt.maven.CompletedFuture @@ -65,6 +66,8 @@ class AetherDependency(val artifact: Artifact, override val optional: Boolean = override val shortId = artifact.groupId + ":" + artifact.artifactId + ":" + artifact.classifier + override val excluded = arrayListOf() + override fun compareTo(other: AetherDependency): Int { return Versions.toLongVersion(artifact.version).compareTo(Versions.toLongVersion( other.artifact.version)) diff --git a/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/maven/dependency/FileDependency.kt b/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/maven/dependency/FileDependency.kt index a271cf9a..047754e2 100644 --- a/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/maven/dependency/FileDependency.kt +++ b/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/maven/dependency/FileDependency.kt @@ -1,5 +1,6 @@ package com.beust.kobalt.maven.dependency +import com.beust.kobalt.api.Dependencies import com.beust.kobalt.api.IClasspathDependency import com.beust.kobalt.maven.CompletedFuture import org.apache.maven.model.Dependency @@ -31,6 +32,8 @@ open class FileDependency(open val fileName: String, override val optional: Bool override fun directDependencies() = arrayListOf() + override val excluded = arrayListOf() + override fun compareTo(other: FileDependency) = fileName.compareTo(other.fileName) override fun toString() = fileName diff --git a/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/misc/Git.kt b/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/misc/Git.kt index 0507130b..f4c4161f 100644 --- a/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/misc/Git.kt +++ b/modules/kobalt-plugin-api/src/main/kotlin/com/beust/kobalt/misc/Git.kt @@ -31,7 +31,12 @@ class Git @Inject constructor() { .findGitDir() .build() val git = org.eclipse.jgit.api.Git(repo) - val ref = git.tag().setAnnotated(annotated).setName(version).setMessage(message).call() + // jGit library will complain and not tag if setAnnotated(false) + var ref = if (annotated) { + git.tag().setAnnotated(annotated).setName(version).setMessage(message).call() + } else { + git.tag().setName(version).setMessage(message).call() + } git.push().setPushTags().call() true } catch(ex: Exception) { diff --git a/src/main/kotlin/com/beust/kobalt/app/ParsedBuildFile.kt b/src/main/kotlin/com/beust/kobalt/app/ParsedBuildFile.kt index 8f7d3fe3..cf9afc94 100644 --- a/src/main/kotlin/com/beust/kobalt/app/ParsedBuildFile.kt +++ b/src/main/kotlin/com/beust/kobalt/app/ParsedBuildFile.kt @@ -8,6 +8,7 @@ import com.beust.kobalt.api.Project import com.beust.kobalt.internal.build.BuildFile import com.beust.kobalt.internal.build.VersionFile import com.beust.kobalt.maven.DependencyManager +import com.beust.kobalt.maven.aether.Filters.EXCLUDE_OPTIONAL_FILTER import com.beust.kobalt.misc.BlockExtractor import com.beust.kobalt.misc.KFiles import com.beust.kobalt.misc.kobaltLog @@ -129,8 +130,8 @@ class ParsedBuildFile(val buildFile: BuildFile, val context: KobaltContext, val } } - private fun generateJarFile(context: KobaltContext, buildFile: BuildFile, buildScriptJarFile: File, - originalFile: BuildFile) { + private fun generateJarFile(context: KobaltContext, buildFile: BuildFile, + buildScriptJarFile: File, originalFile: BuildFile) { // // Compile the jar file diff --git a/src/main/resources/kobalt.properties b/src/main/resources/kobalt.properties index c0ad399b..bb9154b7 100644 --- a/src/main/resources/kobalt.properties +++ b/src/main/resources/kobalt.properties @@ -1 +1 @@ -kobalt.version=1.0.17 +kobalt.version=1.0.18 diff --git a/src/test/kotlin/com/beust/kobalt/internal/ExcludeTest.kt b/src/test/kotlin/com/beust/kobalt/internal/ExcludeTest.kt index a772e428..41865745 100644 --- a/src/test/kotlin/com/beust/kobalt/internal/ExcludeTest.kt +++ b/src/test/kotlin/com/beust/kobalt/internal/ExcludeTest.kt @@ -24,8 +24,8 @@ class ExcludeTest @Inject constructor(compilerFactory: BuildFileCompiler.IFactor arrayOf(EXCLUDED_DEPENDENCY) ) - @Test(dataProvider = "dp") - fun excludeShouldWork(excludedDependency: String?) { + @Test(dataProvider = "dp", description = "Text exclusions that apply to the whole project") + fun globalExcludeShouldWork(excludedDependency: String?) { val projectText = """ dependencies { compile("org.apache.maven:maven-model:jar:3.3.9") @@ -44,7 +44,43 @@ class ExcludeTest @Inject constructor(compilerFactory: BuildFileCompiler.IFactor } else { assertThat(allIds).contains(EXCLUDED_DEPENDENCY) } + } + @DataProvider + fun dp2() = arrayOf>( + arrayOf(null, 8, ""), + arrayOf("{ exclude(\".*org.apache.*\") }", 4, "org.apache"), + arrayOf("{ exclude(groupId = \"org.apache.*\") }", 4, "org.apache"), + arrayOf("{ exclude(artifactId = \".*core.*\") }", 7, "core"), + arrayOf("{ exclude(artifactId = \"httpcore\", version = \"4.3.3\") }", 7, "httpcore"), + arrayOf("{ exclude(version = \"4.3.3\") }", 7, "httpcore"), + arrayOf("{ exclude(artifactId = \"commons.codec\") }", 7, "commons-codec") + ) + + @Test(dataProvider = "dp2", description = "Text exclusions tied to a specific dependency") + fun localExcludeShouldWork(excludedDependency: String?, expectedCount: Int, excludedString: String) { + val projectText = """ + dependencies { + compile("org.eclipse.jgit:org.eclipse.jgit:4.5.0.201609210915-r") + """ + + (if (excludedDependency != null) """$excludedDependency""" else "") + + """ + } + """ + + val project = compileSingleProject(projectText) + val allIds = dependencyManager.calculateDependencies(project, Kobalt.context!!, + scopes = listOf(Scope.COMPILE)) + .map { it.id } + + assertThat(allIds.size).isEqualTo(expectedCount) + if (excludedDependency != null) { + if (allIds.any { it.contains(excludedString) }) { + throw AssertionError("id's should not contain any string \"$excludedString\": $allIds") + } + } else { + assertThat(allIds.filter { it.contains("org.apache") }.size).isEqualTo(2) + } } }