mirror of https://github.com/sbt/sbt.git
Add tasks / settings allowing using other SBT sources as a repository
This commit is contained in:
parent
0f914cf781
commit
16225d98e6
|
|
@ -99,6 +99,18 @@ class Helper(
|
|||
MavenRepository("https://repo1.maven.org/maven2")
|
||||
)
|
||||
|
||||
val sourceDirectories = common.sources.map { path =>
|
||||
val subDir = "target/repository"
|
||||
val dir = new File(path)
|
||||
val repoDir = new File(dir, subDir)
|
||||
if (!dir.exists())
|
||||
Console.err.println(s"Warning: sources $path not found")
|
||||
else if (!repoDir.exists())
|
||||
Console.err.println(s"Warning: directory $subDir not found under sources path $path")
|
||||
|
||||
repoDir
|
||||
}
|
||||
|
||||
val repositoriesValidation = CacheParse.repositories(common.repository).map { repos0 =>
|
||||
|
||||
var repos = (if (common.noDefault) Nil else defaultRepositories) ++ repos0
|
||||
|
|
@ -119,10 +131,14 @@ class Helper(
|
|||
}
|
||||
|
||||
val repositories = repositoriesValidation match {
|
||||
case Success(repos) => repos
|
||||
case Success(repos) =>
|
||||
val sourceRepositories = sourceDirectories.map(dir =>
|
||||
MavenRepository(dir.toURI.toString, changing = Some(true))
|
||||
)
|
||||
sourceRepositories ++ repos
|
||||
case Failure(errors) =>
|
||||
prematureExit(
|
||||
s"Error parsing repositories:\n${errors.list.map(" "+_).mkString("\n")}"
|
||||
s"Error with repositories:\n${errors.list.map(" "+_).mkString("\n")}"
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -140,8 +156,36 @@ class Helper(
|
|||
s"Cannot parse forced versions:\n" + forceVersionErrors.map(" "+_).mkString("\n")
|
||||
}
|
||||
|
||||
val sourceRepositoryForceVersions = sourceDirectories.flatMap { base =>
|
||||
|
||||
// FIXME Also done in the plugin module
|
||||
|
||||
def pomDirComponents(f: File, components: Vector[String]): Stream[Vector[String]] =
|
||||
if (f.isDirectory) {
|
||||
val components0 = components :+ f.getName
|
||||
Option(f.listFiles()).toStream.flatten.flatMap(pomDirComponents(_, components0))
|
||||
} else if (f.getName.endsWith(".pom"))
|
||||
Stream(components)
|
||||
else
|
||||
Stream.empty
|
||||
|
||||
Option(base.listFiles())
|
||||
.toVector
|
||||
.flatten
|
||||
.flatMap(pomDirComponents(_, Vector()))
|
||||
// at least 3 for org / name / version - the contrary should not happen, but who knows
|
||||
.filter(_.length >= 3)
|
||||
.map { components =>
|
||||
val org = components.dropRight(2).mkString(".")
|
||||
val name = components(components.length - 2)
|
||||
val version = components.last
|
||||
|
||||
Module(org, name) -> version
|
||||
}
|
||||
}
|
||||
|
||||
val forceVersions = {
|
||||
val grouped = forceVersions0
|
||||
val grouped = (forceVersions0 ++ sourceRepositoryForceVersions)
|
||||
.groupBy { case (mod, _) => mod }
|
||||
.map { case (mod, l) => mod -> l.map { case (_, version) => version } }
|
||||
|
||||
|
|
|
|||
|
|
@ -24,9 +24,12 @@ case class CommonOptions(
|
|||
@Help("Maximum number of resolution iterations (specify a negative value for unlimited, default: 100)")
|
||||
@Short("N")
|
||||
maxIterations: Int = 100,
|
||||
@Help("Repositories - for multiple repositories, separate with comma and/or repeat this option (e.g. -r central,ivy2local -r sonatype-snapshots, or equivalently -r central,ivy2local,sonatype-snapshots)")
|
||||
@Help("Repository - for multiple repositories, separate with comma and/or add this option multiple times (e.g. -r central,ivy2local -r sonatype-snapshots, or equivalently -r central,ivy2local,sonatype-snapshots)")
|
||||
@Short("r")
|
||||
repository: List[String],
|
||||
@Help("Source repository - for multiple repositories, separate with comma and/or add this option multiple times")
|
||||
@Short("R")
|
||||
sources: List[String],
|
||||
@Help("Do not add default repositories (~/.ivy2/local, and Central)")
|
||||
noDefault: Boolean = false,
|
||||
@Help("Modify names in Maven repository paths for SBT plugins")
|
||||
|
|
|
|||
|
|
@ -0,0 +1,77 @@
|
|||
package coursier.maven
|
||||
|
||||
import coursier.core.{ Dependency, Project }
|
||||
|
||||
object WritePom {
|
||||
|
||||
def project(proj: Project, packaging: Option[String]) = {
|
||||
|
||||
def dependencyNode(config: String, dep: Dependency) = {
|
||||
<dependency>
|
||||
<groupId>{dep.module.organization}</groupId>
|
||||
<artifactId>{dep.module.name}</artifactId>
|
||||
{
|
||||
if (dep.version.isEmpty)
|
||||
Nil
|
||||
else
|
||||
Seq(<version>{dep.version}</version>)
|
||||
}
|
||||
{
|
||||
if (config.isEmpty)
|
||||
Nil
|
||||
else
|
||||
Seq(<scope>{config}</scope>)
|
||||
}
|
||||
</dependency>
|
||||
}
|
||||
|
||||
<project>
|
||||
// parent
|
||||
<groupId>{proj.module.organization}</groupId>
|
||||
<artifactId>{proj.module.name}</artifactId>
|
||||
{
|
||||
packaging
|
||||
.map(p => <packaging>{p}</packaging>)
|
||||
.toSeq
|
||||
}
|
||||
<description>{proj.info.description}</description>
|
||||
<url>{proj.info.homePage}</url>
|
||||
<version>{proj.version}</version>
|
||||
// licenses
|
||||
<name>{proj.module.name}</name>
|
||||
<organization>
|
||||
<name>{proj.module.name}</name>
|
||||
<url>{proj.info.homePage}</url>
|
||||
</organization>
|
||||
// SCM
|
||||
// developers
|
||||
{
|
||||
if (proj.dependencies.isEmpty)
|
||||
Nil
|
||||
else
|
||||
<dependencies>{
|
||||
proj.dependencies.map {
|
||||
case (config, dep) =>
|
||||
dependencyNode(config, dep)
|
||||
}
|
||||
}</dependencies>
|
||||
}
|
||||
{
|
||||
if (proj.dependencyManagement.isEmpty)
|
||||
Nil
|
||||
else
|
||||
<dependencyManagement>
|
||||
<dependencies>{
|
||||
proj.dependencyManagement.map {
|
||||
case (config, dep) =>
|
||||
dependencyNode(config, dep)
|
||||
}
|
||||
}</dependencies>
|
||||
</dependencyManagement>
|
||||
}
|
||||
// properties
|
||||
// repositories
|
||||
</project>
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -16,6 +16,7 @@ object CoursierPlugin extends AutoPlugin {
|
|||
val coursierArtifactsChecksums = Keys.coursierArtifactsChecksums
|
||||
val coursierCachePolicies = Keys.coursierCachePolicies
|
||||
val coursierVerbosity = Keys.coursierVerbosity
|
||||
val coursierSourceRepositories = Keys.coursierSourceRepositories
|
||||
val coursierResolvers = Keys.coursierResolvers
|
||||
val coursierSbtResolvers = Keys.coursierSbtResolvers
|
||||
val coursierFallbackDependencies = Keys.coursierFallbackDependencies
|
||||
|
|
@ -32,6 +33,11 @@ object CoursierPlugin extends AutoPlugin {
|
|||
|
||||
val coursierDependencyTree = Keys.coursierDependencyTree
|
||||
val coursierDependencyInverseTree = Keys.coursierDependencyInverseTree
|
||||
|
||||
val coursierExport = Keys.coursierExport
|
||||
val coursierExportDirectory = Keys.coursierExportDirectory
|
||||
val coursierExportJavadoc = Keys.coursierExportJavadoc
|
||||
val coursierExportSources = Keys.coursierExportSources
|
||||
}
|
||||
|
||||
import autoImport._
|
||||
|
|
@ -52,6 +58,7 @@ object CoursierPlugin extends AutoPlugin {
|
|||
coursierArtifactsChecksums := Seq(None),
|
||||
coursierCachePolicies := Settings.defaultCachePolicies,
|
||||
coursierVerbosity := Settings.defaultVerbosityLevel,
|
||||
coursierSourceRepositories := Nil,
|
||||
coursierResolvers <<= Tasks.coursierResolversTask,
|
||||
coursierSbtResolvers <<= externalResolvers in updateSbtClassifiers,
|
||||
coursierFallbackDependencies <<= Tasks.coursierFallbackDependenciesTask,
|
||||
|
|
@ -74,7 +81,11 @@ object CoursierPlugin extends AutoPlugin {
|
|||
coursierResolution <<= Tasks.resolutionTask(),
|
||||
coursierSbtClassifiersResolution <<= Tasks.resolutionTask(
|
||||
sbtClassifiers = true
|
||||
)
|
||||
),
|
||||
coursierExport <<= Tasks.coursierExportTask,
|
||||
coursierExportDirectory := baseDirectory.in(ThisBuild).value / "target" / "repository",
|
||||
coursierExportJavadoc := false,
|
||||
coursierExportSources := false
|
||||
) ++
|
||||
inConfig(Compile)(treeSettings) ++
|
||||
inConfig(Test)(treeSettings)
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ object Keys {
|
|||
|
||||
val coursierVerbosity = SettingKey[Int]("coursier-verbosity", "")
|
||||
|
||||
val coursierSourceRepositories = SettingKey[Seq[File]]("coursier-source-repositories")
|
||||
val coursierResolvers = TaskKey[Seq[Resolver]]("coursier-resolvers", "")
|
||||
val coursierSbtResolvers = TaskKey[Seq[Resolver]]("coursier-sbt-resolvers", "")
|
||||
|
||||
|
|
@ -41,4 +42,21 @@ object Keys {
|
|||
"coursier-dependency-inverse-tree",
|
||||
"Prints dependencies and transitive dependencies as an inverted tree (dependees as children)"
|
||||
)
|
||||
|
||||
val coursierExport = TaskKey[Option[File]](
|
||||
"coursier-export",
|
||||
"Generates files allowing using these sources as a source dependency repository"
|
||||
)
|
||||
val coursierExportDirectory = TaskKey[File](
|
||||
"coursier-export-directory",
|
||||
"Base directory for the products of coursierExport"
|
||||
)
|
||||
val coursierExportJavadoc = SettingKey[Boolean](
|
||||
"coursier-export-javadoc",
|
||||
"Build javadoc packages for the coursier source dependency repository"
|
||||
)
|
||||
val coursierExportSources = SettingKey[Boolean](
|
||||
"coursier-export-sources",
|
||||
"Build sources packages for the coursier source dependency repository"
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import coursier.core.Publication
|
|||
import coursier.ivy.IvyRepository
|
||||
import coursier.Keys._
|
||||
import coursier.Structure._
|
||||
import coursier.maven.WritePom
|
||||
import coursier.util.{ Config, Print }
|
||||
import org.apache.ivy.core.module.id.ModuleRevisionId
|
||||
|
||||
|
|
@ -299,6 +300,40 @@ object Tasks {
|
|||
else
|
||||
coursierResolvers.value
|
||||
|
||||
val sourceRepositories = coursierSourceRepositories.value.map { dir =>
|
||||
// FIXME Don't hardcode this path?
|
||||
new File(dir, "target/repository")
|
||||
}
|
||||
|
||||
val sourceRepositoriesForcedDependencies = sourceRepositories.flatMap {
|
||||
base =>
|
||||
|
||||
def pomDirComponents(f: File, components: Vector[String]): Stream[Vector[String]] =
|
||||
if (f.isDirectory) {
|
||||
val components0 = components :+ f.getName
|
||||
Option(f.listFiles()).toStream.flatten.flatMap(pomDirComponents(_, components0))
|
||||
} else if (f.getName.endsWith(".pom"))
|
||||
Stream(components)
|
||||
else
|
||||
Stream.empty
|
||||
|
||||
Option(base.listFiles())
|
||||
.toVector
|
||||
.flatten
|
||||
.flatMap(pomDirComponents(_, Vector()))
|
||||
// at least 3 for org / name / version - the contrary should not happen, but who knows
|
||||
.filter(_.length >= 3)
|
||||
.map { components =>
|
||||
val org = components.dropRight(2).mkString(".")
|
||||
val name = components(components.length - 2)
|
||||
val version = components.last
|
||||
|
||||
Module(org, name) -> version
|
||||
}
|
||||
}
|
||||
|
||||
// TODO Warn about possible duplicated modules from source repositories?
|
||||
|
||||
val verbosityLevel = coursierVerbosity.value
|
||||
|
||||
|
||||
|
|
@ -308,7 +343,12 @@ object Tasks {
|
|||
dep.copy(exclusions = dep.exclusions ++ exclusions)
|
||||
}.toSet,
|
||||
filter = Some(dep => !dep.optional),
|
||||
forceVersions = userForceVersions ++ forcedScalaModules(sv) ++ projects.map(_.moduleVersion)
|
||||
forceVersions =
|
||||
// order matters here
|
||||
userForceVersions ++
|
||||
sourceRepositoriesForcedDependencies ++
|
||||
forcedScalaModules(sv) ++
|
||||
projects.map(_.moduleVersion)
|
||||
)
|
||||
|
||||
if (verbosityLevel >= 2) {
|
||||
|
|
@ -331,12 +371,12 @@ object Tasks {
|
|||
"ivy.home" -> (new File(sys.props("user.home")).toURI.getPath + ".ivy2")
|
||||
) ++ sys.props
|
||||
|
||||
val repositories = Seq(
|
||||
globalPluginsRepo,
|
||||
interProjectRepo
|
||||
) ++ resolvers.flatMap(
|
||||
FromSbt.repository(_, ivyProperties, log)
|
||||
) ++ {
|
||||
val sourceRepositories0 = sourceRepositories.map {
|
||||
base =>
|
||||
MavenRepository(base.toURI.toString, changing = Some(true))
|
||||
}
|
||||
|
||||
val fallbackDependenciesRepositories =
|
||||
if (fallbackDependencies.isEmpty)
|
||||
Nil
|
||||
else {
|
||||
|
|
@ -349,7 +389,12 @@ object Tasks {
|
|||
FallbackDependenciesRepository(map)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
val repositories =
|
||||
Seq(globalPluginsRepo, interProjectRepo) ++
|
||||
sourceRepositories0 ++
|
||||
resolvers.flatMap(FromSbt.repository(_, ivyProperties, log)) ++
|
||||
fallbackDependenciesRepositories
|
||||
|
||||
def resolution = {
|
||||
val pool = Executors.newFixedThreadPool(parallelDownloads, Strategy.DefaultDaemonThreadFactory)
|
||||
|
|
@ -748,4 +793,93 @@ object Tasks {
|
|||
)
|
||||
}
|
||||
|
||||
def coursierExportTask =
|
||||
(
|
||||
sbt.Keys.state,
|
||||
sbt.Keys.thisProjectRef,
|
||||
sbt.Keys.projectID,
|
||||
sbt.Keys.scalaVersion,
|
||||
sbt.Keys.scalaBinaryVersion,
|
||||
sbt.Keys.ivyConfigurations,
|
||||
streams,
|
||||
coursierProject,
|
||||
coursierExportDirectory,
|
||||
coursierExportJavadoc,
|
||||
coursierExportSources
|
||||
).flatMap { (state, projectRef, projId, sv, sbv, ivyConfs, streams, proj, exportDir, exportJavadoc, exportSources) =>
|
||||
|
||||
val javadocPackageTasks =
|
||||
if (exportJavadoc)
|
||||
Seq(Some("javadoc") -> packageDoc)
|
||||
else
|
||||
Nil
|
||||
|
||||
val sourcesPackageTasks =
|
||||
if (exportJavadoc)
|
||||
Seq(Some("sources") -> packageSrc)
|
||||
else
|
||||
Nil
|
||||
|
||||
val packageTasks = Seq(None -> packageBin) ++ javadocPackageTasks ++ sourcesPackageTasks
|
||||
|
||||
val configs = Seq(None -> Compile, Some("tests") -> Test)
|
||||
|
||||
val productTasks =
|
||||
for {
|
||||
(classifierOpt, pkgTask) <- packageTasks
|
||||
(classifierPrefixOpt, config) <- configs
|
||||
if publishArtifact.in(projectRef).in(pkgTask).in(config).getOrElse(state, false)
|
||||
} yield {
|
||||
val classifier = (classifierPrefixOpt.toSeq ++ classifierOpt.toSeq).mkString("-")
|
||||
pkgTask.in(projectRef).in(config).get(state).map((classifier, _))
|
||||
}
|
||||
|
||||
val productTask = sbt.std.TaskExtra.joinTasks(productTasks).join
|
||||
|
||||
val dir = new File(
|
||||
exportDir,
|
||||
s"${proj.module.organization.replace('.', '/')}/${proj.module.name}/${proj.version}"
|
||||
)
|
||||
|
||||
def pom = "<?xml version='1.0' encoding='UTF-8'?>\n" + WritePom.project(proj, Some("jar"))
|
||||
|
||||
val log = streams.log
|
||||
|
||||
productTask.map { products =>
|
||||
|
||||
if (products.isEmpty)
|
||||
None
|
||||
else {
|
||||
|
||||
dir.mkdirs()
|
||||
|
||||
val pomFile = new File(dir, s"${proj.module.name}-${proj.version}.pom")
|
||||
Files.write(pomFile.toPath, pom.getBytes("UTF-8"))
|
||||
log.info(s"Wrote POM file to $pomFile")
|
||||
|
||||
for ((classifier, f) <- products) {
|
||||
|
||||
val suffix = if (classifier.isEmpty) "" else "-" + classifier
|
||||
|
||||
val jarPath = new File(dir, s"${proj.module.name}-${proj.version}$suffix.jar")
|
||||
|
||||
if (jarPath.exists()) {
|
||||
if (!jarPath.delete())
|
||||
log.warn(s"Cannot remove $jarPath")
|
||||
}
|
||||
|
||||
Files.createSymbolicLink(
|
||||
jarPath.toPath,
|
||||
dir.toPath.relativize(f.toPath)
|
||||
)
|
||||
log.info(s"Created symbolic link $jarPath -> $f")
|
||||
}
|
||||
|
||||
// TODO Clean extra files in dir
|
||||
|
||||
Some(exportDir)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue