1
0
Fork 0
mirror of https://github.com/ethauvin/kobalt.git synced 2025-04-26 08:27:12 -07:00

Fix optional dependencies problem.

This commit is contained in:
Cedric Beust 2017-02-01 12:58:59 -08:00
parent 38bb53387e
commit ae450e4cbc
15 changed files with 323 additions and 127 deletions

View file

@ -26,7 +26,7 @@ object Versions {
val okio = "1.6.0"
val retrofit = "2.1.0"
val gson = "2.6.2"
val aether = "1.1.0"
val aether = "1.0.0.v20140518"
val sonatypeAether = "1.13.1"
val maven = "3.3.9"
}
@ -102,6 +102,7 @@ val kobaltPluginApi = project {
"org.slf4j:slf4j-nop:1.6.0",
"org.eclipse.aether:aether-spi:${Versions.aether}",
"org.eclipse.aether:aether-util:${Versions.aether}",
"org.eclipse.aether:aether-impl:${Versions.aether}",
"org.eclipse.aether:aether-connector-basic:${Versions.aether}",
"org.eclipse.aether:aether-transport-file:${Versions.aether}",
@ -158,6 +159,7 @@ val kobaltApp = project(kobaltPluginApi, wrapper) {
"com.squareup.retrofit2:converter-gson:${Versions.retrofit}",
"com.squareup.okhttp3:okhttp-ws:${Versions.okhttp}",
"biz.aQute.bnd:bndlib:2.4.0",
"org.sonatype.aether:aether-api:${Versions.sonatypeAether}",
"com.squareup.okhttp3:logging-interceptor:3.2.0",
@ -176,7 +178,10 @@ val kobaltApp = project(kobaltPluginApi, wrapper) {
dependenciesTest {
compile("org.testng:testng:6.9.11",
"org.assertj:assertj-core:3.4.1")
"org.assertj:assertj-core:3.4.1",
"org.eclipse.aether:aether-spi:${Versions.aether}",
"org.eclipse.aether:aether-util:${Versions.aether}"
)
}
assemble {

View file

@ -104,7 +104,7 @@ class JarGenerator @Inject constructor(val dependencyManager: DependencyManager)
context.variant.productFlavor.compileDependencies +
context.variant.productFlavor.compileRuntimeDependencies
val transitiveDependencies = dependencyManager.calculateDependencies(project, context,
listOf(Scope.COMPILE), allDependencies)
scopes = listOf(Scope.COMPILE), passedDependencies = allDependencies)
transitiveDependencies.map {
it.jarFile.get()
}.forEach { file : File ->

View file

@ -1,6 +1,8 @@
package com.beust.kobalt.api
import com.beust.kobalt.maven.aether.Filters
import com.beust.kobalt.maven.aether.Scope
import org.eclipse.aether.graph.DependencyFilter
/**
* Manage the creation of dependencies and also provide dependencies for projects.
@ -36,6 +38,7 @@ interface IDependencyManager {
* allDependencies is typically either compileDependencies or testDependencies
*/
fun calculateDependencies(project: Project?, context: KobaltContext,
scopeFilters: Collection<Scope> = listOf(Scope.COMPILE),
dependencyFilter: DependencyFilter = Filters.EXCLUDE_OPTIONAL_FILTER,
scopes: List<Scope> = listOf(Scope.COMPILE),
vararg passedDependencies: List<IClasspathDependency>): List<IClasspathDependency>
}

View file

@ -2,12 +2,15 @@ package com.beust.kobalt.maven
import com.beust.kobalt.KobaltException
import com.beust.kobalt.api.*
import com.beust.kobalt.maven.aether.Filters
import com.beust.kobalt.maven.aether.KobaltAether
import com.beust.kobalt.maven.aether.Scope
import com.beust.kobalt.maven.dependency.FileDependency
import com.beust.kobalt.misc.KFiles
import com.beust.kobalt.misc.KobaltExecutors
import com.google.common.collect.ArrayListMultimap
import org.eclipse.aether.graph.DependencyFilter
import org.eclipse.aether.util.filter.OrDependencyFilter
import java.io.File
import java.util.*
import javax.inject.Inject
@ -92,7 +95,8 @@ class DependencyManager @Inject constructor(val executors: KobaltExecutors, val
* are passed, they are calculated from the scope filters.
*/
override fun calculateDependencies(project: Project?, context: KobaltContext,
scopeFilters: Collection<Scope>,
dependencyFilter: DependencyFilter,
scopes: List<Scope>,
vararg passedDependencies: List<IClasspathDependency>): List<IClasspathDependency> {
val result = arrayListOf<IClasspathDependency>()
@ -100,7 +104,7 @@ class DependencyManager @Inject constructor(val executors: KobaltExecutors, val
* Extract the correct dependencies from the project based on the scope filters.
*/
fun filtersToDependencies(project: Project, scopes: Collection<Scope>): List<IClasspathDependency> {
return arrayListOf<IClasspathDependency>().apply {
val result = arrayListOf<IClasspathDependency>().apply {
if (scopes.contains(Scope.COMPILE)) {
addAll(project.compileDependencies)
}
@ -111,17 +115,18 @@ class DependencyManager @Inject constructor(val executors: KobaltExecutors, val
addAll(project.testDependencies)
}
}
return result
}
val allDependencies : Array<out List<IClasspathDependency>> =
if (project == null || passedDependencies.any()) passedDependencies
else arrayOf(filtersToDependencies(project, scopeFilters))
else arrayOf(filtersToDependencies(project, scopes))
allDependencies.forEach { dependencies ->
result.addAll(transitiveClosure(dependencies, scopeFilters, project?.name))
result.addAll(transitiveClosure(dependencies, dependencyFilter, project?.name))
}
result.addAll(runClasspathContributors(project, context))
result.addAll(dependentProjectDependencies(project, context, scopeFilters))
result.addAll(dependentProjectDependencies(project, context, dependencyFilter, scopes.contains(Scope.TEST)))
// Dependencies get reordered by transitiveClosure() but since we just added a bunch of new ones,
// we need to reorder them again in case we're adding dependencies that are already present
@ -144,13 +149,13 @@ class DependencyManager @Inject constructor(val executors: KobaltExecutors, val
* TODO: This should be private, everyone should be calling calculateDependencies().
*/
fun transitiveClosure(dependencies : List<IClasspathDependency>,
scopeFilter: Collection<Scope> = emptyList(),
dependencyFilter: DependencyFilter? = null,
requiredBy: String? = null): List<IClasspathDependency> {
val result = arrayListOf<IClasspathDependency>()
dependencies.forEach {
result.add(it)
if (it.isMaven) {
val resolved = aether.resolveAll(it.id, null, scopeFilter).map { it.toString() }
val resolved = aether.resolveAll(it.id, null, dependencyFilter)
result.addAll(resolved.map { create(it) })
}
}
@ -183,7 +188,7 @@ class DependencyManager @Inject constructor(val executors: KobaltExecutors, val
* their own dependencies
*/
private fun dependentProjectDependencies(project: Project?, context: KobaltContext,
scopeFilters: Collection<Scope>): List<IClasspathDependency> {
dependencyFilter: DependencyFilter, isTest: Boolean): List<IClasspathDependency> {
if (project == null) {
return emptyList()
} else {
@ -199,8 +204,8 @@ class DependencyManager @Inject constructor(val executors: KobaltExecutors, val
project.dependsOn.forEach { p ->
maybeAddClassDir(KFiles.joinDir(p.directory, p.classesDir(context)))
if (scopeFilters.contains(Scope.TEST)) maybeAddClassDir(KFiles.makeOutputTestDir(project).path)
val otherDependencies = calculateDependencies(p, context, scopeFilters)
if (isTest) maybeAddClassDir(KFiles.makeOutputTestDir(project).path)
val otherDependencies = calculateDependencies(p, context, dependencyFilter, Scope.toScopes(isTest))
result.addAll(otherDependencies)
}
@ -225,8 +230,13 @@ class DependencyManager @Inject constructor(val executors: KobaltExecutors, val
deps.add(testProvidedDependencies)
scopeFilters.add(Scope.TEST)
}
val filter =
if (isTest) OrDependencyFilter(Filters.COMPILE_FILTER, Filters.TEST_FILTER)
else Filters.COMPILE_FILTER
deps.filter { it.any() }.forEach {
transitive.addAll(calculateDependencies(project, context, scopeFilters, it))
transitive.addAll(calculateDependencies(project, context, filter,
scopes = Scope.toScopes(isTest),
passedDependencies = it))
}
}
}

View file

@ -9,6 +9,7 @@ import com.beust.kobalt.maven.dependency.FileDependency
import com.beust.kobalt.misc.KFiles
import com.google.common.collect.ArrayListMultimap
import com.google.inject.Inject
import org.eclipse.aether.graph.DependencyFilter
import java.io.File
import java.util.*
@ -59,18 +60,18 @@ class DependencyManager2 @Inject constructor(val aether: KobaltAether) {
* Resolve the dependencies for the give project based on the scope filters.
*/
fun resolve(project: Project, context: KobaltContext, isTest: Boolean,
passedScopeFilters : List<Scope> = emptyList(),
passedScopes : List<Scope> = emptyList(),
passedIds: List<IClasspathDependency> = emptyList()): List<IClasspathDependency> {
val result = hashSetOf<IClasspathDependency>()
val nonMavenDependencies = hashSetOf<IClasspathDependency>()
val scopeFilters =
if (passedScopeFilters.isEmpty())
if (isTest) listOf(Scope.TEST)
val actualScopes =
if (passedScopes.isEmpty())
if (isTest) listOf(Scope.TEST, Scope.COMPILE)
else listOf(Scope.COMPILE)
else passedScopeFilters
else passedScopes
val toDependencies = Scope.toDependencyLambda(scopeFilters)
val toDependencies = Scope.toDependencyLambda(actualScopes)
// Make sure that classes/ and test-classes/ are always at the top of this classpath,
// so that older versions of that project on the classpath don't shadow them
@ -82,7 +83,7 @@ class DependencyManager2 @Inject constructor(val aether: KobaltAether) {
// Passed and direct ids
val ids = hashSetOf<IClasspathDependency>().apply {
addAll(passedIds)
addAll(toDependencies(project))
addAll(toDependencies(project).filter { ! it.optional })
}
// Contributed id's
@ -105,8 +106,9 @@ class DependencyManager2 @Inject constructor(val aether: KobaltAether) {
var i = 0
ids.forEach {
if (it.isMaven) {
val resolved = aether.resolveAll(it.id, filterScopes = scopeFilters)
.map { create(it.toString(), false, project.directory) }
result.add(it)
val resolved = aether.resolveAll(it.id, dependencyFilter = scopesToDependencyFilter(actualScopes))
.map { create(it, false, project.directory) }
i++
result.addAll(resolved)
} else {
@ -119,6 +121,18 @@ class DependencyManager2 @Inject constructor(val aether: KobaltAether) {
return reorderDependencies(result)
}
private fun scopesToDependencyFilter(scopes: List<Scope>, includeOptional: Boolean = false): DependencyFilter {
return DependencyFilter { p0, p1 ->
if (p0.dependency.optional && ! includeOptional) return@DependencyFilter false
val result = scopes.any {
p0.dependency.scope == "" && scopes.contains(Scope.COMPILE) ||
p0.dependency.scope == it.scope
}
result
}
}
/**
* Reorder dependencies so that if an artifact appears several times, only the one with the higest version
* is included.

View file

@ -5,7 +5,6 @@ import com.beust.kobalt.api.IClasspathDependency
import com.beust.kobalt.api.Kobalt
import com.beust.kobalt.api.Project
import com.beust.kobalt.internal.KobaltSettings
import com.beust.kobalt.internal.KobaltSettingsXml
import com.beust.kobalt.internal.getProxy
import com.beust.kobalt.maven.CompletedFuture
import com.beust.kobalt.maven.LocalDep
@ -23,7 +22,6 @@ import org.eclipse.aether.collection.CollectRequest
import org.eclipse.aether.collection.CollectResult
import org.eclipse.aether.graph.Dependency
import org.eclipse.aether.graph.DependencyFilter
import org.eclipse.aether.graph.DependencyNode
import org.eclipse.aether.repository.ArtifactRepository
import org.eclipse.aether.repository.RemoteRepository
import org.eclipse.aether.resolution.DependencyRequest
@ -32,10 +30,7 @@ import org.eclipse.aether.resolution.VersionRangeRequest
import org.eclipse.aether.resolution.VersionRangeResult
import org.eclipse.aether.transfer.ArtifactNotFoundException
import org.eclipse.aether.util.artifact.JavaScopes
import org.eclipse.aether.util.filter.AndDependencyFilter
import org.eclipse.aether.util.filter.DependencyFilterUtils
import java.io.File
import java.util.*
import java.util.concurrent.Future
enum class Scope(val scope: String, val dependencyLambda: (Project) -> List<IClasspathDependency>) {
@ -47,26 +42,22 @@ enum class Scope(val scope: String, val dependencyLambda: (Project) -> List<ICla
;
companion object {
/**
* @return a filter that excludes optional dependencies and allows all the scopes passed in parameter.
*/
fun toFilter(scopes: Collection<Scope>): DependencyFilter {
val javaScopes = scopes.map { DependencyFilterUtils.classpathFilter(it.scope) }.toTypedArray()
return AndDependencyFilter(KobaltAether.ExcludeOptionalDependencyFilter(), *javaScopes)
}
fun toScopes(isTest: Boolean) = if (isTest) listOf(Scope.TEST, Scope.COMPILE) else listOf(Scope.COMPILE)
/**
* @return a lambda that extracts the correct dependencies from a project based on the scope
* filters passed.
* filters passed (excludes optional dependencies).
*/
fun toDependencyLambda(scopes: Collection<Scope>) : (Project) -> List<IClasspathDependency> {
val result = { project : Project ->
scopes.fold(arrayListOf<IClasspathDependency>(),
val deps = scopes.fold(arrayListOf<IClasspathDependency>(),
{ list: ArrayList<IClasspathDependency>, scope: Scope ->
list.addAll(scope.dependencyLambda(project))
list.addAll(scope.dependencyLambda(project).filter { ! it.optional })
list
})
}
deps
}
return result
}
}
@ -94,16 +85,16 @@ class KobaltAether @Inject constructor (val settings: KobaltSettings, val aether
DependencyResult(AetherDependency(it.artifact), it.repository.toString())
}
fun resolveAll(id: String, artifactScope: Scope? = null, filterScopes: Collection<Scope> = emptyList())
fun resolveAll(id: String, artifactScope: Scope? = null, dependencyFilter: DependencyFilter?)
: List<String> {
val results = aether.resolve(DefaultArtifact(id), artifactScope, filterScopes)
val results = aether.resolve(DefaultArtifact(id), artifactScope, dependencyFilter)
return results.map { it.artifact.toString() }
}
fun resolve(id: String, artifactScope: Scope? = null, filterScopes: Collection<Scope> = emptyList())
fun resolve(id: String, artifactScope: Scope? = null, dependencyFilter: DependencyFilter = Filters.COMPILE_FILTER)
: DependencyResult {
kobaltLog(ConsoleRepositoryListener.LOG_LEVEL, "Resolving $id")
val result = resolveToArtifact(id, artifactScope, filterScopes)
val result = resolveToArtifact(id, artifactScope, dependencyFilter)
if (result != null) {
return DependencyResult(AetherDependency(result.artifact), result.repository.toString())
} else {
@ -111,34 +102,23 @@ class KobaltAether @Inject constructor (val settings: KobaltSettings, val aether
}
}
fun resolveToArtifact(id: String, artifactScope: Scope? = null, filterScopes: Collection<Scope> = emptyList())
fun resolveToArtifact(id: String, artifactScope: Scope? = null,
dependencyFilter: DependencyFilter? = null)
: AetherResult? {
kobaltLog(ConsoleRepositoryListener.LOG_LEVEL, "Resolving $id")
val results = aether.resolve(DefaultArtifact(MavenId.toKobaltId(id)), artifactScope, filterScopes)
val results = aether.resolve(DefaultArtifact(MavenId.toKobaltId(id)), artifactScope, dependencyFilter)
if (results.size > 0) {
return results[0]
} else {
return null
}
}
class ExcludeOptionalDependencyFilter : DependencyFilter {
override fun accept(node: DependencyNode?, p1: MutableList<DependencyNode>?): Boolean {
// val result = node != null && ! node.dependency.isOptional
val accept1 = node == null || node.artifact.artifactId != "srczip"
val accept2 = node != null && !node.dependency.isOptional
val result = accept1 && accept2
return result
}
}
}
@Singleton
class Aether(localRepo: File, val settings: KobaltSettings, eventBus: EventBus) {
private val system = Booter.newRepositorySystem()
private val session = Booter.newRepositorySystemSession(system, localRepo, settings, eventBus)
// private val classpathFilter = Scopes.toFilter(Scopes.COMPILE, Scopes.TEST)
// private val testClasspathFilter = Scopes.toFilter(Scopes.TEST)
private val kobaltRepositories: List<RemoteRepository>
get() = Kobalt.repos.map {
@ -170,7 +150,7 @@ class Aether(localRepo: File, val settings: KobaltSettings, eventBus: EventBus)
if (resolved != null) {
val newArtifact = DefaultArtifact(artifact.groupId, artifact.artifactId, artifact.extension,
resolved.highestVersion.toString())
val artifactResult = resolve(newArtifact, null, emptyList())
val artifactResult = resolve(newArtifact, null)
if (artifactResult.any()) {
return artifactResult[0]
} else {
@ -187,7 +167,9 @@ class Aether(localRepo: File, val settings: KobaltSettings, eventBus: EventBus)
return result
}
fun resolve(artifact: Artifact, artifactScope: Scope?, filterScopes: Collection<Scope>): List<AetherResult> {
fun resolve(artifact: Artifact, artifactScope: Scope?,
dependencyFilter: DependencyFilter? = null)
: List<AetherResult> {
fun manageException(ex: Exception, artifact: Artifact): List<AetherResult> {
if (artifact.extension == "pom") {
// Only display a warning for .pom files. Not resolving a .jar or other artifact
@ -198,7 +180,6 @@ class Aether(localRepo: File, val settings: KobaltSettings, eventBus: EventBus)
}
try {
val scopeFilter = Scope.toFilter(filterScopes)
val result =
if (KobaltAether.isRangeVersion(artifact.version)) {
val request = rangeRequest(artifact)
@ -212,7 +193,8 @@ class Aether(localRepo: File, val settings: KobaltSettings, eventBus: EventBus)
throw KobaltException("Couldn't resolve range artifact " + artifact)
}
} else {
val dependencyRequest = DependencyRequest(collectRequest(artifact, artifactScope), scopeFilter)
val dependencyRequest = DependencyRequest(collectRequest(artifact, artifactScope), dependencyFilter)
try {
system.resolveDependencies(session, dependencyRequest).artifactResults.map {
AetherResult(it.artifact, it.repository)
@ -256,7 +238,7 @@ class AetherDependency(val artifact: Artifact, override val optional: Boolean =
if (file.exists()) {
CompletedFuture(file)
} else {
val td = aether.resolve(artifact, null, emptyList())
val td = aether.resolve(artifact, null)
if (td.any()) {
val newFile = td[0].artifact.file
if (newFile != null) {
@ -318,21 +300,38 @@ class AetherDependency(val artifact: Artifact, override val optional: Boolean =
override fun toString() = id
}
fun main(argv: Array<String>) {
val request = CollectRequest().apply {
root = Dependency(DefaultArtifact("org.testng:testng:6.9.11"), JavaScopes.COMPILE)
fun f(argv: Array<String>) {
val collectRequest = CollectRequest().apply {
root = Dependency(DefaultArtifact("com.squareup.retrofit2:converter-jackson:jar:2.1.0"), JavaScopes.COMPILE)
repositories = listOf(
RemoteRepository.Builder("Maven", "default", "http://repo1.maven.org/maven2/").build(),
RemoteRepository.Builder("JCenter", "default", "https://jcenter.bintray.com").build())
}
val dependencyRequest = DependencyRequest().apply {
collectRequest = request
// RemoteRepository.Builder("Maven", "default", "http://repo1.maven.org/maven2/").build()
RemoteRepository.Builder("JCenter", "default", "https://jcenter.bintray.com").build()
)
}
// val dependencyRequest = DependencyRequest().apply {
// collectRequest = request
// filter = object: DependencyFilter {
// override fun accept(p0: DependencyNode, p1: MutableList<DependencyNode>?): Boolean {
// if (p0.artifact.artifactId.contains("android")) {
// println("ANDROID")
// }
// return p0.dependency.scope == JavaScopes.COMPILE
// }
//
// }
// }
val dr2 = DependencyRequest(collectRequest, null).apply {}
// val system = ManualRepositorySystemFactory.newRepositorySystem()
// val session = DefaultRepositorySystemSession()
// val localRepo = LocalRepository(File("/Users/cedricbeust/t/localAether").absolutePath)
// session.localRepositoryManager = system.newLocalRepositoryManager(session, localRepo)
val system = Booter.newRepositorySystem()
val session = Booter.newRepositorySystemSession(system, File("/tmp"), KobaltSettings(KobaltSettingsXml()),
EventBus())
// val session = MavenRepositorySystemUtils.newSession(KobaltSettings(KobaltSettingsXml()))
val result = system.resolveDependencies(session, dependencyRequest).artifactResults
val session = Booter.newRepositorySystemSession(system)
val result = system.resolveDependencies(session, dr2).artifactResults
println("RESULT: " + result)
// KobaltLogger.LOG_LEVEL = 1
@ -346,4 +345,36 @@ fun main(argv: Array<String>) {
// println("Artifact: " + d)
}
fun f2() {
val system = Booter.newRepositorySystem()
val session = Booter.newRepositorySystemSession(system)
val artifact = DefaultArtifact("com.squareup.retrofit2:converter-jackson:jar:2.1.0")
// DependencyFilter classpathFlter = DependencyFilterUtils.classpathFilter( JavaScopes.COMPILE );
val f2 = DependencyFilter { dependencyNode, list ->
println("ACCEPTING " + dependencyNode)
true
}
val collectRequest = CollectRequest()
collectRequest.root = Dependency(artifact, JavaScopes.COMPILE)
collectRequest.repositories = listOf(
RemoteRepository.Builder("Maven", "default", "http://repo1.maven.org/maven2/").build()
)
val dependencyRequest = DependencyRequest(collectRequest, null)
val artifactResults = system.resolveDependencies(session, dependencyRequest).artifactResults
for (artifactResult in artifactResults) {
println(artifactResult.artifact.toString() + " resolved to " + artifactResult.artifact.file)
}
}
fun main(args: Array<String>) {
f2()
}

View file

@ -16,6 +16,21 @@ object Booter {
// return org.eclipse.aether.examples.plexus.PlexusRepositorySystemFactory.newRepositorySystem();
}
fun newRepositorySystemSession(system: RepositorySystem): DefaultRepositorySystemSession {
val session = org.apache.maven.repository.internal.MavenRepositorySystemUtils.newSession()
val localRepo = LocalRepository("target/local-repo")
session.localRepositoryManager = system.newLocalRepositoryManager(session, localRepo)
session.transferListener = ConsoleTransferListener()
session.repositoryListener = ConsoleRepositoryListener(System.out, EventBus())
// uncomment to generate dirty trees
// session.setDependencyGraphTransformer( null );
return session
}
fun newRepositorySystemSession(system: RepositorySystem, repo: File, settings: KobaltSettings,
eventBus: EventBus): DefaultRepositorySystemSession {
val session = MavenRepositorySystemUtils.newSession(settings)

View file

@ -0,0 +1,15 @@
package com.beust.kobalt.maven.aether
import org.eclipse.aether.graph.DependencyFilter
import org.eclipse.aether.util.artifact.JavaScopes
object Filters {
val COMPILE_FILTER = DependencyFilter { p0, p1 ->
p0.dependency.scope == "" || p0.dependency.scope == JavaScopes.COMPILE
}
val TEST_FILTER = DependencyFilter { p0, p1 -> p0.dependency.scope == JavaScopes.TEST }
val EXCLUDE_OPTIONAL_FILTER = DependencyFilter { p0, p1 ->
! p0.dependency.optional
}
}

View file

@ -46,7 +46,7 @@ class BuildFileCompiler @Inject constructor(@Assisted("buildFiles") val buildFil
private val SCRIPT_JAR = "buildScript.jar"
fun compileBuildFiles(args: Args): FindProjectResult {
fun compileBuildFiles(args: Args, forceRecompile: Boolean = false): FindProjectResult {
//
// Create the KobaltContext
// Note: can't use apply{} here or each field will refer to itself instead of the constructor field
@ -66,7 +66,7 @@ class BuildFileCompiler @Inject constructor(@Assisted("buildFiles") val buildFil
//
// Find all the projects in the build file, possibly compiling them
//
val projectResult = findProjects(context)
val projectResult = findProjects(context, forceRecompile)
return projectResult
}
@ -76,7 +76,7 @@ class BuildFileCompiler @Inject constructor(@Assisted("buildFiles") val buildFil
class FindProjectResult(val context: KobaltContext, val projects: List<Project>, val pluginUrls: List<URL>,
val taskResult: TaskResult)
private fun findProjects(context: KobaltContext): FindProjectResult {
private fun findProjects(context: KobaltContext, forceRecompile: Boolean): FindProjectResult {
var errorTaskResult: TaskResult? = null
val projects = arrayListOf<Project>()
buildFiles.forEach { buildFile ->
@ -105,7 +105,7 @@ class BuildFileCompiler @Inject constructor(@Assisted("buildFiles") val buildFil
KFiles.saveFile(modifiedBuildFile, parsedBuildFile.buildScriptCode)
val taskResult = maybeCompileBuildFile(context, BuildFile(Paths.get(modifiedBuildFile.path),
"Modified ${Constants.BUILD_FILE_NAME}", buildFile.realPath),
buildScriptJarFile, pluginUrls)
buildScriptJarFile, pluginUrls, forceRecompile)
if (taskResult.success) {
projects.addAll(buildScriptUtil.runBuildScriptJarFile(buildScriptJarFile, pluginUrls, context))
} else {
@ -124,7 +124,7 @@ class BuildFileCompiler @Inject constructor(@Assisted("buildFiles") val buildFil
}
private fun maybeCompileBuildFile(context: KobaltContext, buildFile: BuildFile, buildScriptJarFile: File,
pluginUrls: List<URL>) : TaskResult {
pluginUrls: List<URL>, forceRecompile: Boolean) : TaskResult {
kobaltLog(2, "Running build file ${buildFile.name} jar: $buildScriptJarFile")
// If the user specifed --profiles, always recompile the build file since we don't know if
@ -135,8 +135,8 @@ class BuildFileCompiler @Inject constructor(@Assisted("buildFiles") val buildFil
// compiled with.
val bs = BuildScriptJarFile(buildScriptJarFile)
val same = bs.sameProfiles(args.profiles)
if (same && buildScriptUtil.isUpToDate(buildFile, buildScriptJarFile)) {
kobaltLog(2, " Build file is up to date")
if (same && ! forceRecompile && buildScriptUtil.isUpToDate(buildFile, buildScriptJarFile)) {
kobaltLog(2, " Build file $buildScriptJarFile is up to date")
return TaskResult()
} else {
kobaltLog(2, " Need to recompile ${buildFile.name}")

View file

@ -13,6 +13,7 @@ import com.beust.kobalt.internal.build.BuildFile
import com.beust.kobalt.misc.KFiles
import com.beust.kobalt.misc.Topological
import com.beust.kobalt.misc.kobaltLog
import com.beust.kobalt.misc.warn
import com.beust.kobalt.plugin.KobaltPlugin
import com.google.inject.Inject
import java.io.File
@ -63,11 +64,15 @@ class BuildScriptUtil @Inject constructor(val plugins: Plugins, val files: KFile
val name = entry.name;
if (name.endsWith(".class")) {
val className = name.substring(0, name.length - 6).replace("/", ".")
val cl : Class<*>? = classLoader.loadClass(className)
if (cl != null) {
classes.add(cl)
} else {
throw KobaltException("Couldn't instantiate $className")
try {
val cl: Class<*>? = classLoader.loadClass(className)
if (cl != null) {
classes.add(cl)
} else {
throw KobaltException("Couldn't instantiate $className")
}
} catch(ex: ClassNotFoundException) {
warn("Couldn't find class $className")
}
}
entry = stream.nextJarEntry;

View file

@ -162,8 +162,8 @@ class ParsedBuildFile(val buildFile: BuildFile, val context: KobaltContext, val
//
// Compile the jar file
//
val kotlintDeps = dependencyManager.calculateDependencies(null, context)
val deps: List<String> = kotlintDeps.map { it.jarFile.get().absolutePath }
val kotlinDeps = dependencyManager.calculateDependencies(null, context)
val deps: List<String> = kotlinDeps.map { it.jarFile.get().absolutePath }
val outputJar = File(buildScriptJarFile.absolutePath)
val result = kotlinCompilePrivate {
classpath(files.kobaltJar)

View file

@ -112,7 +112,7 @@ class ApplicationPlugin @Inject constructor(val configActor: ConfigActor<Applica
val allDependencies = project.compileDependencies + project.compileRuntimeDependencies
val allTheDependencies =
dependencyManager.calculateDependencies(project, context,
listOf(Scope.COMPILE, Scope.RUNTIME),
scopes = listOf(Scope.COMPILE, Scope.RUNTIME),
passedDependencies = allDependencies)
.map { it.jarFile.get().path }
allDeps.addAll(allTheDependencies)

View file

@ -6,9 +6,11 @@ import com.beust.kobalt.TestModule
import com.beust.kobalt.api.IClasspathDependency
import com.beust.kobalt.api.Kobalt
import com.beust.kobalt.app.BuildFileCompiler
import com.beust.kobalt.maven.aether.Filters
import com.beust.kobalt.maven.aether.Scope
import com.google.inject.Inject
import org.assertj.core.api.Assertions.assertThat
import org.eclipse.aether.util.filter.AndDependencyFilter
import org.testng.annotations.Guice
import org.testng.annotations.Test
@ -19,7 +21,17 @@ class DependencyManagerTest @Inject constructor(val dependencyManager: Dependenc
private fun assertContains(dependencies: List<IClasspathDependency>, vararg ids: String) {
ids.forEach { id ->
assertThat(dependencies.any { it.id.contains(id) }).isTrue()
if (! dependencies.any { it.id.contains(id) }) {
throw AssertionError("Couldn't find $id in $dependencies")
}
}
}
private fun assertDoesNotContain(dependencies: List<IClasspathDependency>, vararg ids: String) {
ids.forEach { id ->
if (dependencies.any { it.id.contains(id) }) {
throw AssertionError("$id should not be found in $dependencies")
}
}
}
@ -27,87 +39,118 @@ class DependencyManagerTest @Inject constructor(val dependencyManager: Dependenc
fun testScopeDependenciesShouldBeDownloaded() {
val testDeps = listOf(dependencyManager.create("org.testng:testng:6.9.11"))
val filter = AndDependencyFilter(Filters.EXCLUDE_OPTIONAL_FILTER, Filters.COMPILE_FILTER)
// Should only resolve to TestNG
dependencyManager.transitiveClosure(testDeps, listOf(Scope.COMPILE)).let { dependencies ->
dependencyManager.transitiveClosure(testDeps, filter).let { dependencies ->
assertThat(dependencies.any { it.id.contains(":jcommander:") }).isFalse()
assertContains(dependencies, ":testng:")
}
// Should resolve to TestNG and its dependencies
dependencyManager.transitiveClosure(testDeps, listOf(Scope.TEST)).let { dependencies ->
dependencyManager.transitiveClosure(testDeps).let { dependencies ->
assertContains(dependencies, ":jcommander:")
assertContains(dependencies, ":bsh:")
assertContains(dependencies, ":ant:")
assertContains(dependencies, ":ant-launcher:")
assertContains(dependencies, ":testng:")
}
}
@Test
fun honorRuntimeDependenciesBetweenProjects() {
Kobalt.context = null
val buildFileString = """
import com.beust.kobalt.*
val lib = project {
name = "lib"
val lib1 = project {
name = "lib1"
dependencies {
compile("org.testng:testng:6.9.11")
runtime("com.beust:jcommander:1.48")
compile("com.beust:klaxon:0.26",
"com.beust:jcommander:1.48")
}
}
val p = project(lib) {
name = "transitive"
val p = project(lib1) {
name = "transitive1"
}
"""
val compileResult = compileBuildFile(buildFileString, Args(), compilerFactory)
val compileResult = compileBuildFile(sharedBuildFile, Args(), compilerFactory)
val project2 = compileResult.projects[1]
val dependencies = dependencyManager.calculateDependencies(project2, Kobalt.context!!,
listOf(Scope.COMPILE, Scope.RUNTIME))
assertContains(dependencies, ":testng:")
assertContains(dependencies, ":jcommander:")
val dependencies = dependencyManager.calculateDependencies(project2, Kobalt.context!!, Filters.COMPILE_FILTER)
assertContains(dependencies, ":klaxon:")
assertContains(dependencies, ":guice:")
assertDoesNotContain(dependencies, ":guave:")
}
val sharedBuildFile = """
import com.beust.kobalt.*
val lib2 = project {
name = "lib2"
dependencies {
// pick dependencies that don't have dependencies themselves, to avoid interferences
compile("com.beust:klaxon:0.27",
"com.google.inject:guice:4.0")
runtime("com.beust:jcommander:1.48")
compileOptional("junit:junit:4.12")
}
}
val p = project(lib2) {
name = "transitive2"
}
"""
@Test
fun honorRuntimeDependenciesBetweenProjects2() {
val buildFileString = """
import com.beust.kobalt.*
val lib = project {
name = "lib"
val lib2 = project {
name = "lib2"
dependencies {
compile("org.testng:testng:6.9.11")
// pick dependencies that don't have dependencies themselves, to avoid interferences
compile("com.beust:klaxon:0.27",
"com.google.inject:guice:4.0)
runtime("com.beust:jcommander:1.48")
}
}
val p = project(lib) {
name = "transitive"
val p = project(lib2) {
name = "transitive2"
}
"""
val compileResult = compileBuildFile(buildFileString, Args(), compilerFactory)
val compileResult = compileBuildFile(sharedBuildFile, Args(), compilerFactory)
val project2 = compileResult.projects[1]
dependencyManager2.resolve(project2, Kobalt.context!!, isTest = false,
passedScopeFilters = listOf(Scope.COMPILE, Scope.RUNTIME)).let { dependencies ->
assertThat(dependencies.size).isEqualTo(4)
assertContains(dependencies, ":testng:")
assertContains(dependencies, ":jcommander:")
}
Kobalt.context!!.let { context ->
dependencyManager2.resolve(project2, context, isTest = false,
passedScopes = listOf(Scope.COMPILE)).let { dependencies ->
assertContains(dependencies, ":klaxon:jar:0.27")
assertContains(dependencies, ":guice:")
assertDoesNotContain(dependencies, ":jcommander:")
assertDoesNotContain(dependencies, ":junit:")
}
dependencyManager2.resolve(project2, Kobalt.context!!, isTest = false,
passedScopeFilters = listOf(Scope.COMPILE)).let { dependencies ->
assertThat(dependencies.size).isEqualTo(3)
assertContains(dependencies, ":testng:")
}
dependencyManager2.resolve(project2, context, isTest = false,
passedScopes = listOf(Scope.RUNTIME)).let { dependencies ->
assertContains(dependencies, ":jcommander:")
assertDoesNotContain(dependencies, ":klaxon:jar:0.27")
assertDoesNotContain(dependencies, ":guice:")
assertDoesNotContain(dependencies, ":junit:")
}
dependencyManager2.resolve(project2, context, isTest = false,
passedScopes = listOf(Scope.COMPILE, Scope.RUNTIME)).let { dependencies ->
assertContains(dependencies, ":klaxon:")
assertContains(dependencies, ":jcommander:")
assertContains(dependencies, ":guice:")
assertDoesNotContain(dependencies, ":junit:")
}
dependencyManager2.resolve(project2, Kobalt.context!!, isTest = false,
passedScopeFilters = listOf(Scope.RUNTIME)).let { dependencies ->
assertThat(dependencies.size).isEqualTo(3)
assertContains(dependencies, ":jcommander:")
}
}

View file

@ -147,7 +147,7 @@ class DownloadTest @Inject constructor(
@Test
fun parentPomTest() {
// Resolve com.squareup.retrofit2:converter-moshi:2.0.0
// Resolve com.squareup.retrofit2:converter-moshi:1.1.0
// This id has a parent pom which defines moshi version to be 1.1.0. Make sure that this
// version is being fetched instead of moshi:1.2.0-SNAPSHOT (which gets discarded anyway
// since snapshots are not allowed to be returned when looking up a versionless id)

View file

@ -0,0 +1,55 @@
package com.beust.kobalt.misc
import com.beust.kobalt.TestModule
import com.beust.kobalt.maven.DependencyManager
import com.beust.kobalt.maven.aether.Booter
import com.beust.kobalt.maven.aether.KobaltAether
import com.google.inject.Inject
import org.assertj.core.api.Assertions.assertThat
import org.eclipse.aether.artifact.DefaultArtifact
import org.eclipse.aether.collection.CollectRequest
import org.eclipse.aether.graph.Dependency
import org.eclipse.aether.repository.RemoteRepository
import org.eclipse.aether.resolution.DependencyRequest
import org.eclipse.aether.util.artifact.JavaScopes
import org.testng.annotations.Guice
import org.testng.annotations.Test
@Guice(modules = arrayOf(TestModule::class))
class AetherTest {
@Inject
lateinit var kobaltAether: KobaltAether
@Inject
lateinit var dependencyManager: DependencyManager
@Test
fun aetherShouldNotIncludeOptionalDependencies() {
val system = Booter.newRepositorySystem()
val session = Booter.newRepositorySystemSession(system)
val artifact = DefaultArtifact("com.squareup.retrofit2:converter-jackson:jar:2.1.0")
val collectRequest = CollectRequest().apply {
root = Dependency(artifact, JavaScopes.COMPILE)
repositories = listOf(
RemoteRepository.Builder("Maven", "default", "http://repo1.maven.org/maven2/").build()
)
}
val dependencyRequest = DependencyRequest(collectRequest, null)
val artifactResults = system.resolveDependencies(session, dependencyRequest).artifactResults
// Make sure that com.google.android is not included (it's an optional dependency of retrofit2)
assertThat(artifactResults.none { it.toString().contains("android") })
}
@Test
fun kobaltAetherShouldNotIncludeOptionalDependencies() {
val dep = kobaltAether.create("com.squareup.retrofit2:converter-jackson:jar:2.1.0", optional = false)
val closure = dependencyManager.transitiveClosure(listOf(dep))
// Make sure that com.google.android is not included (it's an optional dependency of retrofit2)
assertThat(closure.none { it.toString().contains("android") })
}
}