aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--build/build.scala9
-rw-r--r--build/build/build.scala2
-rwxr-xr-xcbt2
-rw-r--r--doc/design.md35
-rw-r--r--internal/plugins/library/Library.scala51
-rw-r--r--internal/plugins/library/build/build.scala7
-rw-r--r--internal/plugins/shared/Shared.scala2
-rw-r--r--libraries/capture_args/build/build.scala12
-rw-r--r--libraries/capture_args/build/build/build.scala5
-rw-r--r--libraries/capture_args/package.scala28
-rw-r--r--libraries/common-0/ProxySecurityManager.java (renamed from nailgun_launcher/ProxySecurityManager.java)92
-rw-r--r--libraries/common-0/TrapSecurityManager.java (renamed from nailgun_launcher/TrapSecurityManager.java)59
-rw-r--r--libraries/common-0/TrapSystemExit.java35
-rw-r--r--libraries/common-0/build/build.scala7
-rw-r--r--libraries/common-0/build/build/build.scala5
-rw-r--r--libraries/common-1/ExitCode.java24
-rw-r--r--libraries/common-1/ExitCode.scala11
-rw-r--r--libraries/common-1/build/build.scala8
-rw-r--r--libraries/common-1/build/build/build.scala5
-rw-r--r--libraries/common-1/common_1.scala34
-rw-r--r--libraries/file/build/build.scala8
-rw-r--r--libraries/file/build/build/build.scala5
-rw-r--r--libraries/file/file.scala112
-rw-r--r--libraries/interfaces/ExitCode.java4
-rw-r--r--libraries/proguard/Proguard.scala10
-rw-r--r--libraries/proguard/build/build.scala8
-rw-r--r--libraries/proguard/build/build/build.scala8
-rw-r--r--libraries/reflect/StaticMethod.scala4
-rw-r--r--libraries/reflect/build/build.scala8
-rw-r--r--libraries/reflect/build/build/build.scala5
-rw-r--r--libraries/reflect/reflect.scala177
-rw-r--r--nailgun_launcher/NailgunLauncher.java29
-rw-r--r--nailgun_launcher/Stage0Lib.java33
-rw-r--r--plugins/proguard/Proguard.scala2
-rw-r--r--plugins/uber-jar/src/UberJar.scala2
-rw-r--r--stage1/Stage1Lib.scala125
-rw-r--r--stage1/cbt.scala62
-rw-r--r--stage1/resolver.scala29
-rw-r--r--stage2/BasicBuild.scala11
-rw-r--r--stage2/BuildBuild.scala18
-rw-r--r--stage2/DirectoryDependency.scala2
-rw-r--r--stage2/Lib.scala58
-rw-r--r--stage2/libraries.scala12
-rw-r--r--stage2/plugins.scala17
-rw-r--r--stage2/plugins/Dotty.scala6
-rw-r--r--test/build/build.scala1
46 files changed, 819 insertions, 370 deletions
diff --git a/build/build.scala b/build/build.scala
index c5ad1b7..e077343 100644
--- a/build/build.scala
+++ b/build/build.scala
@@ -1,3 +1,4 @@
+package cbt_build.cbt
import cbt._
import cbt_internal._
@@ -12,8 +13,8 @@ class Build(val context: Context) extends Shared with Scalariform with PublishLo
super.dependencies ++ Resolver(mavenCentral).bind(
MavenDependency("org.eclipse.jgit", "org.eclipse.jgit", "4.2.0.201601211800-r"),
ScalaDependency("org.scala-lang.modules","scala-xml",constants.scalaXmlVersion)
- )
- } :+ libraries.eval
+ ) :+ libraries.reflect :+ libraries.eval
+ }
override def sources = Seq(
"nailgun_launcher", "stage1", "stage2", "compatibility"
@@ -22,7 +23,9 @@ class Build(val context: Context) extends Shared with Scalariform with PublishLo
override def scalariform = super.scalariform.copy(
Seq(
context.cbtHome / "stage2" / "DirectoryDependency.scala",
- context.cbtHome / "stage2" / "LazyDependency.scala"
+ context.cbtHome / "stage2" / "LazyDependency.scala",
+ context.cbtHome / "stage2" / "libraries.scala",
+ context.cbtHome / "stage2" / "plugins.scala"
)
)
diff --git a/build/build/build.scala b/build/build/build.scala
index 6752f27..44b8b71 100644
--- a/build/build/build.scala
+++ b/build/build/build.scala
@@ -1,6 +1,6 @@
package cbt_build.cbt.build
import cbt._
-class Build(val context: Context) extends CbtInternal{
+class Build(val context: Context) extends BuildBuild with CbtInternal{
override def dependencies = (
super.dependencies :+ cbtInternal.shared :+ plugins.scalariform
)
diff --git a/cbt b/cbt
index 53fe9f4..61fc58a 100755
--- a/cbt
+++ b/cbt
@@ -196,7 +196,7 @@ stage1 () {
log "Checking for changes in cbt/nailgun_launcher" "$@"
NAILGUN_INDICATOR=$NAILGUN$TARGET../classes.last-success
changed=1
- NAILGUN_SOURCES=("$NAILGUN"*.java)
+ NAILGUN_SOURCES=("$NAILGUN"*.java "$CBT_HOME"/libraries/common-0/*.java)
for file in "${NAILGUN_SOURCES[@]}"; do
if [ "$file" -nt "$NAILGUN_INDICATOR" ]; then changed=0; fi
done
diff --git a/doc/design.md b/doc/design.md
index 28bbdaa..38e5434 100644
--- a/doc/design.md
+++ b/doc/design.md
@@ -83,6 +83,40 @@ class Build(val context: Context) extends SomePlugin{
Such a simple replacement of `b` while keeping all other arguments would
not be easily possible if doSomething was a def not a case class.
+## Why do the libraries have ops packages and Module traits?
+
+Java's and Scala's package system does allow importing things,
+but not exporting things. Everything has to be imported
+explicitly anywhere it is supposed to be used. It's just a
+package system, not a module system. This leads to a lot of
+import boiler plate. CBT tries to minimize the imports
+necessary for it's use however. So how to we do this while
+at the same time allowing modularization? In particular, how
+do we do this with stand-alone methods and implicit classes
+that have to be in an object, e.g. the package object?
+
+Scala's traits can be used as a module system that supports exports.
+This means we can take several modules (traits) and merge them into
+something that exports everything defined in any of them. Basically
+inheriting a trait means importing names into the scope of the
+inheriting class and exporting those names to the class's users.
+
+CBT's libraries define Module traits, which their package objects inherit.
+This makes it easy to use the libraries by itself. CBT's core however
+also inherits all of the library Module traits in it's package object,
+meaning that by a simple `import cbt._` you get everything from all
+libraries. This solves the import boiler plate.
+
+For implicit classes it is a little bit trickier as those should
+extend AnyVal for performance reasons, but that's only allowed in
+an object, not a trait. So what we do instead is put Universal traits
+(see http://docs.scala-lang.org/overviews/core/value-classes.html)
+containing all the logic into a helper package `ops`. The package
+objects that want to offer the implicit classes now define them
+extending the Universal traits. This means a little boiler plate
+where the package object is define, but also solves the import
+boiler plate everywhere else.
+
## What is newBuild and why do we need it?
Methods in a class can call each other and thereby effectively form a graph.
@@ -137,3 +171,4 @@ trait CrossVersionPlugin{
Problem solved. In fact this allows for a very, very flexible way of
creating differents variants of your build.
+
diff --git a/internal/plugins/library/Library.scala b/internal/plugins/library/Library.scala
new file mode 100644
index 0000000..a9dec7c
--- /dev/null
+++ b/internal/plugins/library/Library.scala
@@ -0,0 +1,51 @@
+package cbt_internal
+import cbt._
+import java.io._
+import scala.concurrent._
+import scala.concurrent.duration._
+trait Library extends Scalariform with GoogleJavaFormat with DynamicOverrides with AdvancedScala{
+ def inceptionYear: Int
+ def description: String
+ def version = ???
+ override def compile = {
+ googleJavaFormat()
+ scalariform()
+ super.compile
+ }
+
+ def publishIfChanged = newBuild[PublishIfChanged]({s"""
+ def inceptionYear = $inceptionYear
+ def description = ${description.quote}
+ def apply = if(changedInMaster) publish
+ """})
+}
+
+trait PublishIfChanged extends PackageJars with DynamicOverrides with Shared{
+ override def url = super.url ++ "/libraries/" ++ name
+
+ def gitHash = {
+ val p = new ProcessBuilder(
+ "git rev-parse HEAD".split(" "): _*
+ )
+ .directory( projectDirectory )
+ .start
+
+ val sout = new InputStreamReader(p.getInputStream);
+ import scala.concurrent.ExecutionContext.Implicits.global
+ val out = Future(blocking(Iterator.continually(sout.read).takeWhile(_ != -1).map(_.toChar).mkString))
+ p.waitFor
+ val revision = Await.result( out, Duration.Inf ).trim
+ revision
+ }
+ override def version = "rev-"++gitHash
+
+ def changedInMaster = (
+ 0 ===
+ new ProcessBuilder(
+ "git diff --exit-code --quiet master..master^ .".split(" "): _*
+ )
+ .directory( projectDirectory )
+ .start
+ .waitFor
+ )
+}
diff --git a/internal/plugins/library/build/build.scala b/internal/plugins/library/build/build.scala
new file mode 100644
index 0000000..6b9432e
--- /dev/null
+++ b/internal/plugins/library/build/build.scala
@@ -0,0 +1,7 @@
+package cbt_build.cbt_internal.library_build_plugin
+import cbt._
+class Build(val context: Context) extends Plugin with CbtInternal{
+ override def dependencies = (
+ super.dependencies :+ cbtInternal.shared :+ plugins.scalariform :+ plugins.googleJavaFormat
+ )
+}
diff --git a/internal/plugins/shared/Shared.scala b/internal/plugins/shared/Shared.scala
index 90bc4b2..2db9770 100644
--- a/internal/plugins/shared/Shared.scala
+++ b/internal/plugins/shared/Shared.scala
@@ -1,7 +1,7 @@
package cbt_internal
import cbt._
import java.net.URL
-trait Shared extends SonatypeRelease with SnapshotVersion with GithubPom{
+trait Shared extends AdvancedScala with SonatypeRelease with SnapshotVersion with GithubPom{
override def user = "cvogt"
override def groupId = "org.cvogt"
override def organization = Some( Organization( "Jan Christopher Vogt", Some( new URL("http://cvogt.org") ) ) )
diff --git a/libraries/capture_args/build/build.scala b/libraries/capture_args/build/build.scala
index 24c1faa..fab36bf 100644
--- a/libraries/capture_args/build/build.scala
+++ b/libraries/capture_args/build/build.scala
@@ -1,11 +1,21 @@
package cbt_build.cbt.capture_args
import cbt._
-class Build(val context: Context) extends BaseBuild{
+import cbt_internal._
+class Build(val context: Context) extends Library{
+ def description = (
+ "macro that allows you to extract a functions arguments"
+ ++" as strings in order to programmatically pass them to a stringly typed"
+ ++" api such as a process call, http or a .main method"
+ )
+
+ def inceptionYear = 2017
+
override def dependencies = (
super.dependencies ++ // don't forget super.dependencies here for scala-library, etc.
Resolver( mavenCentral ).bind(
MavenDependency( "org.scala-lang", "scala-reflect", scalaVersion )
)
)
+
override def scalacOptions = super.scalacOptions :+ "-language:experimental.macros"
}
diff --git a/libraries/capture_args/build/build/build.scala b/libraries/capture_args/build/build/build.scala
new file mode 100644
index 0000000..6fabf47
--- /dev/null
+++ b/libraries/capture_args/build/build/build.scala
@@ -0,0 +1,5 @@
+package cbt_build.cbt.capture_args.build
+import cbt._
+class Build(val context: Context) extends BuildBuild with CbtInternal{
+ override def dependencies = super.dependencies :+ cbtInternal.library
+}
diff --git a/libraries/capture_args/package.scala b/libraries/capture_args/package.scala
index f5cd219..5c96a8d 100644
--- a/libraries/capture_args/package.scala
+++ b/libraries/capture_args/package.scala
@@ -2,29 +2,29 @@ package cbt.capture_args
import scala.reflect._
import scala.reflect.macros.blackbox.Context
-case class Argument( annotations: Seq[annotation.Annotation], name: String, values: Option[Seq[String]] ){
+case class Argument( annotations: Seq[annotation.Annotation], name: String, values: Option[Seq[String]] ) {
def toSeqOption = values.map( name +: _ )
}
case class Signature( name: String, args: Seq[Argument] )
-object `package`{
- def captureArgsImplementation(c: Context): c.Tree = {
+object `package` {
+ def captureArgsImplementation( c: Context ): c.Tree = {
import c.universe._
- def literal( a: Any ) = Literal(Constant(a))
- def ident( name: String ) = Ident(TermName(name))
+ def literal( a: Any ) = Literal( Constant( a ) )
+ def ident( name: String ) = Ident( TermName( name ) )
- def findOwnerRecursive(symbol: Symbol, predicate: Symbol => Boolean): Option[Symbol] = {
- Option(symbol).flatMap{
+ def findOwnerRecursive( symbol: Symbol, predicate: Symbol => Boolean ): Option[Symbol] = {
+ Option( symbol ).flatMap {
s =>
- if(s == NoSymbol) None else if(predicate(s)) Some(s) else findOwnerRecursive(s.owner, predicate)
+ if ( s == NoSymbol ) None else if ( predicate( s ) ) Some( s ) else findOwnerRecursive( s.owner, predicate )
}
}
val method: MethodSymbol = (
- findOwnerRecursive(c.internal.enclosingOwner, _.isMethod).map(_.asMethod)
+ findOwnerRecursive( c.internal.enclosingOwner, _.isMethod ).map( _.asMethod )
orElse
- findOwnerRecursive(c.internal.enclosingOwner, _.isClass).map(_.asClass.primaryConstructor.asMethod)
+ findOwnerRecursive( c.internal.enclosingOwner, _.isClass ).map( _.asClass.primaryConstructor.asMethod )
getOrElse {
c.error(
c.enclosingPosition,
@@ -33,14 +33,14 @@ object `package`{
???
}
)
- val name = literal(method.name.decodedName.toString)
+ val name = literal( method.name.decodedName.toString )
// Note: method.paramLists requires explicitly annotated result type
- val params = method.paramLists.flatten.map(_.asTerm)
+ val params = method.paramLists.flatten.map( _.asTerm )
- val args = params.map{ s =>
+ val args = params.map { s =>
val name = literal( s.name.decodedName.toString )
val i = ident( s.name.toString )
- q"_root_.cbt.capture_args.Argument( _root_.scala.Seq( ..${s.annotations.map(_.tree)} ), $name, valueToStrings($i) )"
+ q"_root_.cbt.capture_args.Argument( _root_.scala.Seq( ..${s.annotations.map( _.tree )} ), $name, valueToStrings($i) )"
}
val tree = q"""
_root_.cbt.capture_args.Signature( name = $name, args = Seq( ..$args ) )
diff --git a/nailgun_launcher/ProxySecurityManager.java b/libraries/common-0/ProxySecurityManager.java
index 1a6e49c..4669add 100644
--- a/nailgun_launcher/ProxySecurityManager.java
+++ b/libraries/common-0/ProxySecurityManager.java
@@ -1,4 +1,4 @@
-package cbt;
+package cbt.reflect;
import java.security.*;
import java.io.FileDescriptor;
@@ -9,94 +9,122 @@ SecurityManager proxy that forwards all calls to the provided target if != null.
Useful to replace a previously installed SecurityManager, overriding some methods
but forwarding the rest.
*/
-public class ProxySecurityManager extends SecurityManager{
+class ProxySecurityManager extends SecurityManager {
private SecurityManager target;
- public ProxySecurityManager(SecurityManager target){
+
+ protected ProxySecurityManager(SecurityManager target) {
this.target = target;
}
+
public Object getSecurityContext() {
- if(target != null)
- return target.getSecurityContext();
+ if (target != null) return target.getSecurityContext();
else return super.getSecurityContext();
}
+
public void checkPermission(Permission perm) {
- if(target != null) target.checkPermission(perm);
+ if (target != null) target.checkPermission(perm);
}
+
public void checkPermission(Permission perm, Object context) {
- if(target != null) target.checkPermission(perm, context);
+ if (target != null) target.checkPermission(perm, context);
}
+
public void checkCreateClassLoader() {
- if(target != null) target.checkCreateClassLoader();
+ if (target != null) target.checkCreateClassLoader();
}
+
public void checkAccess(Thread t) {
- if(target != null) target.checkAccess(t);
+ if (target != null) target.checkAccess(t);
}
+
public void checkAccess(ThreadGroup g) {
- if(target != null) target.checkAccess(g);
+ if (target != null) target.checkAccess(g);
}
+
public void checkExit(int status) {
- if(target != null) target.checkExit(status);
+ if (target != null) target.checkExit(status);
}
+
public void checkExec(String cmd) {
- if(target != null) target.checkExec(cmd);
+ if (target != null) target.checkExec(cmd);
}
+
public void checkLink(String lib) {
- if(target != null) target.checkLink(lib);
+ if (target != null) target.checkLink(lib);
}
+
+ /*
public void checkRead(FileDescriptor fd) {
- if(target != null) target.checkRead(fd);
+ if (target != null) target.checkRead(fd);
}
+
public void checkRead(String file) {
- if(target != null) target.checkRead(file);
+ if (target != null) target.checkRead(file);
}
+
public void checkRead(String file, Object context) {
- if(target != null) target.checkRead(file, context);
+ if (target != null) target.checkRead(file, context);
}
+ */
+
public void checkWrite(FileDescriptor fd) {
- if(target != null) target.checkWrite(fd);
+ if (target != null) target.checkWrite(fd);
}
+
public void checkWrite(String file) {
- if(target != null) target.checkWrite(file);
+ if (target != null) target.checkWrite(file);
}
+
public void checkDelete(String file) {
- if(target != null) target.checkDelete(file);
+ if (target != null) target.checkDelete(file);
}
+
public void checkConnect(String host, int port) {
- if(target != null) target.checkConnect(host, port);
+ if (target != null) target.checkConnect(host, port);
}
+
public void checkConnect(String host, int port, Object context) {
- if(target != null) target.checkConnect(host, port, context);
+ if (target != null) target.checkConnect(host, port, context);
}
+
public void checkListen(int port) {
- if(target != null) target.checkListen(port);
+ if (target != null) target.checkListen(port);
}
+
public void checkAccept(String host, int port) {
- if(target != null) target.checkAccept(host, port);
+ if (target != null) target.checkAccept(host, port);
}
+
public void checkMulticast(InetAddress maddr) {
- if(target != null) target.checkMulticast(maddr);
+ if (target != null) target.checkMulticast(maddr);
}
+
public void checkPropertiesAccess() {
- if(target != null) target.checkPropertiesAccess();
+ if (target != null) target.checkPropertiesAccess();
}
+
public void checkPropertyAccess(String key) {
- if(target != null) target.checkPropertyAccess(key);
+ if (target != null) target.checkPropertyAccess(key);
}
+
public void checkPrintJobAccess() {
- if(target != null) target.checkPrintJobAccess();
+ if (target != null) target.checkPrintJobAccess();
}
+
public void checkPackageAccess(String pkg) {
- if(target != null) target.checkPackageAccess(pkg);
+ if (target != null) target.checkPackageAccess(pkg);
}
+
public void checkPackageDefinition(String pkg) {
- if(target != null) target.checkPackageDefinition(pkg);
+ if (target != null) target.checkPackageDefinition(pkg);
}
+
public void checkSetFactory() {
- if(target != null) target.checkSetFactory();
+ if (target != null) target.checkSetFactory();
}
+
public ThreadGroup getThreadGroup() {
- if(target != null)
- return target.getThreadGroup();
+ if (target != null) return target.getThreadGroup();
else return super.getThreadGroup();
}
}
diff --git a/nailgun_launcher/TrapSecurityManager.java b/libraries/common-0/TrapSecurityManager.java
index be59671..161b74f 100644
--- a/nailgun_launcher/TrapSecurityManager.java
+++ b/libraries/common-0/TrapSecurityManager.java
@@ -1,4 +1,5 @@
-package cbt;
+package cbt.reflect;
+
import java.security.*;
/*
When enabled, this SecurityManager turns System.exit(...) calls into exceptions that can be caught and handled.
@@ -8,76 +9,80 @@ When disabled this delegates to the SecurityManager installed before if any, whi
would be Nailgun's if running on Nailgun. If we do not delegate to Nailgun, it seems we
could in some cases kill the server process
*/
-public class TrapSecurityManager extends ProxySecurityManager{
- public static ThreadLocal<Boolean> trapExitCode(){
+public class TrapSecurityManager extends ProxySecurityManager {
+ public static ThreadLocal<Boolean> trapExitCode() {
// storing the flag in the installed security manager
// instead of e.g. a static member is necessary because
// we run multiple versions of CBT with multiple TrapSecurityManager classes
// but we need to affect the installed one
SecurityManager sm = System.getSecurityManager();
- if(sm instanceof TrapSecurityManager){
+ if (sm instanceof TrapSecurityManager) {
return ((TrapSecurityManager) sm)._trapExitCode;
} else {
- try{
+ try {
@SuppressWarnings("unchecked")
ThreadLocal<Boolean> res =
- (ThreadLocal<Boolean>) sm.getClass().getMethod("trapExitCode").invoke(null);
+ (ThreadLocal<Boolean>) sm.getClass().getMethod("trapExitCode").invoke(null);
return res;
- } catch(Exception e) {
+ } catch (Exception e) {
throw new RuntimeException(e);
}
}
}
private final ThreadLocal<Boolean> _trapExitCode =
- new ThreadLocal<Boolean>() {
- @Override protected Boolean initialValue() {
- return false;
- }
- };
+ new ThreadLocal<Boolean>() {
+ @Override
+ protected Boolean initialValue() {
+ return false;
+ }
+ };
- public TrapSecurityManager(){
- super(NailgunLauncher.initialSecurityManager);
+ protected TrapSecurityManager(SecurityManager parent) {
+ super(parent);
}
- public void checkPermission( Permission permission ){
+ public void checkPermission(Permission permission) {
/*
NOTE: is it actually ok, to just make these empty?
Calling .super leads to ClassNotFound exteption for a lambda.
Calling to the previous SecurityManager leads to a stack overflow
*/
- if(!TrapSecurityManager.trapExitCode().get()){
+ if (!TrapSecurityManager.trapExitCode().get()) {
super.checkPermission(permission);
}
}
- public void checkPermission( Permission permission, Object context ){
+
+ public void checkPermission(Permission permission, Object context) {
/* Does this methods need to be overidden? */
- if(!TrapSecurityManager.trapExitCode().get()){
+ if (!TrapSecurityManager.trapExitCode().get()) {
super.checkPermission(permission, context);
}
}
+ // FIXME: we should probably choose a more unique name for this
private static final String prefix = "[TrappedExit] ";
@Override
- public void checkExit( int status ){
- if(TrapSecurityManager.trapExitCode().get()){
+ public void checkExit(int status) {
+ if (TrapSecurityManager.trapExitCode().get()) {
// using a RuntimeException and a prefix here instead of a custom
// exception type because this is thrown by the installed TrapSecurityManager
// but other versions of cbt need to be able to catch it, that do not have access
// to that version of the TrapSecurityManager class
- throw new RuntimeException(prefix+status);
+ throw new RuntimeException(prefix + status);
}
super.checkExit(status);
}
- public static boolean isTrappedExit( Throwable t ){
- return t instanceof RuntimeException && t.getMessage() != null && t.getMessage().startsWith(prefix);
+ public static boolean isTrappedExit(Throwable t) {
+ return t instanceof RuntimeException
+ && t.getMessage() != null
+ && t.getMessage().startsWith(prefix);
}
- public static int exitCode( Throwable t ){
- assert(isTrappedExit(t));
- return Integer.parseInt( t.getMessage().substring(prefix.length()) );
+ public static int exitCode(Throwable t) {
+ assert (isTrappedExit(t));
+ return Integer.parseInt(t.getMessage().substring(prefix.length()));
}
-
}
diff --git a/libraries/common-0/TrapSystemExit.java b/libraries/common-0/TrapSystemExit.java
new file mode 100644
index 0000000..86bc880
--- /dev/null
+++ b/libraries/common-0/TrapSystemExit.java
@@ -0,0 +1,35 @@
+package cbt.reflect;
+
+import java.security.*;
+import java.lang.reflect.InvocationTargetException;
+
+public abstract class TrapSystemExit<T> {
+ public static SecurityManager createSecurityManager(SecurityManager delegateTo) {
+ return new TrapSecurityManager(delegateTo);
+ }
+
+ public static <T> T run(TrapSystemExit<T> runnable) throws Throwable {
+ boolean trapExitCodeBefore = TrapSecurityManager.trapExitCode().get();
+ try {
+ TrapSecurityManager.trapExitCode().set(true);
+ return runnable.run();
+ } catch (InvocationTargetException exception) {
+ Throwable cause = exception.getCause();
+ if (TrapSecurityManager.isTrappedExit(cause)) {
+ return runnable.wrap(TrapSecurityManager.exitCode(cause));
+ }
+ throw exception;
+ } catch (Exception exception) {
+ if (TrapSecurityManager.isTrappedExit(exception)) {
+ return runnable.wrap(TrapSecurityManager.exitCode(exception));
+ }
+ throw exception;
+ } finally {
+ TrapSecurityManager.trapExitCode().set(trapExitCodeBefore);
+ }
+ }
+
+ public abstract T run() throws Throwable;
+
+ public abstract T wrap(int exitCode);
+}
diff --git a/libraries/common-0/build/build.scala b/libraries/common-0/build/build.scala
new file mode 100644
index 0000000..0a4e5cb
--- /dev/null
+++ b/libraries/common-0/build/build.scala
@@ -0,0 +1,7 @@
+package cbt_build.common_0
+import cbt._
+import cbt_internal._
+class Build(val context: Context) extends Library{
+ override def inceptionYear = 2017
+ override def description = "classes shared by multiple cbt libraries and needed in stage 0"
+}
diff --git a/libraries/common-0/build/build/build.scala b/libraries/common-0/build/build/build.scala
new file mode 100644
index 0000000..d3f98ce
--- /dev/null
+++ b/libraries/common-0/build/build/build.scala
@@ -0,0 +1,5 @@
+package cbt_build.reflect.build
+import cbt._
+class Build(val context: Context) extends BuildBuild with CbtInternal{
+ override def dependencies = super.dependencies :+ cbtInternal.library
+}
diff --git a/libraries/common-1/ExitCode.java b/libraries/common-1/ExitCode.java
new file mode 100644
index 0000000..1c16f67
--- /dev/null
+++ b/libraries/common-1/ExitCode.java
@@ -0,0 +1,24 @@
+package cbt;
+/*
+public class ExitCode{
+ public int integer;
+ public ExitCode(int integer){
+ this.integer = integer;
+ }
+ public static ExitCode apply(int integer){
+ return new ExitCode( integer );
+ }
+ public static ExitCode Success = new ExitCode(0);
+ public static ExitCode Failure = new ExitCode(1);
+
+ @Override
+ public boolean equals(Object other){
+ return (other instanceof ExitCode) && ((ExitCode) other).integer == integer;
+ }
+ @Override
+ public int hashCode(){
+ return integer;
+ }
+}
+
+*/
diff --git a/libraries/common-1/ExitCode.scala b/libraries/common-1/ExitCode.scala
new file mode 100644
index 0000000..41d9f3f
--- /dev/null
+++ b/libraries/common-1/ExitCode.scala
@@ -0,0 +1,11 @@
+package cbt
+// CLI interop
+case class ExitCode( integer: Int ) extends interfaces.ExitCode {
+ def ||( other: => ExitCode ) = if ( this == ExitCode.Success ) this else other
+ def &&( other: => ExitCode ) = if ( this != ExitCode.Success ) this else other
+}
+object ExitCode {
+ val Success = ExitCode( 0 )
+ val Failure = ExitCode( 1 )
+}
+
diff --git a/libraries/common-1/build/build.scala b/libraries/common-1/build/build.scala
new file mode 100644
index 0000000..247fd01
--- /dev/null
+++ b/libraries/common-1/build/build.scala
@@ -0,0 +1,8 @@
+package cbt_build.common_1
+import cbt._
+import cbt_internal._
+class Build(val context: Context) extends Library{
+ override def inceptionYear = 2017
+ override def description = "classes shared by multiple cbt libraries and needed in stage 1"
+ override def dependencies = super.dependencies :+ libraries.common_0 :+ libraries.interfaces
+}
diff --git a/libraries/common-1/build/build/build.scala b/libraries/common-1/build/build/build.scala
new file mode 100644
index 0000000..d3f98ce
--- /dev/null
+++ b/libraries/common-1/build/build/build.scala
@@ -0,0 +1,5 @@
+package cbt_build.reflect.build
+import cbt._
+class Build(val context: Context) extends BuildBuild with CbtInternal{
+ override def dependencies = super.dependencies :+ cbtInternal.library
+}
diff --git a/libraries/common-1/common_1.scala b/libraries/common-1/common_1.scala
new file mode 100644
index 0000000..66da224
--- /dev/null
+++ b/libraries/common-1/common_1.scala
@@ -0,0 +1,34 @@
+package cbt.common_1
+import cbt.ExitCode
+object `package` extends Module {
+ implicit class CbtExitCodeOps( val exitCode: ExitCode ) extends AnyVal with ops.CbtExitCodeOps
+ implicit class TypeInferenceSafeEquals[T]( val value: T ) extends AnyVal with ops.TypeInferenceSafeEquals[T]
+ implicit class CbtBooleanOps( val condition: Boolean ) extends AnyVal with ops.CbtBooleanOps
+ implicit class CbtStringOps( val string: String ) extends AnyVal with ops.CbtStringOps
+}
+
+package ops {
+ trait CbtExitCodeOps extends Any {
+ def exitCode: ExitCode
+ def ||( other: => ExitCode ) = if ( exitCode == ExitCode.Success ) exitCode else other
+ def &&( other: => ExitCode ) = if ( exitCode != ExitCode.Success ) exitCode else other
+ }
+ trait TypeInferenceSafeEquals[T] extends Any {
+ def value: T
+ /** if you don't manually upcast, this will catch comparing different types */
+ def ===( other: T ) = value == other
+ def =!=( other: T ) = value != other // =!= instead of !==, because it has better precedence
+ }
+ trait CbtBooleanOps extends Any {
+ def condition: Boolean
+ def option[T]( value: => T ): Option[T] = if ( condition ) Some( value ) else None
+ }
+ trait CbtStringOps extends Any {
+ def string: String
+ def escape = string.replace( "\\", "\\\\" ).replace( "\"", "\\\"" )
+ def quote = s""""$escape""""
+ def ~( right: String ): String = string + right
+ }
+}
+
+trait Module
diff --git a/libraries/file/build/build.scala b/libraries/file/build/build.scala
new file mode 100644
index 0000000..d9017a1
--- /dev/null
+++ b/libraries/file/build/build.scala
@@ -0,0 +1,8 @@
+package cbt_build.reflect
+import cbt._
+import cbt_internal._
+class Build(val context: Context) extends Library{
+ override def inceptionYear = 2017
+ override def description = "helpers to work with java io and nio"
+ override def dependencies = super.dependencies :+ libraries.common_1
+}
diff --git a/libraries/file/build/build/build.scala b/libraries/file/build/build/build.scala
new file mode 100644
index 0000000..d3f98ce
--- /dev/null
+++ b/libraries/file/build/build/build.scala
@@ -0,0 +1,5 @@
+package cbt_build.reflect.build
+import cbt._
+class Build(val context: Context) extends BuildBuild with CbtInternal{
+ override def dependencies = super.dependencies :+ cbtInternal.library
+}
diff --git a/libraries/file/file.scala b/libraries/file/file.scala
new file mode 100644
index 0000000..f20c9a8
--- /dev/null
+++ b/libraries/file/file.scala
@@ -0,0 +1,112 @@
+package cbt.file
+import java.io.File
+import java.nio.file.Files._
+import java.nio.file.StandardCopyOption._
+import cbt.common_1._
+object `package` extends Module {
+ implicit class CbtFileOps( val file: File ) extends ops.CbtFileOps
+}
+
+package ops {
+ trait CbtFileOps extends Any {
+ def file: File
+ def ++( s: String ): File = {
+ if ( s endsWith "/" ) throw new Exception(
+ """Trying to append a String that ends in "/" to a File would loose the trailing "/". Use .stripSuffix("/") if you need to."""
+ )
+ new File( string + s ) // PERFORMANCE HOTSPOT
+ }
+ def /( s: String ): File = {
+ new File( file, s )
+ }
+ def parent = realpath( file / ".." )
+ def string = file.toString
+
+ def listOrFail: Seq[File] = Option( file.listFiles ).getOrElse( throw new Exception( "no such file: " + file ) ).toVector
+ def listRecursive: Seq[File] =
+ file +: (
+ if ( file.isDirectory ) file.listOrFail.flatMap( _.listRecursive ).toVector else Vector[File]()
+ )
+
+ def lastModifiedRecursive = listRecursive.map( _.lastModified ).max
+
+ def readAsString = new String( readAllBytes( file.toPath ) )
+
+ def quote = s"""new _root_.java.io.File(${string.quote})"""
+ }
+}
+
+trait Module {
+ def realpath( name: File ) = new File( java.nio.file.Paths.get( name.getAbsolutePath ).normalize.toString )
+ def transformFiles( files: Seq[File], transform: String => String ): Seq[File] = {
+ transformFilesOrError( files, s => Right( transform( s ) ) )._1
+ }
+
+ def transformFilesOrError[T]( files: Seq[File], transform: String => Either[T, String] ): ( Seq[File], Seq[( File, T )] ) = {
+ val results = files.map { file =>
+ val string = file.readAsString
+ transform( string ).left.map(
+ file -> _
+ ).right.map(
+ replaced =>
+ if ( string != replaced ) {
+ val tmpFile = file ++ ".cbt-tmp"
+ assert( !tmpFile.exists )
+ write( tmpFile.toPath, replaced.getBytes )
+ move( tmpFile.toPath, file.toPath, REPLACE_EXISTING )
+ Some( file )
+ } else None
+ )
+ }
+
+ ( results.map( _.right.toOption ).flatten.flatten, results.map( _.left.toOption ).flatten )
+ }
+
+ def autoRelative(
+ files: Seq[File], collector: PartialFunction[( File, String ), String] = { case ( _, r ) => r },
+ allowDuplicates: Boolean = false
+ ): Seq[( File, String )] = {
+ val map = files.sorted.flatMap { base =>
+ val b = base.getCanonicalFile.string
+ if ( base.isDirectory ) {
+ base.listRecursive.map { f =>
+ f -> f.getCanonicalFile.string.stripPrefix( b ).stripPrefix( File.separator )
+ }
+ } else {
+ Seq( base -> base.getName )
+ }
+ }.collect {
+ case v @ ( file, _ ) if collector.isDefinedAt( v ) => file -> collector( v )
+ }
+ if ( !allowDuplicates ) {
+ val relatives = map.unzip._2
+ val duplicateFiles = ( relatives diff relatives.distinct ).distinct
+ assert(
+ duplicateFiles.isEmpty, {
+ val rs = relatives.toSet
+ "Conflicting:\n\n" +
+ map.filter( rs contains _._2 ).groupBy( _._2 ).mapValues( _.map( _._1 ).sorted ).toSeq.sortBy( _._1 ).map {
+ case ( name, files ) => s"$name:\n" ++ files.mkString( "\n" )
+ }.mkString( "\n\n" )
+ }
+ )
+ }
+ map
+ }
+
+ /* recursively deletes folders*/
+ def deleteRecursive( file: File ): Unit = {
+ val s = file.string
+ // some desperate attempts to keep people from accidentally deleting their hard drive
+ assert( file === file.getCanonicalFile, "deleteRecursive requires previous .getCanonicalFile" )
+ assert( file.isAbsolute, "deleteRecursive requires absolute path" )
+ assert( file.string != "", "deleteRecursive requires non-empty file path" )
+ assert( s.split( File.separator.replace( "\\", "\\\\" ) ).size > 4, "deleteRecursive requires absolute path of at least depth 4" )
+ assert( !file.listRecursive.exists( _.isHidden ), "deleteRecursive requires no files to be hidden" )
+ assert( file.listRecursive.forall( _.canWrite ), "deleteRecursive requires all files to be writable" )
+ if ( file.isDirectory ) {
+ file.listOrFail.map( deleteRecursive )
+ }
+ file.delete
+ }
+}
diff --git a/libraries/interfaces/ExitCode.java b/libraries/interfaces/ExitCode.java
new file mode 100644
index 0000000..f29d8c0
--- /dev/null
+++ b/libraries/interfaces/ExitCode.java
@@ -0,0 +1,4 @@
+package cbt.interfaces;
+public interface ExitCode{
+ public int integer();
+}
diff --git a/libraries/proguard/Proguard.scala b/libraries/proguard/Proguard.scala
index 7d57a77..ec5c2e9 100644
--- a/libraries/proguard/Proguard.scala
+++ b/libraries/proguard/Proguard.scala
@@ -29,8 +29,8 @@ object ProGuard {
}
case class ProGuard[T](
main: Seq[String] => Int,
- T: Seq[File] => T,
- log: String => Unit = _ => ()
+ T: Seq[File] => T,
+ log: String => Unit = _ => ()
) {
/**
@@ -160,9 +160,9 @@ case class ProGuard[T](
private object argsFor {
def apply[T: argsFor]( value: T ) = implicitly[argsFor[T]].apply( value )
implicit object SeqFile extends argsFor[Seq[File]]( v => Some( Seq( v.map( _.getPath ).mkString( ":" ) ) ) )
- implicit object File extends argsFor[File]( v => Some( Seq( v.getPath ) ) )
- implicit object String extends argsFor[String]( v => Some( Seq( v ) ) )
- implicit object Int extends argsFor[Int]( i => Some( Seq( i.toString ) ) )
+ implicit object File extends argsFor[File]( v => Some( Seq( v.getPath ) ) )
+ implicit object String extends argsFor[String]( v => Some( Seq( v ) ) )
+ implicit object Int extends argsFor[Int]( i => Some( Seq( i.toString ) ) )
implicit object Boolean
extends argsFor[Boolean]( {
case false => None
diff --git a/libraries/proguard/build/build.scala b/libraries/proguard/build/build.scala
index 3ca38b5..754de20 100644
--- a/libraries/proguard/build/build.scala
+++ b/libraries/proguard/build/build.scala
@@ -1,11 +1,12 @@
package cbt_build.proguard
import cbt._
+import cbt_internal._
import java.nio.file.Files._
import java.net._
import java.io._
import scala.xml._
-class Build(val context: Context) extends Scalafmt{
+class Build(val context: Context) extends Library{
def description: String = "Type-safe scala wrapper to interfaces with ProGuard.main runner"
def inceptionYear = 2017
@@ -14,11 +15,6 @@ class Build(val context: Context) extends Scalafmt{
compile
}
- override def scalafmt = super.scalafmt.copy(
- config = Scalafmt.cbtRecommendedConfig,
- whiteSpaceInParenthesis = true
- )
-
override def compile = {
// currently suffers from non-deterministic formatting. Try a few times to reproduce commit state.
val formatted = scalafmt.apply.map(_.string).mkString("\n")
diff --git a/libraries/proguard/build/build/build.scala b/libraries/proguard/build/build/build.scala
index 7928cfa..5057404 100644
--- a/libraries/proguard/build/build/build.scala
+++ b/libraries/proguard/build/build/build.scala
@@ -1,13 +1,11 @@
-package cbt_build.proguard.build
+package proguard_build.build
import cbt._
-class Build(val context: Context) extends BuildBuild{
+class Build(val context: Context) extends BuildBuild with CbtInternal{
override def dependencies = (
super.dependencies ++ // don't forget super.dependencies here for scala-library, etc.
Resolver( mavenCentral, sonatypeReleases ).bind(
ScalaDependency("org.scala-lang.modules","scala-xml","1.0.5"),
"org.ccil.cowan.tagsoup" % "tagsoup" % "1.2.1"
- ) ++ Seq(
- plugins.scalafmt
- )
+ ) ++ Seq( cbtInternal.library )
)
}
diff --git a/libraries/reflect/StaticMethod.scala b/libraries/reflect/StaticMethod.scala
new file mode 100644
index 0000000..e2a0d07
--- /dev/null
+++ b/libraries/reflect/StaticMethod.scala
@@ -0,0 +1,4 @@
+package cbt.reflect
+case class StaticMethod[Arg, Result]( function: Arg => Result, name: String ) extends ( Arg => Result ) {
+ def apply( arg: Arg ): Result = function( arg )
+}
diff --git a/libraries/reflect/build/build.scala b/libraries/reflect/build/build.scala
new file mode 100644
index 0000000..5c27090
--- /dev/null
+++ b/libraries/reflect/build/build.scala
@@ -0,0 +1,8 @@
+package cbt_build.reflect
+import cbt._
+import cbt_internal._
+class Build(val context: Context) extends Library{
+ override def inceptionYear = 2017
+ override def description = "discover classes on your classpath and invoke methods reflectively, preventing System.exit"
+ override def dependencies = super.dependencies :+ libraries.file
+}
diff --git a/libraries/reflect/build/build/build.scala b/libraries/reflect/build/build/build.scala
new file mode 100644
index 0000000..d3f98ce
--- /dev/null
+++ b/libraries/reflect/build/build/build.scala
@@ -0,0 +1,5 @@
+package cbt_build.reflect.build
+import cbt._
+class Build(val context: Context) extends BuildBuild with CbtInternal{
+ override def dependencies = super.dependencies :+ cbtInternal.library
+}
diff --git a/libraries/reflect/reflect.scala b/libraries/reflect/reflect.scala
new file mode 100644
index 0000000..c18d926
--- /dev/null
+++ b/libraries/reflect/reflect.scala
@@ -0,0 +1,177 @@
+package cbt.reflect
+
+import java.io.File
+import java.lang.reflect.{ Constructor, Method, InvocationTargetException, Modifier }
+
+import scala.reflect.ClassTag
+
+import cbt.ExitCode
+import cbt.file._
+
+object `package` extends Module {
+ implicit class CbtClassOps( val c: Class[_] ) extends AnyVal with ops.CbtClassOps
+ implicit class CbtConstructorOps( val c: Constructor[_] ) extends AnyVal with ops.CbtConstructorOps
+ implicit class CbtMethodOps( val m: Method ) extends AnyVal with ops.CbtMethodOps
+}
+
+package ops {
+ trait CbtClassOps extends Any {
+ def c: Class[_]
+ def name = c.getName
+ def method( name: String, parameterTypes: Class[_]* ) = c.getMethod( name, parameterTypes: _* )
+ def methods = c.getMethods
+ def modifiers = c.getModifiers
+ def constructors = c.getConstructors
+ def declaredMethods = c.getDeclaredMethods
+ def isInterface = Modifier.isInterface( c.getModifiers )
+ def isAbstract = Modifier.isAbstract( c.getModifiers )
+ def isPrivate = Modifier.isPrivate( c.getModifiers )
+ def isProtected = Modifier.isProtected( c.getModifiers )
+ def isPublic = Modifier.isPublic( c.getModifiers )
+ def isFinal = Modifier.isFinal( c.getModifiers )
+ }
+ trait CbtConstructorOps extends Any {
+ def c: Constructor[_]
+ def parameterTypes = c.getParameterTypes
+ }
+ trait CbtMethodOps extends Any {
+ def m: Method
+ def name = m.getName
+ def declaringClass = m.getDeclaringClass
+ def modifiers = m.getModifiers
+ def parameters = m.getParameters
+ def parameterTypes = m.getParameterTypes
+ def returnType = m.getReturnType
+
+ def isAbstract = Modifier.isAbstract( m.getModifiers )
+ def isFinal = Modifier.isFinal( m.getModifiers )
+ def isNative = Modifier.isNative( m.getModifiers )
+ def isPrivate = Modifier.isPrivate( m.getModifiers )
+ def isProtected = Modifier.isProtected( m.getModifiers )
+ def isPublic = Modifier.isPublic( m.getModifiers )
+ def isStatic = Modifier.isStatic( m.getModifiers )
+ def isStrict = Modifier.isStrict( m.getModifiers )
+ def isSynchronized = Modifier.isSynchronized( m.getModifiers )
+ def isTransient = Modifier.isTransient( m.getModifiers )
+ def isVolatile = Modifier.isVolatile( m.getModifiers )
+ }
+}
+trait Module {
+ def runMain( cls: Class[_], args: Seq[String] ): ExitCode =
+ discoverStaticExitMethodForced[Array[String]]( cls, "main" ).apply( args.to )
+
+ def discoverMain( cls: Class[_] ): Option[StaticMethod[Seq[String], ExitCode]] = {
+ discoverStaticExitMethod[Array[String]]( cls, "main" )
+ .map( f =>
+ f.copy(
+ function = ( arg: Seq[String] ) => f.function( arg.to )
+ ) )
+ }
+
+ /** ignoreMissingClasses allows ignoring other classes root directories which are subdirectories of this one */
+ def iterateClasses(
+ classesRootDirectory: File,
+ classLoader: ClassLoader,
+ ignoreMissingClasses: Boolean
+ ): Seq[Class[_]] =
+ iterateClassNames( classesRootDirectory )
+ .map { name =>
+ try {
+ classLoader.loadClass( name )
+ } catch {
+ case e: ClassNotFoundException if ignoreMissingClasses => null
+ case e: NoClassDefFoundError if ignoreMissingClasses => null
+ }
+ }
+ .filterNot( ignoreMissingClasses && _ == null )
+
+ /** Given a directory corresponding to the root package, iterate
+ * the names of all classes derived from the class files found
+ */
+ def iterateClassNames( classesRootDirectory: File ): Seq[String] =
+ classesRootDirectory.listRecursive
+ .filter( _.isFile )
+ .map( _.getPath )
+ .collect {
+ // no $ to avoid inner classes
+ case path if !path.contains( "$" ) && path.endsWith( ".class" ) =>
+ path
+ .stripSuffix( ".class" )
+ .stripPrefix( classesRootDirectory.getPath )
+ .stripPrefix( File.separator ) // 1 for the slash
+ .replace( File.separator, "." )
+ }
+
+ def discoverStaticExitMethodForced[Arg: ClassTag](
+ cls: Class[_], name: String
+ ): StaticMethod[Arg, ExitCode] = {
+ val f = discoverStaticMethodForced[Arg, Unit]( cls, name )
+ f.copy(
+ function = arg => trapExitCode { f.function( arg ); ExitCode.Success }
+ )
+ }
+
+ def discoverStaticMethodForced[Arg, Result](
+ cls: Class[_], name: String
+ )(
+ implicit
+ Result: ClassTag[Result], Arg: ClassTag[Arg]
+ ): StaticMethod[Arg, Result] = {
+ val m = cls.method( name, Arg.runtimeClass )
+ assert( Result.runtimeClass.isAssignableFrom( m.returnType ) )
+ typeStaticMethod( m )
+ }
+
+ def discoverStaticExitMethod[Arg: ClassTag](
+ cls: Class[_], name: String
+ ): Option[StaticMethod[Arg, ExitCode]] =
+ discoverStaticMethod[Arg, Unit]( cls, name ).map( f =>
+ f.copy(
+ function = arg => trapExitCode { f.function( arg ); ExitCode.Success }
+ ) )
+
+ def discoverStaticMethod[Arg, Result](
+ cls: Class[_], name: String
+ )(
+ implicit
+ Result: ClassTag[Result], Arg: ClassTag[Arg]
+ ): Option[StaticMethod[Arg, Result]] = {
+ Some( cls )
+ .filterNot( _.isAbstract )
+ .filterNot( _.isInterface )
+ .flatMap( _
+ .getMethods
+ .find( m =>
+ !m.isAbstract
+ && m.isPublic
+ && m.name == name
+ && m.parameterTypes.toList == List( Arg.runtimeClass )
+ && Result.runtimeClass.isAssignableFrom( m.returnType ) ) )
+ .map( typeStaticMethod )
+ }
+
+ def typeStaticMethod[Arg, Result]( method: Method ): StaticMethod[Arg, Result] = {
+ val m = method
+ val instance =
+ if ( m.isStatic ) null
+ else m.declaringClass.newInstance // Dottydoc needs this. It's main method is not static.
+ StaticMethod(
+ arg => m.invoke( instance, arg.asInstanceOf[AnyRef] ).asInstanceOf[Result],
+ m.getClass.name.stripSuffix( "$" ) ++ "." ++ m.name ++ "( "
+ ++ m.parameters.map( _.getType.name ).mkString( ", " )
+ ++ " )"
+ )
+ }
+
+ def trapExitCodeOrValue[T]( result: => T, i: Int = 5 ): Either[ExitCode, T] = {
+ TrapSystemExit.run(
+ new TrapSystemExit[Either[ExitCode, T]] {
+ def run = Right( result )
+ def wrap( exitCode: Int ) = Left( new ExitCode( exitCode ) )
+ }
+ )
+ }
+
+ def trapExitCode( code: => ExitCode ): ExitCode =
+ trapExitCodeOrValue( code ).merge
+}
diff --git a/nailgun_launcher/NailgunLauncher.java b/nailgun_launcher/NailgunLauncher.java
index fe9f27f..1c6f3b5 100644
--- a/nailgun_launcher/NailgunLauncher.java
+++ b/nailgun_launcher/NailgunLauncher.java
@@ -5,6 +5,7 @@ import java.net.*;
import java.security.*;
import java.util.*;
import static cbt.Stage0Lib.*;
+import cbt.reflect.TrapSystemExit;
import static java.io.File.pathSeparator;
import java.nio.file.*;
import static java.nio.file.Files.write;
@@ -66,7 +67,7 @@ public class NailgunLauncher{
return;
}
- System.setSecurityManager( new TrapSecurityManager() );
+ System.setSecurityManager( TrapSystemExit.createSecurityManager(initialSecurityManager) );
installProxySettings();
String[] diff = args[0].split("\\.");
long start = _start - (Long.parseLong(diff[0]) * 1000L) - Long.parseLong(diff[1]);
@@ -146,7 +147,7 @@ public class NailgunLauncher{
String nailgunTarget = nailgunSources + TARGET;
String stage1Sources = cbtHome + "/" + STAGE1;
String stage1Target = stage1Sources + TARGET;
- File compatibilitySources = new File(cbtHome + "/compatibility");
+ String compatibilitySources = cbtHome + "/compatibility";
String mavenCache = cache + "maven";
String mavenUrl = "https://repo1.maven.org/maven2";
File loopFile = new File(cwd + "/target/.cbt-loop.tmp");
@@ -171,9 +172,14 @@ public class NailgunLauncher{
compatibilityLastModified = new File( compatibilityTarget + "../classes.last-success" ).lastModified();
} else {
compatibilitySourceFiles = new ArrayList<File>();
- for( File f: compatibilitySources.listFiles() ){
- if( f.isFile() && f.toString().endsWith(".java") ){
- compatibilitySourceFiles.add(f);
+ for( String d: new String[]{
+ compatibilitySources,
+ cbtHome + "/libraries/interfaces"
+ } ){
+ for( File f: new File(d).listFiles() ){
+ if( f.isFile() && f.toString().endsWith(".java") ){
+ compatibilitySourceFiles.add(f);
+ }
}
}
@@ -206,9 +212,16 @@ public class NailgunLauncher{
String stage1Classpath = classpath( stage1ClasspathArray );
stage1SourceFiles = new ArrayList<File>();
- for( File f: new File(stage1Sources).listFiles() ){
- if( f.isFile() && f.toString().endsWith(".scala") ){
- stage1SourceFiles.add(f);
+ for( String d: new String[]{
+ stage1Sources,
+ cbtHome + "/libraries/reflect",
+ cbtHome + "/libraries/common-1",
+ cbtHome + "/libraries/file"
+ } ){
+ for( File f: new File(d).listFiles() ){
+ if( f.isFile() && (f.toString().endsWith(".scala") || f.toString().endsWith(".java")) ){
+ stage1SourceFiles.add(f);
+ }
}
}
diff --git a/nailgun_launcher/Stage0Lib.java b/nailgun_launcher/Stage0Lib.java
index 8ab6150..34af7b0 100644
--- a/nailgun_launcher/Stage0Lib.java
+++ b/nailgun_launcher/Stage0Lib.java
@@ -12,6 +12,7 @@ import static cbt.NailgunLauncher.*;
import java.nio.file.*;
import java.nio.file.attribute.FileTime;
import static java.lang.Math.min;
+import cbt.reflect.TrapSystemExit;
public class Stage0Lib{
public static void _assert(boolean condition, Object msg){
@@ -21,22 +22,21 @@ public class Stage0Lib{
}
public static int runMain(String cls, String[] args, ClassLoader cl) throws Throwable{
- boolean trapExitCodeBefore = TrapSecurityManager.trapExitCode().get();
- try{
- TrapSecurityManager.trapExitCode().set(true);
- cl.loadClass(cls)
- .getMethod("main", String[].class)
- .invoke( null, (Object) args);
- return 0;
- }catch( InvocationTargetException exception ){
- Throwable cause = exception.getCause();
- if(TrapSecurityManager.isTrappedExit(cause)){
- return TrapSecurityManager.exitCode(cause);
+ return TrapSystemExit.run(
+ new TrapSystemExit<Integer>(){
+ @Override
+ public Integer run() throws Throwable{
+ cl.loadClass(cls)
+ .getMethod("main", String[].class)
+ .invoke( null, (Object) args);
+ return 0;
+ }
+ @Override
+ public Integer wrap(int exitCode){
+ return exitCode;
+ }
}
- throw exception;
- } finally {
- TrapSecurityManager.trapExitCode().set(trapExitCodeBefore);
- }
+ );
}
public static Object get(Object object, String method) throws Throwable{
@@ -111,7 +111,8 @@ public class Stage0Lib{
"-d", target,
"-S-deprecation",
"-S-feature",
- "-S-unchecked"
+ "-S-unchecked",
+ "-S-language:existentials"
}
)
);
diff --git a/plugins/proguard/Proguard.scala b/plugins/proguard/Proguard.scala
index 486d969..f229e66 100644
--- a/plugins/proguard/Proguard.scala
+++ b/plugins/proguard/Proguard.scala
@@ -24,7 +24,7 @@ object ProGuard {
context.logger, transientCache, context.classLoaderCache
).bindOne(
MavenDependency(groupId, artifactId, version)
- ).runMain(cbt.proguard.ProGuard.mainClass, args: _*).integer,
+ ).runMain(cbt.proguard.ProGuard.mainClass, args).integer,
ClassPath(_),
context.logger.log("proguard",_)
)
diff --git a/plugins/uber-jar/src/UberJar.scala b/plugins/uber-jar/src/UberJar.scala
index f330f83..856b69a 100644
--- a/plugins/uber-jar/src/UberJar.scala
+++ b/plugins/uber-jar/src/UberJar.scala
@@ -41,7 +41,7 @@ class UberJarLib(lib: cbt.Lib, log: String => Unit) {
val uberJar = lib.createJar(jarFile, dirs :+ extracted, mainClass=mainClass)
log("Writing uber jar - DONE")
- extracted.deleteRecursive
+ lib.deleteRecursive( extracted )
System.err.println(lib.green("Creating uber jar - DONE"))
diff --git a/stage1/Stage1Lib.scala b/stage1/Stage1Lib.scala
index 565a06c..8aaa6e6 100644
--- a/stage1/Stage1Lib.scala
+++ b/stage1/Stage1Lib.scala
@@ -11,31 +11,11 @@ import java.security._
import java.util.{Set=>_,Map=>_,List=>_,_}
import javax.xml.bind.annotation.adapters.HexBinaryAdapter
-// CLI interop
-case class ExitCode(integer: Int){
- def ||( other: => ExitCode ) = if( this == ExitCode.Success ) this else other
- def &&( other: => ExitCode ) = if( this != ExitCode.Success ) this else other
-}
-object ExitCode{
- val Success = ExitCode(0)
- val Failure = ExitCode(1)
-}
-
-object CatchTrappedExitCode{
- def unapply(e: Throwable): Option[ExitCode] = {
- Option(e) flatMap {
- case i: InvocationTargetException => unapply(i.getTargetException)
- case e if TrapSecurityManager.isTrappedExit(e) => Some( ExitCode(TrapSecurityManager.exitCode(e)) )
- case _ => None
- }
- }
-}
-
-class BaseLib{
- def realpath(name: File) = new File(java.nio.file.Paths.get(name.getAbsolutePath).normalize.toString)
-}
-
-class Stage1Lib( logger: Logger ) extends BaseLib{
+class Stage1Lib( logger: Logger ) extends
+ _root_.cbt.common_1.Module with
+ _root_.cbt.reflect.Module with
+ _root_.cbt.file.Module
+{
lib =>
implicit protected val implicitLogger: Logger = logger
@@ -99,24 +79,28 @@ class Stage1Lib( logger: Logger ) extends BaseLib{
}
}
+ /*
// ========== compilation / execution ==========
// TODO: move classLoader first
- def runMain( cls: String, args: Seq[String], classLoader: ClassLoader, fakeInstance: Boolean = false ): ExitCode = {
+ def runMain( className: String, args: Seq[String], classLoader: ClassLoader ): ExitCode = {
import java.lang.reflect.Modifier
- logger.run(s"Running $cls.main($args) with classLoader: " ++ classLoader.toString)
+ logger.run(s"Running $className.main($args) with classLoader: " ++ classLoader.toString)
trapExitCode{
- val c = classLoader.loadClass(cls)
- val m = c.getMethod( "main", classOf[Array[String]] )
- val instance =
- if(!fakeInstance) null else c.newInstance
- assert(
- fakeInstance || (m.getModifiers & java.lang.reflect.Modifier.STATIC) > 0,
- "Cannot run non-static method " ++ cls+".main"
- )
- m.invoke( instance, args.toArray.asInstanceOf[AnyRef] )
+ /*
+ val cls = classLoader.loadClass(className)
+ discoverCbtMain( cls ) orElse discoverMain( cls ) getOrElse (
+ throw new NoSuchMethodException( "No main method found in " ++ cbt )
+ ).apply( arg.toVector )*/
ExitCode.Success
}
}
+ */
+
+ def discoverCbtMainForced( cls: Class[_] ): cbt.reflect.StaticMethod[Context, ExitCode] =
+ discoverStaticMethodForced[Context, ExitCode]( cls, "cbtMain" )
+
+ def discoverCbtMain( cls: Class[_] ): Option[cbt.reflect.StaticMethod[Context, ExitCode]] =
+ discoverStaticMethod[Context, ExitCode]( cls, "cbtMain" )
/** shows an interactive dialogue in the shell asking the user to pick one of many choices */
def pickOne[T]( msg: String, choices: Seq[T] )( show: T => String ): Option[T] = {
@@ -149,51 +133,10 @@ class Stage1Lib( logger: Logger ) extends BaseLib{
}
/** interactively pick one main class */
- def runClass( mainClasses: Seq[Class[_]] ): Option[Class[_]] = {
+ def pickClass( mainClasses: Seq[Class[_]] ): Option[Class[_]] = {
pickOne( "Which one do you want to run?", mainClasses )( _.toString )
}
- /** Given a directory corresponding to the root package, iterate
- the names of all classes derived from the class files found */
- def iterateClassNames( classesRootDirectory: File ): Seq[String] =
- classesRootDirectory
- .listRecursive
- .filter(_.isFile)
- .map(_.getPath)
- .collect{
- // no $ to avoid inner classes
- case path if !path.contains("$") && path.endsWith(".class") =>
- path.stripSuffix(".class")
- .stripPrefix(classesRootDirectory.getPath)
- .stripPrefix(File.separator) // 1 for the slash
- .replace(File.separator, ".")
- }
-
- /** ignoreMissingClasses allows ignoring other classes root directories which are subdirectories of this one */
- def iterateClasses( classesRootDirectory: File, classLoader: ClassLoader, ignoreMissingClasses: Boolean ) =
- iterateClassNames(classesRootDirectory).map{ name =>
- try{
- classLoader.loadClass(name)
- } catch {
- case e: ClassNotFoundException if ignoreMissingClasses => null
- case e: NoClassDefFoundError if ignoreMissingClasses => null
- }
- }.filterNot(ignoreMissingClasses && _ == null)
-
- def mainClasses( classesRootDirectory: File, classLoader: ClassLoader ): Seq[Class[_]] = {
- val arrayClass = classOf[Array[String]]
- val unitClass = classOf[Unit]
-
- iterateClasses( classesRootDirectory, classLoader, true ).filter( c =>
- !c.isInterface &&
- c.getDeclaredMethods().exists( m =>
- m.getName == "main"
- && m.getParameterTypes.toList == List(arrayClass)
- && m.getReturnType == unitClass
- )
- )
- }
-
implicit class ClassLoaderExtensions(classLoader: ClassLoader){
def canLoad(className: String) = {
try{
@@ -322,8 +265,9 @@ ${sourceFiles.sorted.mkString(" \\\n")}
}
}
}
- def redirectOutToErr[T](code: => T): T = {
- val ( out, err ) = try{
+
+ def getOutErr: (ThreadLocal[PrintStream], ThreadLocal[PrintStream]) =
+ try{
// trying nailgun's System.our/err wrapper
val field = System.out.getClass.getDeclaredField("streams")
assert(System.out.getClass.getName == "com.martiansoftware.nailgun.ThreadLocalPrintStream")
@@ -339,8 +283,8 @@ ${sourceFiles.sorted.mkString(" \\\n")}
field.setAccessible(true)
val outStream = field.get(System.out)
val errStream = field.get(System.err)
- assert(outStream.getClass.getName == "cbt.ThreadLocalOutputStream")
- assert(errStream.getClass.getName == "cbt.ThreadLocalOutputStream")
+ assert(outStream.getClass.getName == "cbt.ThreadLocalOutputStream", outStream.getClass.getName)
+ assert(errStream.getClass.getName == "cbt.ThreadLocalOutputStream", errStream.getClass.getName)
val field2 = outStream.getClass.getDeclaredField("threadLocal")
field2.setAccessible(true)
val out = field2.get(outStream).asInstanceOf[ThreadLocal[PrintStream]]
@@ -348,6 +292,8 @@ ${sourceFiles.sorted.mkString(" \\\n")}
( out, err )
}
+ def redirectOutToErr[T](code: => T): T = {
+ val ( out, err ) = getOutErr
val oldOut: PrintStream = out.get
out.set( err.get: PrintStream )
val res = code
@@ -355,21 +301,6 @@ ${sourceFiles.sorted.mkString(" \\\n")}
res
}
- def trapExitCodeOrValue[T]( result: => T ): Either[ExitCode,T] = {
- val trapExitCodeBefore = TrapSecurityManager.trapExitCode().get
- try{
- TrapSecurityManager.trapExitCode().set(true)
- Right( result )
- } catch {
- case CatchTrappedExitCode(exitCode) =>
- logger.stage1(s"caught exit code $exitCode")
- Left( exitCode )
- } finally {
- TrapSecurityManager.trapExitCode().set(trapExitCodeBefore)
- }
- }
-
- def trapExitCode( code: => ExitCode ): ExitCode = trapExitCodeOrValue(code).merge
def ScalaDependency(
groupId: String, artifactId: String, version: String, classifier: Classifier = Classifier.none,
diff --git a/stage1/cbt.scala b/stage1/cbt.scala
index 8cba9df..64257c2 100644
--- a/stage1/cbt.scala
+++ b/stage1/cbt.scala
@@ -3,13 +3,19 @@ import java.io._
import java.nio.file._
import java.nio.file.Files._
import java.net._
+import java.lang.reflect._
object `package`{
- implicit class TypeInferenceSafeEquals[T](value: T){
- /** if you don't manually upcast, this will catch comparing different types */
- def ===(other: T) = value == other
- def =!=(other: T) = value != other // =!= instead of !==, because it has better precedence
- }
+ implicit class CbtExitCodeOps( val exitCode: ExitCode ) extends AnyVal with common_1.ops.CbtExitCodeOps
+ implicit class TypeInferenceSafeEquals[T]( val value: T ) extends AnyVal with common_1.ops.TypeInferenceSafeEquals[T]
+ implicit class CbtBooleanOps( val condition: Boolean ) extends AnyVal with common_1.ops.CbtBooleanOps
+ implicit class CbtStringOps( val string: String ) extends AnyVal with common_1.ops.CbtStringOps
+
+ implicit class CbtFileOps( val file: File ) extends file.ops.CbtFileOps
+
+ implicit class CbtClassOps( val c: Class[_] ) extends AnyVal with reflect.ops.CbtClassOps
+ implicit class CbtConstructorOps( val c: Constructor[_] ) extends AnyVal with reflect.ops.CbtConstructorOps
+ implicit class CbtMethodOps( val m: Method ) extends AnyVal with reflect.ops.CbtMethodOps
val mavenCentral = new URL("https://repo1.maven.org/maven2")
val jcenter = new URL("https://jcenter.bintray.com")
@@ -18,15 +24,6 @@ object `package`{
val sonatypeReleases = sonatypeBase ++ "releases"
val sonatypeSnapshots = sonatypeBase ++ "snapshots"
- private val lib = new BaseLib
-
- implicit class CbtBooleanExtensions(condition: Boolean){
- def option[T](value: =>T): Option[T] = if(condition) Some(value) else None
- }
- implicit class CbtStringExtensions(string: String){
- def escape = string.replace("\\","\\\\").replace("\"","\\\"")
- def quote = s""""$escape""""
- }
implicit class PathExtensionMethods( path: Path ){
def /(s: String): Path = path.resolve(s)
def ++( s: String ): Path = {
@@ -36,44 +33,7 @@ object `package`{
Paths.get( path.toString ++ s )
}
}
- implicit class FileExtensionMethods( file: File ){
- def ++( s: String ): File = {
- if(s endsWith "/") throw new Exception(
- """Trying to append a String that ends in "/" to a File would loose the trailing "/". Use .stripSuffix("/") if you need to."""
- )
- new File( file.toString ++ s )
- }
- def /(s: String): File = new File( file, s )
- def parent = lib.realpath(file ++ "/..")
- def string = file.toString
- /* recursively deletes folders*/
- def deleteRecursive: Unit = {
- val s = file.string
- // some desperate attempts to keep people from accidentally deleting their hard drive
- assert( file == file.getCanonicalFile, "deleteRecursive requires previous .getCanonicalFile" )
- assert( file.isAbsolute, "deleteRecursive requires absolute path" )
- assert( file.string != "", "deleteRecursive requires non-empty file path" )
- assert( s.split(File.separator.replace("\\","\\\\")).size > 4, "deleteRecursive requires absolute path of at least depth 4" )
- assert( !listRecursive.exists(_.isHidden), "deleteRecursive requires no files to be hidden" )
- assert( listRecursive.forall(_.canWrite), "deleteRecursive requires all files to be writable" )
- if( file.isDirectory ){
- file.listFiles.map(_.deleteRecursive)
- }
- file.delete
- }
-
- def listOrFail: Seq[File] = Option( file.listFiles ).getOrElse( throw new Exception( "no such file: " + file ) ).toVector
- def listRecursive: Seq[File] = {
- file +: (
- if( file.isDirectory ) file.listFiles.flatMap(_.listRecursive).toVector else Seq[File]()
- )
- }
- def lastModifiedRecursive = listRecursive.map(_.lastModified).max
-
- def readAsString = new String( readAllBytes( file.toPath ) )
- def quote = s"new _root_.java.io.File(${string.quote})"
- }
implicit class URLExtensionMethods( url: URL ){
def ++( s: String ): URL = new URL( url.toString ++ s )
def show = "/[^/@]+@".r.replaceFirstIn( url.toString, "/" ) // remove credentials when showing url for security reasons
diff --git a/stage1/resolver.scala b/stage1/resolver.scala
index e3500b3..0f0acaa 100644
--- a/stage1/resolver.scala
+++ b/stage1/resolver.scala
@@ -77,26 +77,25 @@ trait DependencyImplementation extends Dependency{
)
}
*/
-
- def runMain( className: String, args: Seq[String] ) = lib.runMain( className, args, classLoader )
-
def flatClassLoader: Boolean = false
- def mainClasses: Seq[Class[_]] = exportedClasspath.files.flatMap( lib.mainClasses( _, classLoader ) )
-
- def runClass: Option[String] = lib.runClass( mainClasses ).map( _.getName )
+ def runMain( className: String, args: Seq[String] ): ExitCode = lib.trapExitCode{
+ lib.runMain( classLoader.loadClass( className ), args )
+ }
- def run( args: String* ): ExitCode = {
- runClass.map( runMain( _, args ) ).getOrElse{
- // FIXME: this just doing nothing when class is not found has been repeatedly
- // surprising. Let's try to make this more visible than just logging an error.
- // Currently blocked on task `recursive` trying every subbuild and would error
- // for all that don't have a run class. Maybe that's ok actually.
- logger.task( "No main class found for " ++ show )
- ExitCode.Success
- }
+ def runMain( args: Seq[String] ): ExitCode = lib.trapExitCode{
+ mainMethod.getOrElse(
+ throw new RuntimeException( "No main class found in " + this )
+ )( args )
}
+ def mainMethod = lib.pickOne( "Which one do you want to run?", mainMethods )( _.name )
+
+ def classes = exportedClasspath.files.flatMap(
+ lib.iterateClasses( _, classLoader, false )
+ )
+ def mainMethods = classes.flatMap( lib.discoverMain )
+
def classLoader: ClassLoader = {
if( flatClassLoader ){
new java.net.URLClassLoader(classpath.strings.map(f => new URL("file://" ++ f)).toArray)
diff --git a/stage2/BasicBuild.scala b/stage2/BasicBuild.scala
index 4158040..ebb6a40 100644
--- a/stage2/BasicBuild.scala
+++ b/stage2/BasicBuild.scala
@@ -13,12 +13,7 @@ trait BaseBuild extends BuildInterface with DependencyImplementation with SbtDep
def moduleKey: String = "BaseBuild("+target.string+")"
implicit def transientCache: java.util.Map[AnyRef,AnyRef] = context.transientCache
- object libraries{
- private def dep(name: String) = DirectoryDependency( context.cbtHome / "libraries" / name )
- def captureArgs = dep( "capture_args" )
- def eval = dep( "eval" )
- def proguard = dep( "proguard" )
- }
+ implicit def libraries(implicit context: Context): libraries = new libraries(context)
// library available to builds
implicit protected final val logger: Logger = context.logger
@@ -211,7 +206,8 @@ trait BaseBuild extends BuildInterface with DependencyImplementation with SbtDep
)
}
- def run: ExitCode = run( context.args: _* )
+ def run: ExitCode = runMain( context.args )
+
def test: Dependency = {
val testDirectory = projectDirectory / "test"
if( (testDirectory / lib.buildDirectoryName / lib.buildFileName).exists ){
@@ -225,6 +221,7 @@ trait BaseBuild extends BuildInterface with DependencyImplementation with SbtDep
}
}
}
+
def t: Any = lib.callReflective( test, Some("run"), context )
def rt = recursiveUnsafe(Some("test.run"))
diff --git a/stage2/BuildBuild.scala b/stage2/BuildBuild.scala
index 17ccb36..cc69905 100644
--- a/stage2/BuildBuild.scala
+++ b/stage2/BuildBuild.scala
@@ -3,22 +3,6 @@ import java.nio.file._
import java.io.File
class ConcreteBuildBuild(val context: Context) extends BuildBuild
-class plugins(implicit context: Context){
- // TODO: move this out of the OO
- private def plugin(dir: String) = cbt.DirectoryDependency(context.cbtHome / "plugins" / dir)
- final lazy val googleJavaFormat = plugin( "google-java-format" )
- final lazy val proguard = plugin( "proguard" )
- final lazy val sbtLayout = plugin( "sbt_layout" )
- final lazy val scalafmt = plugin( "scalafmt" )
- final lazy val scalaJs = plugin( "scalajs" )
- final lazy val scalapb = plugin( "scalapb" )
- final lazy val scalariform = plugin( "scalariform" )
- final lazy val scalaTest = plugin( "scalatest" )
- final lazy val sonatypeRelease = plugin( "sonatype-release" )
- final lazy val uberJar = plugin( "uber-jar" )
- final lazy val wartremover = plugin( "wartremover" )
- final lazy val scalafix = plugin( "scalafix" )
-}
trait BuildBuild extends BaseBuild{
override def dependencies = super.dependencies :+ context.cbtDependency
@@ -31,7 +15,7 @@ trait BuildBuild extends BaseBuild{
)
}
-trait CbtInternal extends BuildBuild{
+trait CbtInternal extends BaseBuild{
protected object cbtInternal{
def shared = DirectoryDependency(context.cbtHome / "/internal/plugins/shared")
def library = DirectoryDependency(context.cbtHome / "/internal/plugins/library")
diff --git a/stage2/DirectoryDependency.scala b/stage2/DirectoryDependency.scala
index cfc0bfd..9b07702 100644
--- a/stage2/DirectoryDependency.scala
+++ b/stage2/DirectoryDependency.scala
@@ -96,7 +96,7 @@ object DirectoryDependency {
)
} else {
val buildClass = buildClasses.head
- buildClass.getConstructors.find( _.getParameterTypes.toList === List( classOf[Context] ) ).map {
+ buildClass.constructors.find( _.parameterTypes.toList === List( classOf[Context] ) ).map {
_.newInstance( managedContext ).asInstanceOf[AnyRef]
}.getOrElse {
throw new Exception(
diff --git a/stage2/Lib.scala b/stage2/Lib.scala
index fd3346e..6488c1a 100644
--- a/stage2/Lib.scala
+++ b/stage2/Lib.scala
@@ -64,10 +64,8 @@ final class Lib(val logger: Logger) extends Stage1Lib(logger){
c =>
c
.getMethods
- .filter{ m =>
- java.lang.reflect.Modifier.isPublic(m.getModifiers)
- }
- .filter( _.getParameterTypes.length == 0 )
+ .filter( _.isPublic )
+ .filter( _.parameterTypes.length == 0 )
.map(m => NameTransformer.decode(m.getName) -> m)
.filterNot(_._1 contains "$")
).toMap
@@ -262,34 +260,6 @@ final class Lib(val logger: Logger) extends Stage1Lib(logger){
m
}
- def autoRelative( files: Seq[File], collector: PartialFunction[(File,String), String] = { case (_,r) => r }): Seq[(File, String)] = {
- val map = files.sorted.flatMap{ base =>
- val b = base.getCanonicalFile.string
- if( base.isDirectory ){
- base.listRecursive.map{ f =>
- f -> f.getCanonicalFile.string.stripPrefix(b).stripPrefix(File.separator)
- }
- } else {
- Seq( base -> base.getName )
- }
- }.collect{
- case v@(file, _) if collector.isDefinedAt(v) => file -> collector(v)
- }
- val relatives = map.unzip._2
- val duplicateFiles = (relatives diff relatives.distinct).distinct
- assert(
- duplicateFiles.isEmpty,
- {
- val rs = relatives.toSet
- "Conflicting:\n\n" +
- map.filter(rs contains _._2).groupBy(_._2).mapValues(_.map(_._1).sorted).toSeq.sortBy(_._1).map{
- case (name, files) => s"$name:\n" ++ files.mkString("\n")
- }.mkString("\n\n")
- }
- )
- map
- }
-
def createJar( jarFile: File, files: Seq[File], mainClass: Option[String] = None ): Option[File] = {
deleteIfExists(jarFile.toPath)
if( files.isEmpty ){
@@ -474,29 +444,5 @@ final class Lib(val logger: Logger) extends Stage1Lib(logger){
) findOuterMostModuleDirectory(directory.getParentFile) else directory
}
- def transformFiles( files: Seq[File], transform: String => String ): Seq[File] = {
- transformFilesOrError( files, s => Right(transform(s)) )._1
- }
-
- def transformFilesOrError[T]( files: Seq[File], transform: String => Either[T,String] ): ( Seq[File], Seq[(File, T)] ) = {
- val results = files.map{ file =>
- val string = file.readAsString
- transform( string ).left.map(
- file -> _
- ).right.map(
- replaced =>
- if( string != replaced ) {
- val tmpFile = file ++ ".cbt-tmp"
- assert( !tmpFile.exists )
- write( tmpFile, replaced )
- move( tmpFile.toPath, file.toPath, StandardCopyOption.REPLACE_EXISTING )
- Some( file )
- } else None
- )
- }
-
- ( results.map(_.right.toOption).flatten.flatten, results.map(_.left.toOption).flatten )
- }
-
def clearScreen = System.err.println( (27.toChar +: "[2J").mkString )
}
diff --git a/stage2/libraries.scala b/stage2/libraries.scala
new file mode 100644
index 0000000..3d2951c
--- /dev/null
+++ b/stage2/libraries.scala
@@ -0,0 +1,12 @@
+package cbt
+class libraries( context: Context ) {
+ private def dep( name: String ) = DirectoryDependency( context.cbtHome / "libraries" / name )( context )
+ def captureArgs = dep( "capture_args" )
+ def eval = dep( "eval" )
+ def file = dep( "file" )
+ def proguard = dep( "proguard" )
+ def reflect = dep( "reflect" )
+ def common_0 = dep( "common-0" )
+ def common_1 = dep( "common-1" )
+ def interfaces = dep( "interfaces" )
+}
diff --git a/stage2/plugins.scala b/stage2/plugins.scala
new file mode 100644
index 0000000..eca28f0
--- /dev/null
+++ b/stage2/plugins.scala
@@ -0,0 +1,17 @@
+package cbt
+class plugins( implicit context: Context ) {
+ // TODO: move this out of the OO
+ private def plugin( dir: String ) = DirectoryDependency( context.cbtHome / "plugins" / dir )
+ final lazy val googleJavaFormat = plugin( "google-java-format" )
+ final lazy val proguard = plugin( "proguard" )
+ final lazy val sbtLayout = plugin( "sbt_layout" )
+ final lazy val scalafix = plugin( "scalafix" )
+ final lazy val scalafmt = plugin( "scalafmt" )
+ final lazy val scalaJs = plugin( "scalajs" )
+ final lazy val scalapb = plugin( "scalapb" )
+ final lazy val scalariform = plugin( "scalariform" )
+ final lazy val scalaTest = plugin( "scalatest" )
+ final lazy val sonatypeRelease = plugin( "sonatype-release" )
+ final lazy val uberJar = plugin( "uber-jar" )
+ final lazy val wartremover = plugin( "wartremover" )
+}
diff --git a/stage2/plugins/Dotty.scala b/stage2/plugins/Dotty.scala
index 0bbaf44..766e9d1 100644
--- a/stage2/plugins/Dotty.scala
+++ b/stage2/plugins/Dotty.scala
@@ -107,11 +107,9 @@ class DottyLib(
) ++ compileArgs ++ sourceFiles.map(_.toString)
logger.lib("creating docs for source files "+args.mkString(", "))
val exitCode = redirectOutToErr{
- runMain(
+ dottyCompiler.runMain(
"dotty.tools.dottydoc.DocDriver",
- args,
- dottyCompiler.classLoader,
- fakeInstance = true // this is a hack as Dottydoc's main method is not static
+ args
)
}
System.err.println("done")
diff --git a/test/build/build.scala b/test/build/build.scala
index 2777511..36261b5 100644
--- a/test/build/build.scala
+++ b/test/build/build.scala
@@ -1,3 +1,4 @@
+package cbt_build.cbt.test
import cbt._
class Build(val context: cbt.Context) extends BaseBuild{
override def dependencies = super.dependencies :+ context.cbtDependency