diff options
author | Jan Christopher Vogt <oss.nsp@cvogt.org> | 2016-04-04 00:46:06 -0400 |
---|---|---|
committer | Jan Christopher Vogt <oss.nsp@cvogt.org> | 2016-04-04 00:46:06 -0400 |
commit | 2d1a51d64aaca9ad00057a5bb822a50158b67429 (patch) | |
tree | 8fb5377f275bb639dd5a25ec5dcb37148f0faf10 | |
parent | 2b52377b6f6fbbc45ef705f33088e8e414a77105 (diff) | |
parent | 6e65120d99b6480b4d2577bde61e8754a60a2ee6 (diff) | |
download | cbt-2d1a51d64aaca9ad00057a5bb822a50158b67429.tar.gz cbt-2d1a51d64aaca9ad00057a5bb822a50158b67429.tar.bz2 cbt-2d1a51d64aaca9ad00057a5bb822a50158b67429.zip |
Merge pull request #99 from cvogt/maven-repos
Support for alternative maven resolvers
-rw-r--r-- | README.md | 18 | ||||
-rw-r--r-- | TODO.txt | 21 | ||||
-rw-r--r-- | build/build.scala | 9 | ||||
-rw-r--r-- | circle.yml | 4 | ||||
-rw-r--r-- | nailgun_launcher/CBTUrlClassLoader.java | 3 | ||||
-rw-r--r-- | nailgun_launcher/EarlyDependencies.java | 1 | ||||
-rw-r--r-- | nailgun_launcher/NailgunLauncher.java | 120 | ||||
-rw-r--r-- | nailgun_launcher/Stage0Lib.java | 123 | ||||
-rw-r--r-- | stage1/MavenRepository.scala | 22 | ||||
-rw-r--r-- | stage1/Stage1.scala | 32 | ||||
-rw-r--r-- | stage1/Stage1Lib.scala | 77 | ||||
-rw-r--r-- | stage1/logger.scala | 10 | ||||
-rw-r--r-- | stage1/resolver.scala | 153 | ||||
-rw-r--r-- | stage2/AdminTasks.scala | 43 | ||||
-rw-r--r-- | stage2/BasicBuild.scala | 35 | ||||
-rw-r--r-- | stage2/BuildBuild.scala | 7 | ||||
-rw-r--r-- | stage2/BuildDependency.scala | 1 | ||||
-rw-r--r-- | stage2/GitDependency.scala | 5 | ||||
-rw-r--r-- | stage2/Lib.scala | 4 | ||||
-rw-r--r-- | stage2/SbtDependencyDsl.scala | 43 | ||||
-rw-r--r-- | stage2/Stage2.scala | 8 | ||||
-rw-r--r-- | stage2/mixins.scala | 4 | ||||
-rw-r--r-- | test/simple/build/build.scala | 34 | ||||
-rw-r--r-- | test/test.scala | 57 |
24 files changed, 474 insertions, 360 deletions
@@ -48,14 +48,16 @@ class Build(context: cbt.Context) extends PackageBuild(context){ override def version = "0.6.1-SNAPSHOT" override def groupId = "org.cvogt" override def artifactId = "play-json-extensions" - override def dependencies = super.dependencies ++ Vector( - // encouraged way to declare dependencies - ScalaDependency("com.typesafe.play", "play-json", "2.4.4"), - JavaDependency("joda-time", "joda-time", "2.9.2") - // also supported for SBT syntax compatibility: - // "com.typesafe.play" %% "play-json" % "2.4.4" - // "joda-time" % "joda-time % "2.9.2" - ) + override def dependencies = + super.dependencies :+ + MavenRepository.central.resolve( + // encouraged way to declare dependencies + ScalaDependency("com.typesafe.play", "play-json", "2.4.4"), + MavenDependency("joda-time", "joda-time", "2.9.2") + // also supported for SBT syntax compatibility: + // "com.typesafe.play" %% "play-json" % "2.4.4" + // Maven.central % "joda-time" % "joda-time % "2.9.2" + ) override def compile = { println("Compiling...") super.compile @@ -1,45 +1,26 @@ TODO: - in progress - - automated tests - improve logging - - bugs - - condition guarding zinc is too eager, needs to invalid - - immediate features - maybe rename context.cwd - - add another class that makes all pom required fields abstract - fix main project main method being run during tests - - DI lib into depencies - - fix conflicts in classpath stemming from dependencies - - cleanup classpath/classloader stuff - investigate and solve multiple compilations of the same SourceDependency Build. Maybe introduce global Build map. - cleanup - move from java File to nio Path - - defs for all tasks and cached where needed - - unify work classpath - - unify argument order - near future features - - strip out ammonite dependency - make cbt's own re-build concurrency safe - unify with sbts key names where sensible - allow updating snapshots - cbt cli options inject add dependencies into default build - - write cached macro - - add "debug" mode that shows lots of logging - - running subproject tasks in parallel - http://stackoverflow.com/questions/743288/java-synchronization-utility - dependency exclusion, etc. - - cache class loader per dependency in global, synchronized mutable Map - use cli friendly responses by default everywhere - class path debugging - - duplicate class detection - - missing/broken jars detection + - broken jars detection - invalid files in lib folder - integrate / build out maven search - - proper exit codes - use zinc nailgun multi platform nailgun wrapper https://github.com/typesafehub/zinc/tree/7af98ba11d27d7667301c2222c1e702c7092bc44/src/universal/bin diff --git a/build/build.scala b/build/build.scala index aa5d27a..6dfd395 100644 --- a/build/build.scala +++ b/build/build.scala @@ -5,11 +5,10 @@ import scala.collection.immutable.Seq class Build(context: Context) extends BasicBuild(context){ // FIXME: somehow consolidate this with cbt's own boot-strapping from source. - override def dependencies = super.dependencies ++ Seq( - JavaDependency("org.scala-lang","scala-library",constants.scalaVersion), - JavaDependency("net.incongru.watchservice","barbary-watchservice","1.0"), - JavaDependency("org.eclipse.jgit", "org.eclipse.jgit", "4.2.0.201601211800-r"), - JavaDependency("com.typesafe.zinc","zinc","0.3.9"), + override def dependencies = super.dependencies :+ MavenRepository.central.resolve( + MavenDependency("net.incongru.watchservice","barbary-watchservice","1.0"), + MavenDependency("org.eclipse.jgit", "org.eclipse.jgit", "4.2.0.201601211800-r"), + MavenDependency("com.typesafe.zinc","zinc","0.3.9"), ScalaDependency("org.scala-lang.modules","scala-xml","1.0.5") ) override def sources = Seq( @@ -1,11 +1,9 @@ machine: java: - version: oraclejdk8 + version: oraclejdk7 dependencies: cache_directories: - - "bootstrap_scala/cache" - - "bootstrap_scala/target" - "cache" - "nailgun_launcher/target" - "stage1/target" diff --git a/nailgun_launcher/CBTUrlClassLoader.java b/nailgun_launcher/CBTUrlClassLoader.java index 88bf4a3..c05391b 100644 --- a/nailgun_launcher/CBTUrlClassLoader.java +++ b/nailgun_launcher/CBTUrlClassLoader.java @@ -2,6 +2,7 @@ package cbt; import java.io.*; import java.net.*; import java.util.*; +import static cbt.Stage0Lib.*; class CbtURLClassLoader extends java.net.URLClassLoader{ public String toString(){ return ( @@ -9,7 +10,7 @@ class CbtURLClassLoader extends java.net.URLClassLoader{ + "(\n " + Arrays.toString(getURLs()) + ",\n " - + String.join("\n ",getParent().toString().split("\n")) + + join("\n ",getParent().toString().split("\n")) + "\n)" ); } diff --git a/nailgun_launcher/EarlyDependencies.java b/nailgun_launcher/EarlyDependencies.java index 1e129c7..f4d446c 100644 --- a/nailgun_launcher/EarlyDependencies.java +++ b/nailgun_launcher/EarlyDependencies.java @@ -4,6 +4,7 @@ import java.io.*; import java.nio.file.*; import java.net.*; import java.security.*; +import static cbt.Stage0Lib.*; import static cbt.NailgunLauncher.*; class EarlyDependencies{ diff --git a/nailgun_launcher/NailgunLauncher.java b/nailgun_launcher/NailgunLauncher.java index 50a3c91..a12d059 100644 --- a/nailgun_launcher/NailgunLauncher.java +++ b/nailgun_launcher/NailgunLauncher.java @@ -2,13 +2,10 @@ package cbt; import java.io.*; import java.lang.reflect.*; import java.net.*; -import java.nio.*; -import java.nio.file.*; -import static java.io.File.pathSeparator; import java.security.*; import java.util.*; import java.util.concurrent.ConcurrentHashMap; -import javax.xml.bind.annotation.adapters.HexBinaryAdapter; +import static cbt.Stage0Lib.*; /** * This launcher allows to start the JVM without loading anything else permanently into its @@ -47,7 +44,7 @@ public class NailgunLauncher{ MalformedURLException, IOException, NoSuchAlgorithmException { - long now = System.currentTimeMillis(); + long start = System.currentTimeMillis(); //System.err.println("ClassLoader: "+stage1classLoader); //System.err.println("lastSuccessfullCompile: "+lastSuccessfullCompile); //System.err.println("now: "+now); @@ -69,20 +66,20 @@ public class NailgunLauncher{ } } - Boolean stage1SourcesChanged = false; + Boolean changed = lastSuccessfullCompile == 0; for( File file: stage1SourceFiles ){ if( file.lastModified() > lastSuccessfullCompile ){ - stage1SourcesChanged = true; + changed = true; //System.err.println("File change: "+file.lastModified()); break; } } - if(stage1SourcesChanged || stage1classLoader == null){ + if(changed){ EarlyDependencies earlyDeps = new EarlyDependencies(); int exitCode = zinc(earlyDeps, stage1SourceFiles); if( exitCode == 0 ){ - lastSuccessfullCompile = now; + lastSuccessfullCompile = start; } else { System.exit( exitCode ); } @@ -102,113 +99,12 @@ public class NailgunLauncher{ Integer exitCode = (Integer) stage1classLoader .loadClass("cbt.Stage1") - .getMethod("run", String[].class, ClassLoader.class, Boolean.class) - .invoke( null, (Object) args, stage1classLoader, stage1SourcesChanged); + .getMethod("run", String[].class, ClassLoader.class, Boolean.class, Long.class) + .invoke( null, (Object) args, stage1classLoader, changed, start); System.exit(exitCode); }catch(Exception e){ System.err.println(stage1classLoader); throw e; } } - - public static void _assert(Boolean condition, Object msg){ - if(!condition){ - throw new AssertionError("Assertion failed: "+msg); - } - } - - public static int runMain(String cls, String[] args, ClassLoader cl) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException{ - try{ - System.setSecurityManager( new TrapSecurityManager() ); - cl.loadClass(cls) - .getMethod("main", String[].class) - .invoke( null, (Object) args); - return 0; - }catch( InvocationTargetException exception ){ - Throwable cause = exception.getCause(); - if(cause instanceof TrappedExitCode){ - return ((TrappedExitCode) cause).exitCode; - } - throw exception; - } finally { - System.setSecurityManager(NailgunLauncher.defaultSecurityManager); - } - } - - static int zinc( EarlyDependencies earlyDeps, List<File> sourceFiles ) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException{ - String cp = NAILGUN+TARGET + pathSeparator + earlyDeps.scalaXml_1_0_5_File + pathSeparator + earlyDeps.scalaLibrary_2_11_8_File; - List<String> zincArgs = new ArrayList<String>( - Arrays.asList( - new String[]{ - "-scala-compiler", earlyDeps.scalaCompiler_2_11_8_File, - "-scala-library", earlyDeps.scalaLibrary_2_11_8_File, - "-scala-extra", earlyDeps.scalaReflect_2_11_8_File, - "-sbt-interface", earlyDeps.sbtInterface_0_13_9_File, - "-compiler-interface", earlyDeps.compilerInterface_0_13_9_File, - "-cp", cp, - "-d", STAGE1+TARGET - } - ) - ); - - for( File f: sourceFiles ){ - zincArgs.add(f.toString()); - } - - PrintStream oldOut = System.out; - try{ - System.setOut(System.err); - return runMain( "com.typesafe.zinc.Main", zincArgs.toArray(new String[zincArgs.size()]), earlyDeps.zinc ); - } finally { - System.setOut(oldOut); - } - } - - static ClassLoader classLoader( String file ) throws MalformedURLException{ - return new CbtURLClassLoader( - new URL[]{ new URL("file:"+file) } - ); - } - static ClassLoader classLoader( String file, ClassLoader parent ) throws MalformedURLException{ - return new CbtURLClassLoader( - new URL[]{ new URL("file:"+file) }, parent - ); - } - static ClassLoader cacheGet( String key ){ - return classLoaderCacheValues.get( - classLoaderCacheKeys.get( key ) - ); - } - static ClassLoader cachePut( ClassLoader classLoader, String... jars ){ - String key = String.join( pathSeparator, jars ); - Object keyObject = new Object(); - classLoaderCacheKeys.put( key, keyObject ); - classLoaderCacheValues.put( keyObject, classLoader ); - return classLoader; - } - - public static void download(URL urlString, Path target, String sha1) throws IOException, NoSuchAlgorithmException { - final Path unverified = Paths.get(target+".unverified"); - if(!Files.exists(target)) { - new File(target.toString()).getParentFile().mkdirs(); - System.err.println("downloading " + urlString); - System.err.println("to " + target); - final InputStream stream = urlString.openStream(); - Files.copy(stream, unverified, StandardCopyOption.REPLACE_EXISTING); - stream.close(); - final String checksum = sha1(Files.readAllBytes(unverified)); - if(sha1 == null || sha1.toUpperCase().equals(checksum)) { - Files.move(unverified, target, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE); - } else { - System.err.println(target + " checksum does not match.\nExpected: |" + sha1 + "|\nFound: |" + checksum + "|"); - System.exit(1); - } - } - } - - public static String sha1(byte[] bytes) throws NoSuchAlgorithmException { - final MessageDigest sha1 = MessageDigest.getInstance("SHA1"); - sha1.update(bytes, 0, bytes.length); - return (new HexBinaryAdapter()).marshal(sha1.digest()); - } } diff --git a/nailgun_launcher/Stage0Lib.java b/nailgun_launcher/Stage0Lib.java new file mode 100644 index 0000000..d6f33e1 --- /dev/null +++ b/nailgun_launcher/Stage0Lib.java @@ -0,0 +1,123 @@ +package cbt; +import java.io.*; +import java.lang.reflect.*; +import java.net.*; +import java.nio.*; +import java.nio.file.*; +import java.security.*; +import java.util.*; +import javax.xml.bind.annotation.adapters.HexBinaryAdapter; +import static java.io.File.pathSeparator; +import static cbt.Stage0Lib.*; +import static cbt.NailgunLauncher.*; + +public class Stage0Lib{ + public static void _assert(Boolean condition, Object msg){ + if(!condition){ + throw new AssertionError("Assertion failed: "+msg); + } + } + + public static int runMain(String cls, String[] args, ClassLoader cl) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException{ + try{ + System.setSecurityManager( new TrapSecurityManager() ); + cl.loadClass(cls) + .getMethod("main", String[].class) + .invoke( null, (Object) args); + return 0; + }catch( InvocationTargetException exception ){ + Throwable cause = exception.getCause(); + if(cause instanceof TrappedExitCode){ + return ((TrappedExitCode) cause).exitCode; + } + throw exception; + } finally { + System.setSecurityManager(NailgunLauncher.defaultSecurityManager); + } + } + + public static int zinc( EarlyDependencies earlyDeps, List<File> sourceFiles ) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException{ + String cp = NAILGUN+TARGET + pathSeparator + earlyDeps.scalaXml_1_0_5_File + pathSeparator + earlyDeps.scalaLibrary_2_11_8_File; + List<String> zincArgs = new ArrayList<String>( + Arrays.asList( + new String[]{ + "-scala-compiler", earlyDeps.scalaCompiler_2_11_8_File, + "-scala-library", earlyDeps.scalaLibrary_2_11_8_File, + "-scala-extra", earlyDeps.scalaReflect_2_11_8_File, + "-sbt-interface", earlyDeps.sbtInterface_0_13_9_File, + "-compiler-interface", earlyDeps.compilerInterface_0_13_9_File, + "-cp", cp, + "-d", STAGE1+TARGET + } + ) + ); + + for( File f: sourceFiles ){ + zincArgs.add(f.toString()); + } + + PrintStream oldOut = System.out; + try{ + System.setOut(System.err); + return runMain( "com.typesafe.zinc.Main", zincArgs.toArray(new String[zincArgs.size()]), earlyDeps.zinc ); + } finally { + System.setOut(oldOut); + } + } + + public static ClassLoader classLoader( String file ) throws MalformedURLException{ + return new CbtURLClassLoader( + new URL[]{ new URL("file:"+file) } + ); + } + public static ClassLoader classLoader( String file, ClassLoader parent ) throws MalformedURLException{ + return new CbtURLClassLoader( + new URL[]{ new URL("file:"+file) }, parent + ); + } + public static ClassLoader cacheGet( String key ){ + return classLoaderCacheValues.get( + classLoaderCacheKeys.get( key ) + ); + } + public static ClassLoader cachePut( ClassLoader classLoader, String... jars ){ + String key = join( pathSeparator, jars ); + Object keyObject = new Object(); + classLoaderCacheKeys.put( key, keyObject ); + classLoaderCacheValues.put( keyObject, classLoader ); + return classLoader; + } + + public static void download(URL urlString, Path target, String sha1) throws IOException, NoSuchAlgorithmException { + final Path unverified = Paths.get(target+".unverified"); + if(!Files.exists(target)) { + new File(target.toString()).getParentFile().mkdirs(); + System.err.println("downloading " + urlString); + System.err.println("to " + target); + final InputStream stream = urlString.openStream(); + Files.copy(stream, unverified, StandardCopyOption.REPLACE_EXISTING); + stream.close(); + final String checksum = sha1(Files.readAllBytes(unverified)); + if(sha1 == null || sha1.toUpperCase().equals(checksum)) { + Files.move(unverified, target, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE); + } else { + System.err.println(target + " checksum does not match.\nExpected: |" + sha1 + "|\nFound: |" + checksum + "|"); + System.exit(1); + } + } + } + + public static String sha1(byte[] bytes) throws NoSuchAlgorithmException { + final MessageDigest sha1 = MessageDigest.getInstance("SHA1"); + sha1.update(bytes, 0, bytes.length); + return (new HexBinaryAdapter()).marshal(sha1.digest()); + } + + public static String join(String separator, String[] parts){ + String result = parts[0]; + for(int i = 1; i < parts.length; i++){ + result += separator + parts[i]; + } + return result; + } +}
\ No newline at end of file diff --git a/stage1/MavenRepository.scala b/stage1/MavenRepository.scala new file mode 100644 index 0000000..bfd52a7 --- /dev/null +++ b/stage1/MavenRepository.scala @@ -0,0 +1,22 @@ +package cbt +import scala.collection.immutable.Seq +import java.net._ +case class MavenRepository(url: URL){ + def resolve( dependencies: MavenDependency* )(implicit logger: Logger): BoundMavenDependencies + = new BoundMavenDependencies( Seq(url), dependencies.to ) + def resolveOne( dependency: MavenDependency )(implicit logger: Logger): BoundMavenDependency + = BoundMavenDependency( dependency, Seq(url) ) +} + +object MavenRepository{ + case class combine(repositories: MavenRepository*){ + def resolve( dependencies: MavenDependency* )(implicit logger: Logger): BoundMavenDependencies + = new BoundMavenDependencies( repositories.map(_.url).to, dependencies.to ) + } + def central = MavenRepository(new URL(NailgunLauncher.MAVEN_URL)) + def jcenter = MavenRepository(new URL("https://jcenter.bintray.com/releases")) + def bintray(owner: String) = MavenRepository(new URL(s"https://dl.bintray.com/$owner/maven")) + private val sonatypeBase = new URL("https://oss.sonatype.org/content/repositories/") + def sonatype = MavenRepository(sonatypeBase ++ "releases") + def sonatypeSnapshots = MavenRepository(sonatypeBase ++ "snapshots") +} diff --git a/stage1/Stage1.scala b/stage1/Stage1.scala index e8245c4..3456e1f 100644 --- a/stage1/Stage1.scala +++ b/stage1/Stage1.scala @@ -1,7 +1,6 @@ package cbt import java.io._ -import java.time.LocalTime.now import scala.collection.immutable.Seq import scala.collection.JavaConverters._ @@ -50,41 +49,32 @@ object Stage1{ a.lastModified > b.lastModified } - def run(_args: Array[String], classLoader: ClassLoader, stage1SourcesChanged: java.lang.Boolean): Int = { + def run(_args: Array[String], classLoader: ClassLoader, _cbtChanged: java.lang.Boolean, start: java.lang.Long): Int = { val args = Stage1ArgsParser(_args.toVector) - val logger = new Logger(args.enabledLoggers) + val logger = new Logger(args.enabledLoggers, start) logger.stage1(s"Stage1 start") val lib = new Stage1Lib(logger) import lib._ - - val sourceFiles = stage2.listFiles.toVector.filter(_.isFile).filter(_.toString.endsWith(".scala")) - val changeIndicator = stage2Target ++ "/cbt/Build.class" - - val deps = Dependencies( - JavaDependency("net.incongru.watchservice","barbary-watchservice","1.0"), - JavaDependency("org.eclipse.jgit", "org.eclipse.jgit", "4.2.0.201601211800-r") - ) - val classLoaderCache = new ClassLoaderCache(logger) - val stage2SourcesChanged = lib.needsUpdate(sourceFiles, stage2StatusFile) + val sourceFiles = stage2.listFiles.toVector.filter(_.isFile).filter(_.toString.endsWith(".scala")) + val cbtHasChanged = _cbtChanged || lib.needsUpdate(sourceFiles, stage2StatusFile) logger.stage1("Compiling stage2 if necessary") - val scalaXml = JavaDependency("org.scala-lang.modules","scala-xml_"+constants.scalaMajorVersion,constants.scalaXmlVersion) compile( - stage2SourcesChanged, + cbtHasChanged, sourceFiles, stage2Target, stage2StatusFile, - nailgunTarget +: stage1Target +: Dependencies(deps, scalaXml).classpath, + CbtDependency().dependencyClasspath, Seq("-deprecation"), classLoaderCache, zincVersion = "0.3.9", scalaVersion = constants.scalaVersion ) - logger.stage1(s"[$now] calling CbtDependency.classLoader") - if(NailgunLauncher.stage2classLoader == null){ + logger.stage1(s"calling CbtDependency.classLoader") + if(cbtHasChanged){ NailgunLauncher.stage2classLoader = CbtDependency().classLoader(classLoaderCache) } - logger.stage1(s"[$now] Run Stage2") + logger.stage1(s"Run Stage2") val exitCode = ( NailgunLauncher.stage2classLoader.loadClass( if(args.admin) "cbt.AdminStage2" else "cbt.Stage2" @@ -96,7 +86,7 @@ object Stage1{ new File( args.args(0) ), args.args.drop(1).toVector, // launcher changes cause entire nailgun restart, so no need for them here - cbtHasChanged = stage1SourcesChanged || stage2SourcesChanged, + cbtHasChanged = cbtHasChanged, logger ) ) match { @@ -104,7 +94,7 @@ object Stage1{ case _ => ExitCode.Success } ).integer - logger.stage1(s"[$now] Stage1 end") + logger.stage1(s"Stage1 end") return exitCode; } } diff --git a/stage1/Stage1Lib.scala b/stage1/Stage1Lib.scala index 105fe3e..da9f8dd 100644 --- a/stage1/Stage1Lib.scala +++ b/stage1/Stage1Lib.scala @@ -31,7 +31,7 @@ object CatchTrappedExitCode{ } } -case class Context( cwd: File, args: Seq[String], logger: Logger, classLoaderCache: ClassLoaderCache ) +case class Context( cwd: File, args: Seq[String], logger: Logger, cbtHasChanged: Boolean, classLoaderCache: ClassLoaderCache ) class BaseLib{ def realpath(name: File) = new File(Paths.get(name.getAbsolutePath).normalize.toString) @@ -56,23 +56,35 @@ class Stage1Lib( val logger: Logger ) extends BaseLib{ def blue(string: String) = scala.Console.BLUE++string++scala.Console.RESET def green(string: String) = scala.Console.GREEN++string++scala.Console.RESET - def download(urlString: URL, target: File, sha1: Option[String]){ - val incomplete = Paths.get( target.string ++ ".incomplete" ); - if( !target.exists ){ - target.getParentFile.mkdirs - logger.resolver(blue("downloading ") ++ urlString.string) - logger.resolver(blue("to ") ++ target.string) - val stream = urlString.openStream - Files.copy(stream, incomplete, StandardCopyOption.REPLACE_EXISTING) - sha1.foreach{ - hash => - val expected = hash - val actual = this.sha1(Files.readAllBytes(incomplete)) - assert( expected == actual, s"$expected == $actual" ) - logger.resolver( green("verified") ++ " checksum for " ++ target.string) + def download(url: URL, target: File, sha1: Option[String]): Boolean = { + if( target.exists ){ + true + } else { + val incomplete = Paths.get( target.string ++ ".incomplete" ); + val connection = url.openConnection.asInstanceOf[HttpURLConnection] + if(connection.getResponseCode != HttpURLConnection.HTTP_OK){ + logger.resolver(blue("not found: ") ++ url.string) + false + } else { + logger.resolver(blue("downloading ") ++ url.string) + logger.resolver(blue("to ") ++ target.string) + target.getParentFile.mkdirs + val stream = connection.getInputStream + try{ + Files.copy(stream, incomplete, StandardCopyOption.REPLACE_EXISTING) + } finally { + stream.close() + } + sha1.foreach{ + hash => + val expected = hash + val actual = this.sha1(Files.readAllBytes(incomplete)) + assert( expected == actual, s"$expected == $actual" ) + logger.resolver( green("verified") ++ " checksum for " ++ target.string) + } + Files.move(incomplete, Paths.get(target.string), StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE); + true } - stream.close - Files.move(incomplete, Paths.get(target.string), StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE); } } @@ -137,26 +149,37 @@ class Stage1Lib( val logger: Logger ) extends BaseLib{ None }else{ if( needsRecompile ){ - val zinc = JavaDependency("com.typesafe.zinc","zinc", zincVersion) + import MavenRepository.central + val zinc = central.resolveOne(MavenDependency("com.typesafe.zinc","zinc", zincVersion)) val zincDeps = zinc.transitiveDependencies val sbtInterface = zincDeps - .collect{ case d @ JavaDependency( "com.typesafe.sbt", "sbt-interface", _, Classifier.none ) => d } + .collect{ case d @ + BoundMavenDependency( + MavenDependency( "com.typesafe.sbt", "sbt-interface", _, Classifier.none), + _ + ) => d + } .headOption .getOrElse( throw new Exception(s"cannot find sbt-interface in zinc $zincVersion dependencies: "++zincDeps.toString) ) .jar val compilerInterface = zincDeps - .collect{ case d @ JavaDependency( "com.typesafe.sbt", "compiler-interface", _, Classifier.sources ) => d } + .collect{ case d @ + BoundMavenDependency( + MavenDependency( "com.typesafe.sbt", "compiler-interface", _, Classifier.sources), + _ + ) => d + } .headOption .getOrElse( throw new Exception(s"cannot find compiler-interface in zinc $zincVersion dependencies: "++zincDeps.toString) ) .jar - val scalaLibrary = JavaDependency("org.scala-lang","scala-library",scalaVersion).jar - val scalaReflect = JavaDependency("org.scala-lang","scala-reflect",scalaVersion).jar - val scalaCompiler = JavaDependency("org.scala-lang","scala-compiler",scalaVersion).jar + val scalaLibrary = central.resolveOne(MavenDependency("org.scala-lang","scala-library",scalaVersion)).jar + val scalaReflect = central.resolveOne(MavenDependency("org.scala-lang","scala-reflect",scalaVersion)).jar + val scalaCompiler = central.resolveOne(MavenDependency("org.scala-lang","scala-compiler",scalaVersion)).jar val start = System.currentTimeMillis @@ -212,9 +235,9 @@ class Stage1Lib( val logger: Logger ) extends BaseLib{ def ScalaDependency( groupId: String, artifactId: String, version: String, classifier: Classifier = Classifier.none, - scalaVersion: String + scalaMajorVersion: String ) = - JavaDependency( - groupId, artifactId ++ "_" ++ scalaVersion, version, classifier + MavenDependency( + groupId, artifactId ++ "_" ++ scalaMajorVersion, version, classifier ) -}
\ No newline at end of file +} diff --git a/stage1/logger.scala b/stage1/logger.scala index e513443..91a2412 100644 --- a/stage1/logger.scala +++ b/stage1/logger.scala @@ -1,7 +1,5 @@ package cbt -import java.time._ - /** * This represents a logger with namespaces that can be enabled or disabled as needed. The * namespaces are defined using {{enabledLoggers}}. Possible values are defined in the subobject @@ -9,13 +7,11 @@ import java.time._ * * We can replace this with something more sophisticated eventually. */ -case class Logger(enabledLoggers: Set[String]) { - def this(enabledLoggers: Option[String]) = this( enabledLoggers.toVector.flatMap( _.split(",") ).toSet ) - - val start = LocalTime.now() +case class Logger(enabledLoggers: Set[String], start: Long) { + def this(enabledLoggers: Option[String], start: Long) = this( enabledLoggers.toVector.flatMap( _.split(",") ).toSet, start ) def log(name: String, msg: => String) = { - val timeTaken = (Duration.between(start, LocalTime.now()).toMillis.toDouble / 1000).toString + val timeTaken = ((start - System.currentTimeMillis) / 1000).toString System.err.println( s"[${" "*(6-timeTaken.size)}$timeTaken][$name] $msg" ) } diff --git a/stage1/resolver.scala b/stage1/resolver.scala index 701871c..e8bfc07 100644 --- a/stage1/resolver.scala +++ b/stage1/resolver.scala @@ -1,5 +1,6 @@ package cbt import java.nio.file._ +import java.nio.charset.StandardCharsets import java.net._ import java.io._ import scala.collection.immutable.Seq @@ -35,7 +36,7 @@ abstract class Dependency{ def exportedJars: Seq[File] def jars: Seq[File] = exportedJars ++ dependencyJars - def canBeCached = false + def canBeCached: Boolean //private type BuildCache = KeyLockedLazyCache[Dependency, Future[ClassPath]] def exportClasspathConcurrently: ClassPath = { @@ -68,7 +69,7 @@ abstract class Dependency{ d => // find out latest version of the required dependency val l = d match { - case m: JavaDependency => latest( (m.groupId,m.artifactId) ) + case m: BoundMavenDependency => latest( (m.groupId,m.artifactId) ) case _ => d } // // trigger compilation if not already triggered @@ -166,7 +167,7 @@ abstract class Dependency{ case _:ArtifactInfo => false case _ => true } - noInfo ++ JavaDependency.updateOutdated( hasInfo ).reverse.distinct + noInfo ++ BoundMavenDependency.updateOutdated( hasInfo ).reverse.distinct } def show: String = this.getClass.getSimpleName @@ -182,9 +183,9 @@ abstract class Dependency{ } // TODO: all this hard codes the scala version, needs more flexibility -class ScalaCompilerDependency(version: String)(implicit logger: Logger) extends JavaDependency("org.scala-lang","scala-compiler",version) -class ScalaLibraryDependency (version: String)(implicit logger: Logger) extends JavaDependency("org.scala-lang","scala-library",version) -class ScalaReflectDependency (version: String)(implicit logger: Logger) extends JavaDependency("org.scala-lang","scala-reflect",version) +class ScalaCompilerDependency(version: String)(implicit logger: Logger) extends BoundMavenDependency(MavenDependency("org.scala-lang","scala-compiler",version, Classifier.none), Seq(MavenRepository.central.url)) +class ScalaLibraryDependency (version: String)(implicit logger: Logger) extends BoundMavenDependency(MavenDependency("org.scala-lang","scala-library",version, Classifier.none), Seq(MavenRepository.central.url)) +class ScalaReflectDependency (version: String)(implicit logger: Logger) extends BoundMavenDependency(MavenDependency("org.scala-lang","scala-reflect",version, Classifier.none), Seq(MavenRepository.central.url)) case class ScalaDependencies(version: String)(implicit val logger: Logger) extends Dependency{ sd => override final val needsUpdate = false @@ -199,7 +200,7 @@ case class ScalaDependencies(version: String)(implicit val logger: Logger) exten ) } -case class BinaryDependency( path: File, dependencies: Seq[Dependency] )(implicit val logger: Logger) extends Dependency{ +case class BinaryDependency( path: File, dependencies: Seq[Dependency], canBeCached: Boolean )(implicit val logger: Logger) extends Dependency{ def exportedClasspath = ClassPath(Seq(path)) def exportedJars = Seq[File](path) override def needsUpdate = false @@ -207,16 +208,19 @@ case class BinaryDependency( path: File, dependencies: Seq[Dependency] )(implici } /** Allows to easily assemble a bunch of dependencies */ -case class Dependencies( _dependencies: Dependency* )(implicit val logger: Logger) extends Dependency{ - override def dependencies = _dependencies.to - def needsUpdate = dependencies.exists(_.needsUpdate) - def exportedClasspath = ClassPath(Seq()) - def exportedJars = Seq() - def targetClasspath = ClassPath(Seq()) +case class Dependencies( dependencies: Seq[Dependency] )(implicit val logger: Logger) extends Dependency{ + override def needsUpdate = dependencies.exists(_.needsUpdate) + override def canBeCached = dependencies.forall(_.canBeCached) + override def exportedClasspath = ClassPath(Seq()) + override def exportedJars = Seq() + override def targetClasspath = ClassPath(Seq()) +} +object Dependencies{ + def apply( dependencies: Dependency* )(implicit logger: Logger): Dependencies = Dependencies( dependencies.to ) } case class Stage1Dependency()(implicit val logger: Logger) extends Dependency{ - def needsUpdate = false // FIXME: think this through, might allow simplifications and/or optimizations + override def needsUpdate = false // FIXME: think this through, might allow simplifications and/or optimizations override def canBeCached = false /* private object classLoaderRecursionCache extends Cache[ClassLoader] @@ -232,23 +236,27 @@ case class Stage1Dependency()(implicit val logger: Logger) extends Dependency{ override def exportedClasspath = ClassPath( Seq(nailgunTarget, stage1Target) ) override def exportedJars = ???//Seq[File]() override def dependencies = Seq( - JavaDependency("org.scala-lang","scala-library",constants.scalaVersion), - JavaDependency("org.scala-lang.modules","scala-xml_"+constants.scalaMajorVersion,"1.0.5") + MavenRepository.central.resolve( + MavenDependency("org.scala-lang","scala-library",constants.scalaVersion), + MavenDependency("org.scala-lang.modules","scala-xml_"+constants.scalaMajorVersion,"1.0.5") + ) ) // FIXME: implement sanity check to prevent using incompatible scala-library and xml version on cp override def classLoaderRecursion( latest: Map[(String,String),Dependency], cache: ClassLoaderCache ) = getClass.getClassLoader } case class CbtDependency()(implicit val logger: Logger) extends Dependency{ - def needsUpdate = false // FIXME: think this through, might allow simplifications and/or optimizations + override def needsUpdate = false // FIXME: think this through, might allow simplifications and/or optimizations override def canBeCached = false override def targetClasspath = exportedClasspath override def exportedClasspath = ClassPath( Seq( stage2Target ) ) override def exportedJars = ??? override def dependencies = Seq( Stage1Dependency(), - JavaDependency("net.incongru.watchservice","barbary-watchservice","1.0"), - JavaDependency("org.eclipse.jgit", "org.eclipse.jgit", "4.2.0.201601211800-r") + MavenRepository.central.resolve( + MavenDependency("net.incongru.watchservice","barbary-watchservice","1.0"), + MavenDependency("org.eclipse.jgit", "org.eclipse.jgit", "4.2.0.201601211800-r") + ) ) } @@ -258,19 +266,30 @@ object Classifier{ object javadoc extends Classifier(Some("javadoc")) object sources extends Classifier(Some("sources")) } +abstract class DependenciesProxy{ -case class JavaDependency( +} +class BoundMavenDependencies( + urls: Seq[URL], mavenDependencies: Seq[MavenDependency] +)(implicit logger: Logger) extends Dependencies( + mavenDependencies.map( BoundMavenDependency(_,urls) ) +) +case class MavenDependency( groupId: String, artifactId: String, version: String, classifier: Classifier = Classifier.none +) +case class BoundMavenDependency( + mavenDependency: MavenDependency, repositories: Seq[URL] )(implicit val logger: Logger) extends ArtifactInfo{ + val MavenDependency( groupId, artifactId, version, classifier ) = mavenDependency assert( Option(groupId).collect{ - case JavaDependency.ValidIdentifier(_) => + case BoundMavenDependency.ValidIdentifier(_) => }.nonEmpty, s"not a valid groupId: '$groupId'" ) assert( Option(artifactId).collect{ - case JavaDependency.ValidIdentifier(_) => + case BoundMavenDependency.ValidIdentifier(_) => }.nonEmpty, s"not a valid artifactId: '$artifactId'" ) @@ -280,62 +299,61 @@ case class JavaDependency( ) override def needsUpdate = false - override def canBeCached = true + override def canBeCached = dependencies.forall(_.canBeCached) private val groupPath = groupId.split("\\.").mkString("/") - def basePath = s"/$groupPath/$artifactId/$version/$artifactId-$version" ++ classifier.name.map("-"++_).getOrElse("") + protected[cbt] def basePath = s"/$groupPath/$artifactId/$version/$artifactId-$version" ++ classifier.name.map("-"++_).getOrElse("") - private def resolverUrl:URL = new URL( - if(version.endsWith("-SNAPSHOT")) "https://oss.sonatype.org/content/repositories/snapshots" else "https://repo1.maven.org/maven2" - ) - private def baseUrl: URL = resolverUrl ++ basePath - private def baseFile: File = mavenCache ++ basePath - private def pomFile: File = baseFile ++ ".pom" - private def jarFile: File = baseFile ++ ".jar" //private def coursierJarFile = userHome++"/.coursier/cache/v1/https/repo1.maven.org/maven2"++basePath++".jar" - private def pomUrl: URL = baseUrl ++ ".pom" - private[cbt] def jarUrl: URL = baseUrl ++ ".jar" - def exportedJars = Seq( jar ) - def exportedClasspath = ClassPath( exportedJars ) - def targetClasspath = exportedClasspath + override def exportedJars = Seq( jar ) + override def exportedClasspath = ClassPath( exportedJars ) + override def targetClasspath = exportedClasspath import scala.collection.JavaConversions._ - - def jarSha1 = { - val file = jarFile ++ ".sha1" - lib.download( jarUrl ++ ".sha1" , file, None ) - // split(" ") here so checksum file contents in this format work: df7f15de037a1ee4d57d2ed779739089f560338c jna-3.2.2.pom - Files.readAllLines(Paths.get(file.string)).mkString("\n").split(" ").head.trim + + private def resolve(suffix: String, hash: Option[String]): File = { + val file = mavenCache ++ basePath ++ "." ++ suffix + val urls = repositories.map(_ ++ basePath ++ "." ++ suffix) + urls.find( + lib.download(_, file, hash) + ).getOrElse( + throw new Exception(s"\nCannot resolve\n$this\nCan't find any of\n"++urls.mkString("\n")) + ) + file } - def pomSha1 = { - val file = pomFile++".sha1" - lib.download( pomUrl++".sha1" , file, None ) - // split(" ") here so checksum file contents in this format work: df7f15de037a1ee4d57d2ed779739089f560338c jna-3.2.2.pom - Files.readAllLines(Paths.get(file.string)).mkString("\n").split(" ").head.trim + private def resolveHash(suffix: String) = { + Files.readAllLines( + resolve( suffix ++ ".sha1", None ).toPath, + StandardCharsets.UTF_8 + ).mkString("\n").split(" ").head.trim } + + private object jarSha1Cache extends Cache[String] + def jarSha1: String = jarSha1Cache{ resolveHash("jar") } + + private object pomSha1Cache extends Cache[String] + def pomSha1: String = pomSha1Cache{ resolveHash("pom") } private object jarCache extends Cache[File] - def jar = jarCache{ - lib.download( jarUrl, jarFile, Some(jarSha1) ) - jarFile - } - def pomXml = XML.loadFile(pom.toString) + def jar: File = jarCache{ resolve("jar", Some(jarSha1)) } - def pom = { - lib.download( pomUrl, pomFile, Some(pomSha1) ) - pomFile - } + private object pomCache extends Cache[File] + def pom: File = pomCache{ resolve("pom", Some(pomSha1)) } + def pomXml = XML.loadFile(pom.string) // ========== pom traversal ========== - lazy val transitivePom: Seq[JavaDependency] = { + lazy val transitivePom: Seq[BoundMavenDependency] = { (pomXml \ "parent").collect{ case parent => - JavaDependency( - (parent \ "groupId").text, - (parent \ "artifactId").text, - (parent \ "version").text + BoundMavenDependency( + MavenDependency( + (parent \ "groupId").text, + (parent \ "artifactId").text, + (parent \ "version").text + ), + repositories )(logger) }.flatMap(_.transitivePom) :+ this } @@ -362,7 +380,7 @@ case class JavaDependency( } ).toMap - def dependencies: Seq[JavaDependency] = { + def dependencies: Seq[BoundMavenDependency] = { if(classifier == Classifier.sources) Seq() else (pomXml \ "dependencies" \ "dependency").collect{ case xml if (xml \ "scope").text == "" && (xml \ "optional").text != "true" => @@ -383,9 +401,12 @@ case class JavaDependency( throw new Exception(s"$artifactId not found in \n$dependencyVersions") ) ) - JavaDependency( - groupId, artifactId, version, - Classifier( Some( (xml \ "classifier").text ).filterNot(_ == "").filterNot(_ == null) ) + BoundMavenDependency( + MavenDependency( + groupId, artifactId, version, + Classifier( Some( (xml \ "classifier").text ).filterNot(_ == "").filterNot(_ == null) ) + ), + repositories ) }.toVector } @@ -411,7 +432,7 @@ case class JavaDependency( } } } -object JavaDependency{ +object BoundMavenDependency{ def ValidIdentifier = "^([A-Za-z0-9_\\-.]+)$".r // according to maven's DefaultModelValidator.java def semanticVersionLessThan(left: String, right: String) = { // FIXME: this ignores ends when different size diff --git a/stage2/AdminTasks.scala b/stage2/AdminTasks.scala index a61e767..655b2b0 100644 --- a/stage2/AdminTasks.scala +++ b/stage2/AdminTasks.scala @@ -9,7 +9,7 @@ class AdminTasks(lib: Lib, args: Seq[String], cwd: File){ args(1).split(",").toVector.map{ d => val v = d.split(":") - new JavaDependency(v(0),v(1),v(2)).classpath + MavenRepository.central.resolveOne(MavenDependency(v(0),v(1),v(2))).classpath } ) } @@ -17,15 +17,17 @@ class AdminTasks(lib: Lib, args: Seq[String], cwd: File){ args(1).split(",").toVector.map{ d => val v = d.split(":") - new JavaDependency(v(0),v(1),v(2)).dependencyTree + MavenRepository.central.resolveOne(MavenDependency(v(0),v(1),v(2))).dependencyTree }.mkString("\n\n") } def amm = ammonite def ammonite = { val version = args.lift(1).getOrElse(constants.scalaVersion) val scalac = new ScalaCompilerDependency( version ) - val d = JavaDependency( - "com.lihaoyi","ammonite-repl_2.11.7",args.lift(1).getOrElse("0.5.7") + val d = MavenRepository.central.resolveOne( + MavenDependency( + "com.lihaoyi","ammonite-repl_2.11.7",args.lift(1).getOrElse("0.5.7") + ) ) // FIXME: this does not work quite yet, throws NoSuchFileException: /ammonite/repl/frontend/ReplBridge$.class lib.runMain( @@ -46,41 +48,25 @@ class AdminTasks(lib: Lib, args: Seq[String], cwd: File){ val scalaMajorVersion = scalaVersion.split("\\.").take(2).mkString(".") val scalaXmlVersion = args.lift(2).getOrElse(constants.scalaXmlVersion) val zincVersion = args.lift(3).getOrElse(constants.zincVersion) - /* - def tree(d: JavaDependency, indent: Int): String ={ - val dependencies = { - if( d.dependencies.nonEmpty ){ - d.dependencies.map{ - case d: JavaDependency => tree(d,indent + 1) - }.mkString(",\n" ++ ( " " * indent ),",\n" ++ ( " " * indent ), "") - } else "" - } - ( - s"""new EarlyDependency( "${d.groupId}", "${d.artifactId}", "${d.version}", "${d.jarSha1}"$dependencies)""" - ) - }*/ val scalaDeps = Seq( - JavaDependency("org.scala-lang","scala-reflect",scalaVersion), - JavaDependency("org.scala-lang","scala-compiler",scalaVersion) + MavenRepository.central.resolveOne(MavenDependency("org.scala-lang","scala-reflect",scalaVersion)), + MavenRepository.central.resolveOne(MavenDependency("org.scala-lang","scala-compiler",scalaVersion)) ) val scalaXml = Dependencies( - JavaDependency("org.scala-lang.modules","scala-xml_"+scalaMajorVersion,scalaXmlVersion), - JavaDependency("org.scala-lang","scala-library",scalaVersion) + MavenRepository.central.resolveOne(MavenDependency("org.scala-lang.modules","scala-xml_"+scalaMajorVersion,scalaXmlVersion)), + MavenRepository.central.resolveOne(MavenDependency("org.scala-lang","scala-library",scalaVersion)) ) - val zinc = JavaDependency("com.typesafe.zinc","zinc",zincVersion) - println(zinc.dependencyTree) + val zinc = MavenRepository.central.resolveOne(MavenDependency("com.typesafe.zinc","zinc",zincVersion)) - def valName(dep: JavaDependency) = { + def valName(dep: BoundMavenDependency) = { val words = dep.artifactId.split("_").head.split("-") words(0) ++ words.drop(1).map(s => s(0).toString.toUpperCase ++ s.drop(1)).mkString ++ "_" ++ dep.version.replace(".","_") ++ "_" } - def vals(d: JavaDependency) = s""" """ - - def jarVal(dep: JavaDependency) = "_" + valName(dep) +"Jar" - def transitive(dep: Dependency) = (dep +: dep.transitiveDependencies.reverse).collect{case d: JavaDependency => d} + def jarVal(dep: BoundMavenDependency) = "_" + valName(dep) +"Jar" + def transitive(dep: Dependency) = (dep +: dep.transitiveDependencies.reverse).collect{case d: BoundMavenDependency => d} def codeEach(dep: Dependency) = { transitive(dep).tails.map(_.reverse).toVector.reverse.drop(1).map{ deps => @@ -105,6 +91,7 @@ import java.io.*; import java.nio.file.*; import java.net.*; import java.security.*; +import static cbt.Stage0Lib.*; import static cbt.NailgunLauncher.*; class EarlyDependencies{ diff --git a/stage2/BasicBuild.scala b/stage2/BasicBuild.scala index 9ed8c26..39cc9e3 100644 --- a/stage2/BasicBuild.scala +++ b/stage2/BasicBuild.scala @@ -12,14 +12,16 @@ import scala.collection.immutable.Seq import scala.util._ class BasicBuild( context: Context ) extends Build( context ) -class Build(val context: Context) extends Dependency with TriggerLoop{ +class Build(val context: Context) extends Dependency with TriggerLoop with SbtDependencyDsl{ // library available to builds implicit final val logger: Logger = context.logger implicit final val classLoaderCache: ClassLoaderCache = context.classLoaderCache + implicit final val _context = context override final protected val lib: Lib = new Lib(logger) // ========== general stuff ========== + override def canBeCached = false def enableConcurrency = false final def projectDirectory: File = lib.realpath(context.cwd) assert( projectDirectory.exists, "projectDirectory does not exist: " ++ projectDirectory.string ) @@ -32,7 +34,9 @@ class Build(val context: Context) extends Dependency with TriggerLoop{ def zincVersion = "0.3.9" def dependencies: Seq[Dependency] = Seq( - "org.scala-lang" % "scala-library" % scalaVersion + MavenRepository.central.resolve( + "org.scala-lang" % "scala-library" % scalaVersion + ) ) // ========== paths ========== @@ -84,22 +88,6 @@ class Build(val context: Context) extends Dependency with TriggerLoop{ scalaVersion: String = scalaMajorVersion ) = lib.ScalaDependency( groupId, artifactId, version, classifier, scalaVersion ) - /** SBT-like dependency builder DSL for syntax compatibility */ - class DependencyBuilder2( groupId: String, artifactId: String, scalaVersion: Option[String] ){ - def %(version: String) = scalaVersion.map( - v => ScalaDependency(groupId, artifactId, version, scalaVersion = v) - ).getOrElse( - JavaDependency(groupId, artifactId, version) - ) - } - implicit class DependencyBuilder(groupId: String){ - def %%(artifactId: String) = new DependencyBuilder2( groupId, artifactId, Some(scalaMajorVersion) ) - def %(artifactId: String) = new DependencyBuilder2( groupId, artifactId, None ) - } - implicit class DependencyBuilder3(d: JavaDependency){ - def %(classifier: String) = d.copy(classifier = Classifier(Some(classifier))) - } - final def BuildDependency(path: File) = cbt.BuildDependency( context.copy( cwd = path, args = Seq() ) ) @@ -125,12 +113,11 @@ class Build(val context: Context) extends Dependency with TriggerLoop{ def scalacOptions: Seq[String] = Seq( "-feature", "-deprecation", "-unchecked" ) private object needsUpdateCache extends Cache[Boolean] - def needsUpdate: Boolean = { - needsUpdateCache( - lib.needsUpdate( sourceFiles, compileStatusFile ) - || transitiveDependencies.exists(_.needsUpdate) - ) - } + def needsUpdate: Boolean = needsUpdateCache( + context.cbtHasChanged + || lib.needsUpdate( sourceFiles, compileStatusFile ) + || transitiveDependencies.exists(_.needsUpdate) + ) private object compileCache extends Cache[Option[File]] def compile: Option[File] = compileCache{ diff --git a/stage2/BuildBuild.scala b/stage2/BuildBuild.scala index 9746d8c..813b44b 100644 --- a/stage2/BuildBuild.scala +++ b/stage2/BuildBuild.scala @@ -5,7 +5,7 @@ import scala.collection.immutable.Seq class BuildBuild(context: Context) extends Build(context){ override def dependencies = Seq( CbtDependency()(context.logger) ) ++ super.dependencies def managedBuildDirectory: File = lib.realpath( projectDirectory.parent ) - val managedBuild = { + val managedBuild = try{ val managedContext = context.copy( cwd = managedBuildDirectory ) val cl = new cbt.URLClassLoader( exportedClasspath, @@ -16,6 +16,11 @@ class BuildBuild(context: Context) extends Build(context){ .getConstructor(classOf[Context]) .newInstance(managedContext) .asInstanceOf[Build] + } catch { + case e: ClassNotFoundException if e.getMessage == lib.buildClassName => + throw new Exception("You need to remove the directory or define a class Build in: "+context.cwd) + case e: Exception => + throw new Exception("during build: "+context.cwd, e) } override def triggerLoopFiles = super.triggerLoopFiles ++ managedBuild.triggerLoopFiles override def finalBuild = managedBuild.finalBuild diff --git a/stage2/BuildDependency.scala b/stage2/BuildDependency.scala index e3a01c7..19357f9 100644 --- a/stage2/BuildDependency.scala +++ b/stage2/BuildDependency.scala @@ -21,6 +21,7 @@ case class BuildDependency(context: Context) extends TriggerLoop{ final override lazy val lib: Lib = new Lib(logger) private val root = lib.loadRoot( context.copy(args=Seq()) ) lazy val build = root.finalBuild + override def canBeCached = build.canBeCached def exportedClasspath = ClassPath(Seq()) def exportedJars = Seq() def dependencies = Seq(build) diff --git a/stage2/GitDependency.scala b/stage2/GitDependency.scala index 59de98a..27bf253 100644 --- a/stage2/GitDependency.scala +++ b/stage2/GitDependency.scala @@ -7,9 +7,10 @@ import org.eclipse.jgit.lib.Ref case class GitDependency( url: String, ref: String // example: git://github.com/cvogt/cbt.git#<some-hash> -)(implicit val logger: Logger, classLoaderCache: ClassLoaderCache ) extends Dependency{ +)(implicit val logger: Logger, classLoaderCache: ClassLoaderCache, context: Context ) extends Dependency{ override def lib = new Lib(logger) + override def canBeCached = true // TODO: add support for authentication via ssh and/or https // See http://www.codeaffine.com/2014/12/09/jgit-authentication/ @@ -37,7 +38,7 @@ case class GitDependency( } val managedBuild = lib.loadDynamic( - Context( cwd = checkoutDirectory, args = Seq(), logger, classLoaderCache ) + context.copy( cwd = checkoutDirectory, args = Seq() ) ) Seq( managedBuild ) } diff --git a/stage2/Lib.scala b/stage2/Lib.scala index dd4a12f..6b6263c 100644 --- a/stage2/Lib.scala +++ b/stage2/Lib.scala @@ -133,7 +133,7 @@ final class Lib(logger: Logger) extends Stage1Lib(logger) with Scaffold{ .filter{ m => java.lang.reflect.Modifier.isPublic(m.getModifiers) } - .filter( _.getParameterCount == 0 ) + .filter( _.getParameterTypes.length == 0 ) .map(m => NameTransformer.decode(m.getName) -> m) ).toMap @@ -145,7 +145,7 @@ final class Lib(logger: Logger) extends Stage1Lib(logger) with Scaffold{ ( ( if( thisTasks.nonEmpty ){ - s"""Methods provided by Build ${context} + s"""Methods provided by Build ${context.cwd} ${thisTasks.mkString(" ")} diff --git a/stage2/SbtDependencyDsl.scala b/stage2/SbtDependencyDsl.scala new file mode 100644 index 0000000..4fd4250 --- /dev/null +++ b/stage2/SbtDependencyDsl.scala @@ -0,0 +1,43 @@ +package cbt +trait SbtDependencyDsl{ self: Build => + /** SBT-like dependency builder DSL for syntax compatibility */ + class DependencyBuilder2( groupId: String, artifactId: String, scalaVersion: Option[String] ){ + def %(version: String) = scalaVersion.map( + v => ScalaDependency(groupId, artifactId, version, scalaVersion = v) + ).getOrElse( + MavenDependency(groupId, artifactId, version) + ) + } + implicit class DependencyBuilder(groupId: String){ + def %%(artifactId: String) = new DependencyBuilder2( groupId, artifactId, Some(scalaMajorVersion) ) + def %(artifactId: String) = new DependencyBuilder2( groupId, artifactId, None ) + } + implicit class DependencyBuilder3(d: MavenDependency){ + def %(classifier: String) = d.copy(classifier = Classifier(Some(classifier))) + } + + /* + /** SBT-like dependency builder DSL for syntax compatibility */ + implicit class DependencyBuilder0(repository: Maven){ + def %(groupId: String) = new DependencyBuilder1b(repository, groupId) + } + implicit class DependencyBuilder1a(groupId: String){ + def %%(artifactId: String) = new DependencyBuilder2( Maven.central, groupId, artifactId, Some(scalaMajorVersion) ) + def %(artifactId: String) = new DependencyBuilder2( Maven.central, groupId, artifactId, None ) + } + class DependencyBuilder1b(repository: Maven, groupId: String){ + def %%(artifactId: String) = new DependencyBuilder2( repository, groupId, artifactId, Some(scalaMajorVersion) ) + def %(artifactId: String) = new DependencyBuilder2( repository, groupId, artifactId, None ) + } + class DependencyBuilder2( repository: Maven, groupId: String, artifactId: String, scalaMajorVersion: Option[String] ){ + def %(version: String) = scalaMajorVersion.map( + v => repository(groupId, artifactId, version, scalaMajorVersion = v) + ).getOrElse( + repository.java(groupId, artifactId, version) + ) + } + implicit class DependencyBuilder3(d: MavenDependency){ + def %(classifier: String) = d.copy(classifier = Classifier(Some(classifier))) + } + */ +}
\ No newline at end of file diff --git a/stage2/Stage2.scala b/stage2/Stage2.scala index e893a06..4ae149c 100644 --- a/stage2/Stage2.scala +++ b/stage2/Stage2.scala @@ -1,8 +1,6 @@ package cbt import java.io._ -import java.time._ -import java.time.LocalTime.now import scala.collection.immutable.Seq @@ -14,7 +12,7 @@ object Stage2 extends Stage2Base{ val lib = new Lib(args.logger) - logger.stage2(s"[$now] Stage2 start") + logger.stage2(s"Stage2 start") val loop = args.args.lift(0) == Some("loop") val direct = args.args.lift(0) == Some("direct") @@ -25,7 +23,7 @@ object Stage2 extends Stage2Base{ } val task = args.args.lift( taskIndex ) - val context = Context( args.cwd, args.args.drop( taskIndex ), logger, /*args.cbtHasChanged,*/ new ClassLoaderCache(logger) ) + val context = Context( args.cwd, args.args.drop( taskIndex ), logger, args.cbtHasChanged, new ClassLoaderCache(logger) ) val first = lib.loadRoot( context ) val build = first.finalBuild @@ -54,6 +52,6 @@ object Stage2 extends Stage2Base{ new lib.ReflectBuild(build).callNullary(task) } - logger.stage2(s"[$now] Stage2 end") + logger.stage2(s"Stage2 end") } } diff --git a/stage2/mixins.scala b/stage2/mixins.scala index c3a57da..4dedd09 100644 --- a/stage2/mixins.scala +++ b/stage2/mixins.scala @@ -16,9 +16,9 @@ trait SbtTest extends Test{ trait ScalaTest extends Build with Test{ def scalaTestVersion: String - override def dependencies = Seq( + override def dependencies = super.dependencies :+ MavenRepository.central.resolve( "org.scalatest" %% "scalatest" % scalaTestVersion - ) ++ super.dependencies + ) override def run: ExitCode = { val discoveryPath = compile.toString++"/" diff --git a/test/simple/build/build.scala b/test/simple/build/build.scala index d3887b3..190bad9 100644 --- a/test/simple/build/build.scala +++ b/test/simple/build/build.scala @@ -1,14 +1,30 @@ import cbt._ import scala.collection.immutable.Seq import java.io.File + class Build(context: cbt.Context) extends BasicBuild(context){ - override def dependencies = Seq( - ScalaDependency("com.typesafe.play", "play-json", "2.4.4"), - JavaDependency("joda-time", "joda-time", "2.9.2"), - GitDependency("https://github.com/xdotai/diff.git", "2e275642041006ff39efde22da7742c2e9a0f63f"), - // the below tests pom inheritance with dependencyManagement and variable substitution for pom properties - JavaDependency("org.eclipse.jgit", "org.eclipse.jgit", "4.2.0.201601211800-r"), - // the below tests pom inheritance with variable substitution for pom xml tag contents - JavaDependency("com.spotify", "missinglink-core", "0.1.1") - ) ++ super.dependencies + override def dependencies = ( + super.dependencies + ++ + Seq( + GitDependency("https://github.com/xdotai/diff.git", "8b501902999fe76d49e04937c4bd6d0b9e07b4a6"), + MavenRepository.central.resolve( + ScalaDependency("com.typesafe.play", "play-json", "2.4.4"), + MavenDependency("joda-time", "joda-time", "2.9.2"), + // the below tests pom inheritance with dependencyManagement and variable substitution for pom properties + MavenDependency("org.eclipse.jgit", "org.eclipse.jgit", "4.2.0.201601211800-r"), + // the below tests pom inheritance with variable substitution for pom xml tag contents + MavenDependency("com.spotify", "missinglink-core", "0.1.1") + ), + MavenRepository.combine( + MavenRepository.central, + MavenRepository.bintray("tpolecat"), + MavenRepository.sonatypeSnapshots + ).resolve( + "org.cvogt" %% "play-json-extensions" % "0.8.0", + "org.tpolecat" %% "tut-core" % "0.4.2", + "ai.x" %% "lens" % "1.0.0-SNAPSHOT" + ) + ) + ) } diff --git a/test/test.scala b/test/test.scala index 242b639..7bd2c6a 100644 --- a/test/test.scala +++ b/test/test.scala @@ -1,12 +1,14 @@ import cbt._ import cbt.paths._ import scala.collection.immutable.Seq +import java.io.File // micro framework object Main{ def main(_args: Array[String]): Unit = { val args = new Stage1ArgsParser(_args.toVector) - implicit val logger: Logger = new Logger(args.enabledLoggers) + implicit val logger: Logger = new Logger(args.enabledLoggers, System.currentTimeMillis) + val lib = new Lib(logger) var successes = 0 var failures = 0 @@ -69,19 +71,14 @@ object Main{ logger.test( "Running tests " ++ _args.toList.toString ) - usage("nothing") - compile("nothing") - usage("multi-build") - compile("multi-build") - usage("simple") - compile("simple") - { - val noContext = Context(cbtHome ++ "/test/nothing", Seq(), logger, new ClassLoaderCache(logger)) + val noContext = Context(cbtHome ++ "/test/nothing", Seq(), logger, false, new ClassLoaderCache(logger)) val b = new Build(noContext){ override def dependencies = Seq( - JavaDependency("net.incongru.watchservice","barbary-watchservice","1.0"), - JavaDependency("net.incongru.watchservice","barbary-watchservice","1.0") + MavenRepository.central.resolve( + MavenDependency("net.incongru.watchservice","barbary-watchservice","1.0"), + MavenDependency("net.incongru.watchservice","barbary-watchservice","1.0") + ) ) } val cp = b.classpath @@ -90,22 +87,48 @@ object Main{ // test that messed up artifacts crash with an assertion (which should tell the user what's up) assertException[AssertionError](){ - JavaDependency("com.jcraft", "jsch", " 0.1.53").classpath + MavenRepository.central.resolveOne( MavenDependency("com.jcraft", "jsch", " 0.1.53") ).classpath } assertException[AssertionError](){ - JavaDependency("com.jcraft", null, "0.1.53").classpath + MavenRepository.central.resolveOne( MavenDependency("com.jcraft", null, "0.1.53") ).classpath } assertException[AssertionError](){ - JavaDependency("com.jcraft", "", " 0.1.53").classpath + MavenRepository.central.resolveOne( MavenDependency("com.jcraft", "", " 0.1.53") ).classpath } assertException[AssertionError](){ - JavaDependency("com.jcraft%", "jsch", " 0.1.53").classpath + MavenRepository.central.resolveOne( MavenDependency("com.jcraft%", "jsch", " 0.1.53") ).classpath } assertException[AssertionError](){ - JavaDependency("", "jsch", " 0.1.53").classpath + MavenRepository.central.resolveOne( MavenDependency("", "jsch", " 0.1.53") ).classpath } - + ( + MavenRepository.combine( + MavenRepository.central, MavenRepository.bintray("tpolecat") + ).resolve( + lib.ScalaDependency("org.tpolecat","tut-core","0.4.2", scalaMajorVersion="2.11") + ).classpath.strings + ++ + MavenRepository.sonatype.resolve( + MavenDependency("org.cvogt","play-json-extensions_2.11","0.8.0") + ).classpath.strings + ++ + MavenRepository.combine( + MavenRepository.central, MavenRepository.sonatypeSnapshots + ).resolve( + MavenDependency("ai.x","lens_2.11","1.0.0-SNAPSHOT") + ).classpath.strings + ).foreach{ + path => assert(new File(path).exists, path) + } + + usage("nothing") + compile("nothing") + usage("multi-build") + compile("multi-build") + usage("simple") + compile("simple") + System.err.println(" DONE!") System.err.println( successes.toString ++ " succeeded, "++ failures.toString ++ " failed" ) if(failures > 0) System.exit(1) else System.exit(0) |