2010-02-08 05:45:19 +01:00
/* sbt -- Simple Build Tool
* Copyright 2008 , 2009 , 2010 Mark Harrah
*/
2010-01-30 02:31:07 +01:00
// based on Ivy's PomModuleDescriptorWriter, which is Apache Licensed, Version 2.0
// http://www.apache.org/licenses/LICENSE-2.0
2011-11-21 21:38:16 +01:00
package sbt
2010-01-30 02:31:07 +01:00
2011-10-26 18:13:42 +02:00
import java.io.File
2011-11-20 00:17:20 +01:00
// Node needs to be renamed to XNode because the task subproject contains a Node type that will shadow
// scala.xml.Node when generating aggregated API documentation
import scala.xml. { Node => XNode , NodeSeq , PrettyPrinter }
2011-08-27 05:27:03 +02:00
import Configurations.Optional
2010-01-30 02:31:07 +01:00
2010-01-30 16:46:47 +01:00
import org.apache.ivy. { core , plugins , Ivy }
import core.settings.IvySettings
2011-11-21 21:38:16 +01:00
import core.module.descriptor
2011-09-06 13:59:42 +02:00
import descriptor. { DependencyDescriptor , License , ModuleDescriptor , ExcludeRule }
2010-01-30 16:46:47 +01:00
import plugins.resolver. { ChainResolver , DependencyResolver , IBiblioResolver }
2010-01-30 02:31:07 +01:00
2011-09-06 13:59:42 +02:00
class MakePom ( val log : Logger )
2010-01-30 02:31:07 +01:00
{
2011-11-21 21:38:16 +01:00
@deprecated ( "Use `write(Ivy, ModuleDescriptor, ModuleInfo, Option[Iterable[Configuration]], Set[String], NodeSeq, XNode => XNode, MavenRepository => Boolean, Boolean, File)` instead" , "0.11.2" )
2011-11-20 00:17:20 +01:00
def write ( ivy : Ivy , module : ModuleDescriptor , moduleInfo : ModuleInfo , configurations : Option [ Iterable [ Configuration ] ] , extra : NodeSeq , process : XNode => XNode , filterRepositories : MavenRepository => Boolean , allRepositories : Boolean , output : File ) : Unit =
2011-11-21 21:38:16 +01:00
write ( ivy , module , moduleInfo : ModuleInfo , configurations : Option [ Iterable [ Configuration ] ] , Set ( Artifact . DefaultType ) , extra , process , filterRepositories , allRepositories , output )
def write ( ivy : Ivy , module : ModuleDescriptor , moduleInfo : ModuleInfo , configurations : Option [ Iterable [ Configuration ] ] , includeTypes : Set [ String ] , extra : NodeSeq , process : XNode => XNode , filterRepositories : MavenRepository => Boolean , allRepositories : Boolean , output : File ) : Unit =
write ( process ( toPom ( ivy , module , moduleInfo , configurations , includeTypes , extra , filterRepositories , allRepositories ) ) , output )
2011-07-12 13:47:31 +02:00
// use \n as newline because toString uses PrettyPrinter, which hard codes line endings to be \n
2011-11-20 00:17:20 +01:00
def write ( node : XNode , output : File ) : Unit = write ( toString ( node ) , output , "\n" )
2011-07-12 13:47:31 +02:00
def write ( xmlString : String , output : File , newline : String )
2010-01-30 02:31:07 +01:00
{
2011-10-27 01:28:26 +02:00
IO . write ( output , "<?xml version='1.0' encoding='" + IO . utf8 . name + "'?>" + newline + xmlString )
2010-01-30 02:31:07 +01:00
}
2011-11-20 00:17:20 +01:00
def toString ( node : XNode ) : String = new PrettyPrinter ( 1000 , 4 ) . format ( node )
2011-11-21 21:38:16 +01:00
@deprecated ( "Use `toPom(Ivy, ModuleDescriptor, ModuleInfo, Option[Iterable[Configuration]], Set[String], NodeSeq, MavenRepository => Boolean, Boolean)` instead" , "0.11.2" )
2011-11-20 00:17:20 +01:00
def toPom ( ivy : Ivy , module : ModuleDescriptor , moduleInfo : ModuleInfo , configurations : Option [ Iterable [ Configuration ] ] , extra : NodeSeq , filterRepositories : MavenRepository => Boolean , allRepositories : Boolean ) : XNode =
2011-11-21 21:38:16 +01:00
toPom ( ivy , module , moduleInfo , configurations , Set ( Artifact . DefaultType ) , extra , filterRepositories , allRepositories )
def toPom ( ivy : Ivy , module : ModuleDescriptor , moduleInfo : ModuleInfo , configurations : Option [ Iterable [ Configuration ] ] , includeTypes : Set [ String ] , extra : NodeSeq , filterRepositories : MavenRepository => Boolean , allRepositories : Boolean ) : XNode =
2010-01-30 02:31:07 +01:00
( < project xmlns = "http://maven.apache.org/POM/4.0.0" xmlns : xsi = "http://www.w3.org/2001/XMLSchema-instance" xsi : schemaLocation = "http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" >
< modelVersion > 4.0 . 0 </ modelVersion >
{ makeModuleID ( module ) }
2011-07-31 19:47:35 +02:00
< name > { moduleInfo . nameFormal } </ name >
2011-09-02 20:46:00 +02:00
{ makeStartYear ( moduleInfo ) }
2011-07-31 19:47:35 +02:00
{ makeOrganization ( moduleInfo ) }
2010-01-30 02:31:07 +01:00
{ extra }
2011-10-02 01:46:51 +02:00
{
val deps = depsInConfs ( module , configurations )
makeProperties ( module , deps ) ++
2011-11-21 21:38:16 +01:00
makeDependencies ( deps , includeTypes )
2011-10-02 01:46:51 +02:00
}
2011-06-13 03:32:51 +02:00
{ makeRepositories ( ivy . getSettings , allRepositories , filterRepositories ) }
2010-01-30 02:31:07 +01:00
</ project > )
def makeModuleID ( module : ModuleDescriptor ) : NodeSeq =
{
val mrid = moduleDescriptor ( module )
2011-11-19 19:40:43 +01:00
val a : NodeSeq =
2010-01-30 02:31:07 +01:00
( < groupId > { mrid . getOrganisation } </ groupId >
< artifactId > { mrid . getName } </ artifactId >
2010-03-30 15:19:36 +02:00
< packaging > { packaging ( module ) } </ packaging > )
2010-01-30 02:31:07 +01:00
val b : NodeSeq =
( ( description ( module . getDescription ) ++
homePage ( module . getHomePage ) ++
revision ( mrid . getRevision ) ++
licenses ( module . getLicenses ) ) : NodeSeq )
a ++ b
}
2011-09-02 20:46:00 +02:00
2011-11-19 20:21:04 +01:00
def makeStartYear ( moduleInfo : ModuleInfo ) : NodeSeq =
moduleInfo . startYear match {
case Some ( y ) => < inceptionYear > { y } </ inceptionYear >
case _ => NodeSeq . Empty
}
2011-07-31 19:47:35 +02:00
def makeOrganization ( moduleInfo : ModuleInfo ) : NodeSeq =
{
< organization >
< name > { moduleInfo . organizationName } </ name >
2011-11-19 20:21:04 +01:00
{ moduleInfo . organizationHomepage match {
case Some ( h ) => < url > { h } </ url >
case _ => NodeSeq . Empty
} }
2011-07-31 19:47:35 +02:00
</ organization >
}
2011-10-02 01:46:51 +02:00
def makeProperties ( module : ModuleDescriptor , dependencies : Seq [ DependencyDescriptor ] ) : NodeSeq =
2011-07-28 01:50:59 +02:00
{
val extra = IvySbt . getExtraAttributes ( module )
2011-10-02 01:46:51 +02:00
val depExtra = CustomPomParser . writeDependencyExtra ( dependencies ) . mkString ( "\n" )
val allExtra = if ( depExtra . isEmpty ) extra else extra . updated ( CustomPomParser . ExtraAttributesKey , depExtra )
if ( allExtra . isEmpty ) NodeSeq . Empty else makeProperties ( allExtra )
2011-07-28 01:50:59 +02:00
}
def makeProperties ( extra : Map [ String ,String ] ) : NodeSeq =
< properties > {
for ( ( key , value ) <- extra ) yield
( < x > { value } </ x > ) . copy ( label = key )
} </ properties >
2010-01-30 02:31:07 +01:00
def description ( d : String ) = if ( ( d eq null ) || d . isEmpty ) NodeSeq . Empty else < description > { d } </ description >
def licenses ( ls : Array [ License ] ) = if ( ls == null || ls . isEmpty ) NodeSeq . Empty else < licenses > { ls . map ( license ) } </ licenses >
def license ( l : License ) =
< license >
< name > { l . getName } </ name >
< url > { l . getUrl } </ url >
2011-07-28 20:39:21 +02:00
< distribution > repo </ distribution >
2010-01-30 02:31:07 +01:00
</ license >
def homePage ( homePage : String ) = if ( homePage eq null ) NodeSeq . Empty else < url > { homePage } </ url >
def revision ( version : String ) = if ( version ne null ) < version > { version } </ version > else NodeSeq . Empty
2010-03-30 15:19:36 +02:00
def packaging ( module : ModuleDescriptor ) =
module . getAllArtifacts match
{
case Array ( ) => "pom"
case Array ( x ) => x . getType
case xs =>
2011-07-31 00:11:20 +02:00
val types = xs . map ( _ . getType ) . toList . filterNot ( IgnoreTypes )
types match {
case Nil => Artifact . PomType
case xs if xs . contains ( Artifact . DefaultType ) => Artifact . DefaultType
case x : : xs => x
}
2010-03-30 15:19:36 +02:00
}
2011-07-31 00:11:20 +02:00
val IgnoreTypes : Set [ String ] = Set ( Artifact . SourceType , Artifact . DocType , Artifact . PomType )
2010-01-30 02:31:07 +01:00
2011-11-21 21:38:16 +01:00
def makeDependencies ( dependencies : Seq [ DependencyDescriptor ] , includeTypes : Set [ String ] ) : NodeSeq =
2011-10-02 01:46:51 +02:00
if ( dependencies . isEmpty )
NodeSeq . Empty
2010-01-30 02:31:07 +01:00
else
< dependencies >
2011-11-21 21:38:16 +01:00
{ dependencies . map ( makeDependency ( _ , includeTypes ) ) }
2010-01-30 02:31:07 +01:00
</ dependencies >
2011-11-21 21:38:16 +01:00
def makeDependency ( dependency : DependencyDescriptor , includeTypes : Set [ String ] ) : NodeSeq =
2010-01-30 02:31:07 +01:00
{
val mrid = dependency . getDependencyRevisionId
< dependency >
< groupId > { mrid . getOrganisation } </ groupId >
< artifactId > { mrid . getName } </ artifactId >
< version > { mrid . getRevision } </ version >
2011-09-06 13:59:42 +02:00
{ scopeAndOptional ( dependency ) }
2011-11-21 21:38:16 +01:00
{ classifier ( dependency , includeTypes ) }
2011-11-19 20:21:04 +01:00
{ exclusions ( dependency ) }
2010-01-30 02:31:07 +01:00
</ dependency >
}
2011-11-21 21:38:16 +01:00
def classifier ( dependency : DependencyDescriptor , includeTypes : Set [ String ] ) : NodeSeq =
2011-10-10 18:10:47 +02:00
{
2011-11-21 21:38:16 +01:00
val jarDep = dependency . getAllDependencyArtifacts . filter ( d => includeTypes ( d . getType ) ) . headOption
2011-11-19 19:40:43 +01:00
jarDep match {
case Some ( a ) => {
val cl = a . getExtraAttribute ( "classifier" )
if ( cl != null ) < classifier > { cl } </ classifier > else NodeSeq . Empty
}
case _ => NodeSeq . Empty
}
2011-10-10 18:10:47 +02:00
}
2011-08-27 05:27:03 +02:00
def scopeAndOptional ( dependency : DependencyDescriptor ) : NodeSeq =
{
val ( scope , opt ) = getScopeAndOptional ( dependency . getModuleConfigurations )
scopeElem ( scope ) ++ optionalElem ( opt )
}
def scopeElem ( scope : Option [ String ] ) : NodeSeq = scope match {
case Some ( s ) => < scope > { s } </ scope >
case None => NodeSeq . Empty
}
def optionalElem ( opt : Boolean ) = if ( opt ) < optional > true </ optional > else NodeSeq . Empty
2010-01-30 02:31:07 +01:00
def moduleDescriptor ( module : ModuleDescriptor ) = module . getModuleRevisionId
2011-08-27 05:27:03 +02:00
def getScopeAndOptional ( confs : Array [ String ] ) : ( Option [ String ] , Boolean ) =
2010-01-30 02:31:07 +01:00
{
2011-08-27 05:27:03 +02:00
val ( opt , notOptional ) = confs . partition ( _ == Optional . name )
val defaultNotOptional = Configurations . defaultMavenConfigurations . find ( notOptional contains _ . name )
val scope = defaultNotOptional match
2010-01-30 02:31:07 +01:00
{
2011-08-27 05:27:03 +02:00
case Some ( conf ) => Some ( conf . name )
2010-01-30 02:31:07 +01:00
case None =>
2011-08-27 05:27:03 +02:00
if ( notOptional . isEmpty || notOptional ( 0 ) == Configurations . Default . name )
None
2010-01-30 02:31:07 +01:00
else
2011-08-27 05:27:03 +02:00
Option ( notOptional ( 0 ) )
2010-01-30 02:31:07 +01:00
}
2011-08-27 05:27:03 +02:00
( scope , ! opt . isEmpty )
2010-01-30 02:31:07 +01:00
}
2010-01-30 16:46:47 +01:00
2011-11-19 20:21:04 +01:00
def exclusions ( dependency : DependencyDescriptor ) : NodeSeq =
{
val excl = dependency . getExcludeRules ( dependency . getModuleConfigurations )
val ( warns , excls ) = List . separate ( excl . map ( makeExclusion ) )
if ( ! warns . isEmpty ) log . warn ( warns . mkString ( IO . Newline ) )
if ( ! excls . isEmpty ) < exclusions > { excls } </ exclusions >
else NodeSeq . Empty
}
2011-09-06 13:59:42 +02:00
def makeExclusion ( exclRule : ExcludeRule ) : Either [ String , NodeSeq ] =
{
val m = exclRule . getId . getModuleId
val ( g , a ) = ( m . getOrganisation , m . getName )
if ( g == null || g . isEmpty || g == "*" || a . isEmpty || a == "*" )
Left ( "Skipped generating '<exclusion/>' for %s. Dependency exclusion should have both 'org' and 'module' to comply with Maven POM's schema." . format ( m ) )
else
Right (
< exclusion >
< groupId > { g } </ groupId >
< artifactId > { a } </ artifactId >
</ exclusion >
)
}
2011-06-13 03:32:51 +02:00
def makeRepositories ( settings : IvySettings , includeAll : Boolean , filterRepositories : MavenRepository => Boolean ) =
2010-01-30 16:46:47 +01:00
{
class MavenRepo ( name : String , snapshots : Boolean , releases : Boolean )
2011-06-13 03:32:51 +02:00
val repositories = if ( includeAll ) allResolvers ( settings ) else resolvers ( settings . getDefaultResolver )
2010-01-30 16:46:47 +01:00
val mavenRepositories =
repositories . flatMap {
2010-04-24 03:20:07 +02:00
case m : IBiblioResolver if m . isM2compatible && m . getRoot != IBiblioResolver . DEFAULT_M2_ROOT =>
MavenRepository ( m . getName , m . getRoot ) : : Nil
2010-01-30 16:46:47 +01:00
case _ => Nil
}
2010-04-24 03:20:07 +02:00
val repositoryElements = mavenRepositories . filter ( filterRepositories ) . map ( mavenRepository )
2010-02-16 18:30:42 +01:00
if ( repositoryElements . isEmpty ) repositoryElements else < repositories > { repositoryElements } </ repositories >
2010-01-30 16:46:47 +01:00
}
2011-06-13 03:32:51 +02:00
def allResolvers ( settings : IvySettings ) : Seq [ DependencyResolver ] = flatten ( castResolvers ( settings . getResolvers ) ) . distinct
2010-01-30 16:46:47 +01:00
def flatten ( rs : Seq [ DependencyResolver ] ) : Seq [ DependencyResolver ] = if ( rs eq null ) Nil else rs . flatMap ( resolvers )
def resolvers ( r : DependencyResolver ) : Seq [ DependencyResolver ] =
r match { case c : ChainResolver => flatten ( castResolvers ( c . getResolvers ) ) ; case _ => r : : Nil }
// cast the contents of a pre-generics collection
private def castResolvers ( s : java.util.Collection [ _ ] ) : Seq [ DependencyResolver ] =
s . toArray . map ( _ . asInstanceOf [ DependencyResolver ] )
def toID ( name : String ) = checkID ( name . filter ( isValidIDCharacter ) . mkString , name )
def isValidIDCharacter ( c : Char ) = c . isLetterOrDigit
private def checkID ( id : String , name : String ) = if ( id . isEmpty ) error ( "Could not convert '" + name + "' to an ID" ) else id
2011-11-20 00:17:20 +01:00
def mavenRepository ( repo : MavenRepository ) : XNode =
2010-04-24 03:20:07 +02:00
mavenRepository ( toID ( repo . name ) , repo . name , repo . root )
2011-11-20 00:17:20 +01:00
def mavenRepository ( id : String , name : String , root : String ) : XNode =
2010-01-30 16:46:47 +01:00
< repository >
< id > { id } </ id >
< name > { name } </ name >
< url > { root } </ url >
2011-07-29 22:22:02 +02:00
< layout > { if ( name == JavaNet1Repository . name ) "legacy" else "default" } </ layout >
2010-01-30 16:46:47 +01:00
</ repository >
2010-03-30 15:19:36 +02:00
/* * Retain dependencies only with the configurations given, or all public configurations of `module` if `configurations` is None.
* This currently only preserves the information required by makePom */
private def depsInConfs ( module : ModuleDescriptor , configurations : Option [ Iterable [ Configuration ] ] ) : Seq [ DependencyDescriptor ] =
{
val keepConfigurations = IvySbt . getConfigurations ( module , configurations )
val keepSet = Set ( keepConfigurations . toSeq : _ * )
def translate ( dependency : DependencyDescriptor ) =
{
val keep = dependency . getModuleConfigurations . filter ( keepSet . contains )
if ( keep . isEmpty )
None
else // TODO: translate the dependency to contain only configurations to keep
Some ( dependency )
}
module . getDependencies flatMap translate
}
2011-10-10 18:10:47 +02:00
}