aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristopher Vogt <oss.nsp@cvogt.org>2017-06-15 22:34:42 -0400
committerChristopher Vogt <oss.nsp@cvogt.org>2017-06-15 22:43:18 -0400
commitb5194aab6f1f57aff6e4538acaf91245fdf15039 (patch)
tree4516f4c4686ceaf35619a68c0d9d1a6c359d1897
parentc65d21ae38bdfb646af991a5f3b1dfe8e41a5318 (diff)
downloadcbt-b5194aab6f1f57aff6e4538acaf91245fdf15039.tar.gz
cbt-b5194aab6f1f57aff6e4538acaf91245fdf15039.tar.bz2
cbt-b5194aab6f1f57aff6e4538acaf91245fdf15039.zip
add process library with extracted and new functions
-rw-r--r--build/build.scala2
-rw-r--r--libraries/process/build/build.scala10
-rw-r--r--libraries/process/build/build/build.scala5
-rw-r--r--libraries/process/process.scala175
-rw-r--r--libraries/process/test/test.scala7
-rw-r--r--stage1/Stage1.scala1
-rw-r--r--stage1/Stage1Lib.scala93
-rw-r--r--stage1/resolver.scala26
-rw-r--r--stage2/BasicBuild.scala28
-rw-r--r--stage2/Lib.scala5
-rw-r--r--stage2/libraries.scala1
11 files changed, 241 insertions, 112 deletions
diff --git a/build/build.scala b/build/build.scala
index 74bf6da..729e6ed 100644
--- a/build/build.scala
+++ b/build/build.scala
@@ -13,7 +13,7 @@ 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.cbt.reflect :+ libraries.cbt.eval
+ ) :+ libraries.cbt.reflect :+ libraries.cbt.eval :+ libraries.cbt.process
}
override def sources = Seq(
diff --git a/libraries/process/build/build.scala b/libraries/process/build/build.scala
new file mode 100644
index 0000000..da859b5
--- /dev/null
+++ b/libraries/process/build/build.scala
@@ -0,0 +1,10 @@
+package cbt_build.process
+import cbt._
+import cbt_internal._
+class Build(val context: Context) extends Library{
+ override def inceptionYear = 2017
+ override def description = "helpers for process calls"
+ override def dependencies = super.dependencies ++ Resolver(mavenCentral).bind(
+ MavenDependency( "net.java.dev.jna", "jna-platform", "4.4.0" )
+ ) :+ libraries.cbt.common_1
+}
diff --git a/libraries/process/build/build/build.scala b/libraries/process/build/build/build.scala
new file mode 100644
index 0000000..d3f98ce
--- /dev/null
+++ b/libraries/process/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/process/process.scala b/libraries/process/process.scala
new file mode 100644
index 0000000..982c9d0
--- /dev/null
+++ b/libraries/process/process.scala
@@ -0,0 +1,175 @@
+package cbt.process
+import cbt.ExitCode
+import java.io._
+
+object `package` extends Module
+
+trait Module {
+ def runMainForked(
+ className: String,
+ args: Seq[String],
+ classpath: String,
+ directory: Option[File],
+ outErrIn: Option[( OutputStream, OutputStream, InputStream )]
+ ): ( Int, () => ExitCode, () => ExitCode ) = {
+ // FIXME: Windows support
+ val java_exe = new File( System.getProperty( "java.home" ) + "/bin/java" )
+ runWithIO(
+ java_exe.toString +: "-cp" +: classpath +: className +: args,
+ directory,
+ outErrIn
+ )
+ }
+
+ def runWithIO(
+ commandLine: Seq[String],
+ directory: Option[File],
+ outErrIn: Option[( OutputStream, OutputStream, InputStream )]
+ ): ( Int, () => ExitCode, () => ExitCode ) = {
+ val pb = new ProcessBuilder( commandLine: _* )
+ outErrIn.map {
+ case ( out, err, in ) =>
+ val process = directory.map( pb.directory( _ ) ).getOrElse( pb )
+ .redirectInput( ProcessBuilder.Redirect.PIPE )
+ .redirectOutput( ProcessBuilder.Redirect.PIPE )
+ .redirectError( ProcessBuilder.Redirect.PIPE )
+ .start
+
+ (
+ processId( process ),
+ () => {
+ val lock = new AnyRef
+
+ val t1 = asyncPipeCharacterStreamSyncLines( process.getErrorStream, err, lock )
+ val t2 = asyncPipeCharacterStreamSyncLines( process.getInputStream, out, lock )
+ val t3 = asyncPipeCharacterStream( System.in, process.getOutputStream, process.isAlive )
+
+ t1.start
+ t2.start
+ t3.start
+
+ t1.join
+ t2.join
+
+ val e = process.waitFor
+ System.err.println( scala.Console.RESET + "Please press ENTER to continue..." )
+ t3.join
+ ExitCode( e )
+ },
+ () => {
+ process.destroy
+ Thread.sleep( 20 )
+ ExitCode( process.destroyForcibly.waitFor )
+ }
+ )
+ }.getOrElse {
+ val process = pb.inheritIO.start
+ (
+ processId( process ),
+ () => ExitCode( process.waitFor ),
+ () => {
+ process.destroy
+ Thread.sleep( 20 )
+ ExitCode( process.destroyForcibly.waitFor )
+ }
+ )
+ }
+ }
+
+ private def accessField( cls: Class[_], field: String ): java.lang.reflect.Field = {
+ val f = cls.getDeclaredField( field )
+ f.setAccessible( true )
+ f
+ }
+
+ import com.sun.jna.{ Library, Native }
+ private trait CLibrary extends Library {
+ def getpid: Int
+ }
+ private val CLibraryInstance: CLibrary = Native.loadLibrary( "c", classOf[CLibrary] ).asInstanceOf[CLibrary]
+
+ def currentProcessId: Int = {
+ if ( Option( System.getProperty( "os.name" ) ).exists( _.startsWith( "Windows" ) ) ) {
+ com.sun.jna.platform.win32.Kernel32.INSTANCE.GetCurrentProcessId
+ } else {
+ CLibraryInstance.getpid
+ }
+ }
+
+ /** process id of given Process */
+ def processId( process: Process ): Int = {
+ val clsName = process.getClass.getName
+ if ( clsName == "java.lang.UNIXProcess" ) {
+ accessField( process.getClass, "pid" ).getInt( process )
+ } else if ( clsName == "java.lang.Win32Process" || clsName == "java.lang.ProcessImpl" ) {
+ import com.sun.jna.platform.win32.{ WinNT, Kernel32 }
+ val handle = new WinNT.HANDLE
+ handle.setPointer(
+ com.sun.jna.Pointer.createConstant(
+ accessField( process.getClass, "handle" ).getLong( process )
+ )
+ )
+ Kernel32.INSTANCE.GetProcessId( handle )
+ } else {
+ throw new Exception( "Unexpected Process sub-class: " + clsName )
+ }
+ }
+
+ def asyncPipeCharacterStreamSyncLines( inputStream: InputStream, outputStream: OutputStream, lock: AnyRef ): Thread = {
+ new Thread(
+ new Runnable {
+ def run = {
+ val b = new BufferedInputStream( inputStream )
+ Iterator.continually {
+ b.read // block until and read next character
+ }.takeWhile( _ != -1 ).map { c =>
+ lock.synchronized { // synchronize with other invocations
+ outputStream.write( c )
+ Iterator
+ .continually( b.read )
+ .takeWhile( _ != -1 )
+ .map { c =>
+ try {
+ outputStream.write( c )
+ outputStream.flush
+ (
+ c != '\n' // release lock when new line was encountered, allowing other writers to slip in
+ && b.available > 0 // also release when nothing is available to not block other outputs
+ )
+ } catch {
+ case e: IOException if e.getMessage == "Stream closed" => false
+ }
+ }
+ .takeWhile( identity )
+ .length // force entire iterator
+ }
+ }.length // force entire iterator
+ }
+ }
+ )
+ }
+
+ def asyncPipeCharacterStream( inputStream: InputStream, outputStream: OutputStream, continue: => Boolean ) = {
+ new Thread(
+ new Runnable {
+ def run = {
+ Iterator
+ .continually { inputStream.read }
+ .takeWhile( _ != -1 )
+ .map { c =>
+ try {
+ outputStream.write( c )
+ outputStream.flush
+ true
+ } catch {
+ case e: IOException if e.getMessage == "Stream closed" => false
+ }
+ }
+ .takeWhile( identity )
+ .takeWhile( _ => continue )
+ .length // force entire iterator
+ }
+ }
+ )
+ }
+}
diff --git a/libraries/process/test/test.scala b/libraries/process/test/test.scala
new file mode 100644
index 0000000..4e1c1a1
--- /dev/null
+++ b/libraries/process/test/test.scala
@@ -0,0 +1,7 @@
+object Tests{
+ def main(args: Array[String]): Unit = {
+ val pb = new ProcessBuilder("cat")
+ val p = pb.start
+ cbt.process.getProcessId( p ) // checks that it actually gets a process id
+ }
+}
diff --git a/stage1/Stage1.scala b/stage1/Stage1.scala
index d9bde7c..99c7b1e 100644
--- a/stage1/Stage1.scala
+++ b/stage1/Stage1.scala
@@ -94,6 +94,7 @@ object Stage1{
stage2.listFiles
++ (stage2 / "plugins").listOrFail
++ (cbtHome / "libraries" / "eval").listOrFail
+ ++ (cbtHome / "libraries" / "process").listOrFail
).filter(_.isFile).filter(_.toString.endsWith(".scala"))
val cls = this.getClass.getClassLoader.loadClass("cbt.NailgunLauncher")
diff --git a/stage1/Stage1Lib.scala b/stage1/Stage1Lib.scala
index 40b3fed..ab95a41 100644
--- a/stage1/Stage1Lib.scala
+++ b/stage1/Stage1Lib.scala
@@ -432,99 +432,6 @@ ${sourceFiles.sorted.mkString(" \\\n")}
outputLastModified
)
}
-
- def asyncPipeCharacterStreamSyncLines( inputStream: InputStream, outputStream: OutputStream, lock: AnyRef ): Thread = {
- new Thread(
- new Runnable{
- def run = {
- val b = new BufferedInputStream( inputStream )
- Iterator.continually{
- b.read // block until and read next character
- }.takeWhile(_ != -1).map{ c =>
- lock.synchronized{ // synchronize with other invocations
- outputStream.write(c)
- Iterator
- .continually( b.read )
- .takeWhile( _ != -1 )
- .map{ c =>
- try{
- outputStream.write(c)
- outputStream.flush
- (
- c != '\n' // release lock when new line was encountered, allowing other writers to slip in
- && b.available > 0 // also release when nothing is available to not block other outputs
- )
- } catch {
- case e: IOException if e.getMessage == "Stream closed" => false
- }
- }
- .takeWhile(identity)
- .length // force entire iterator
- }
- }.length // force entire iterator
- }
- }
- )
- }
-
- def asyncPipeCharacterStream( inputStream: InputStream, outputStream: OutputStream, continue: => Boolean ) = {
- new Thread(
- new Runnable{
- def run = {
- Iterator
- .continually{ inputStream.read }
- .takeWhile(_ != -1)
- .map{ c =>
- try{
- outputStream.write(c)
- outputStream.flush
- true
- } catch {
- case e: IOException if e.getMessage == "Stream closed" => false
- }
- }
- .takeWhile( identity )
- .takeWhile( _ => continue )
- .length // force entire iterator
- }
- }
- )
- }
-
- def runWithIO( commandLine: Seq[String], directory: Option[File] = None ): ExitCode = {
- val (out,err,in) = lib.getOutErrIn match { case (l,r, in) => (l.get,r.get, in) }
- val pb = new ProcessBuilder( commandLine: _* )
- val exitCode =
- if( !NailgunLauncher.runningViaNailgun ){
- pb.inheritIO.start.waitFor
- } else {
- val process = directory.map( pb.directory( _ ) ).getOrElse( pb )
- .redirectInput(ProcessBuilder.Redirect.PIPE)
- .redirectOutput(ProcessBuilder.Redirect.PIPE)
- .redirectError(ProcessBuilder.Redirect.PIPE)
- .start
-
- val lock = new AnyRef
-
- val t1 = lib.asyncPipeCharacterStreamSyncLines( process.getErrorStream, err, lock )
- val t2 = lib.asyncPipeCharacterStreamSyncLines( process.getInputStream, out, lock )
- val t3 = lib.asyncPipeCharacterStream( System.in, process.getOutputStream, process.isAlive )
-
- t1.start
- t2.start
- t3.start
-
- t1.join
- t2.join
-
- val e = process.waitFor
- System.err.println( scala.Console.RESET + "Please press ENTER to continue..." )
- t3.join
- e
- }
-
- ExitCode( exitCode )
- }
}
import scala.reflect._
diff --git a/stage1/resolver.scala b/stage1/resolver.scala
index f4a9b13..13d4070 100644
--- a/stage1/resolver.scala
+++ b/stage1/resolver.scala
@@ -77,25 +77,16 @@ trait DependencyImplementation extends Dependency{
)
}
*/
- def fork = false
- def runMain( className: String, args: Seq[String] ): ExitCode = {
- if(fork){
- val java_exe = new File(System.getProperty("java.home")) / "bin" / "java"
- lib.runWithIO(
- java_exe.string +: "-cp" +: classpath.string +: className +: args
- )
- } else {
- lib.getMain( classLoader.loadClass( className ) )( args )
- }
- }
+ def runMain( className: String, args: Seq[String] ): ExitCode =
+ lib.getMain( classLoader.loadClass( className ) )( args )
- def runMain( args: Seq[String] ): ExitCode = {
- val c = mainClass.getOrElse(
- throw new RuntimeException( "No main class found in " + this )
- )
- runMain( c.getName, args )
- }
+ def runMain( args: Seq[String] ): ExitCode =
+ runMain( mainClassOrFail.getName, args )
+
+ def mainClassOrFail = mainClass.getOrElse(
+ throw new RuntimeException( "No main class found in " + this )
+ )
def mainClass = lib.pickOne(
"Which one do you want to run?",
@@ -209,6 +200,7 @@ case class CbtDependencies(cbtLastModified: Long, mavenCache: File, nailgunTarge
stage1Dependency +:
MavenResolver(cbtLastModified, mavenCache,mavenCentral).bind(
MavenDependency("org.eclipse.jgit", "org.eclipse.jgit", "4.2.0.201601211800-r"),
+ MavenDependency("net.java.dev.jna", "jna-platform", "4.4.0"),
MavenDependency("org.scala-lang","scala-compiler",constants.scalaVersion)
)
)
diff --git a/stage2/BasicBuild.scala b/stage2/BasicBuild.scala
index 0bdbad7..e41545b 100644
--- a/stage2/BasicBuild.scala
+++ b/stage2/BasicBuild.scala
@@ -319,4 +319,32 @@ trait BaseBuild extends BuildInterface with DependencyImplementation with SbtDep
final def crossScalaVersionsArray = Array(scalaVersion)
def publish: Seq[URL] = Seq()
+
+ def fork = false
+
+ def runForked: ExitCode = {
+ val ( pid, waitFor, destroy ) = runForkedHandles
+ waitFor()
+ }
+
+ protected def runForkedHandles = runForked( mainClassOrFail.getName, context.args )
+
+ def runForked( className: String, args: Seq[String] ): ( Int, () => ExitCode, () => ExitCode ) =
+ lib.runMainForked(
+ className,
+ args,
+ classpath.string,
+ Some( context.workingDirectory ),
+ NailgunLauncher.runningViaNailgun.option(
+ lib.getOutErrIn match { case (l,r, in) => (l.get,r.get, in) }
+ )
+ )
+
+ override def runMain( className: String, args: Seq[String] ): ExitCode = {
+ if(fork){
+ runForked(className, args)._2()
+ } else {
+ super.runMain( className, args )
+ }
+ }
}
diff --git a/stage2/Lib.scala b/stage2/Lib.scala
index 56f24c6..8801b33 100644
--- a/stage2/Lib.scala
+++ b/stage2/Lib.scala
@@ -16,7 +16,10 @@ import scala.reflect.NameTransformer
case class Developer(id: String, name: String, timezone: String, url: URL)
/** Don't extend. Create your own libs :). */
-final class Lib(val logger: Logger) extends Stage1Lib(logger){
+final class Lib(val logger: Logger) extends
+ Stage1Lib(logger) with
+ _root_.cbt.process.Module
+{
lib =>
val buildFileName = "build.scala"
diff --git a/stage2/libraries.scala b/stage2/libraries.scala
index 08a7a74..b4515e1 100644
--- a/stage2/libraries.scala
+++ b/stage2/libraries.scala
@@ -14,6 +14,7 @@ class libraries( context: Context, scalaVersion: String, scalaMajorVersion: Stri
def eval = dep( "eval" )
def file = dep( "file" )
def interfaces = dep( "interfaces" )
+ def process = dep( "process" )
def proguard = dep( "proguard" )
def reflect = dep( "reflect" )
def scalatestRunner = dep( "scalatest-runner" )