diff --git a/.appveyor.yml b/.appveyor.yml new file mode 100644 index 000000000..a0d3292f1 --- /dev/null +++ b/.appveyor.yml @@ -0,0 +1,23 @@ +build: off + +init: + - git config --global core.autocrlf input + +install: + - cinst jdk8 -params 'installdir=C:\\jdk8' + - SET JAVA_HOME=C:\jdk8 + - SET PATH=C:\jdk8\bin;%PATH% + + - ps: | + Add-Type -AssemblyName System.IO.Compression.FileSystem + if (!(Test-Path -Path "C:\sbt" )) { + (new-object System.Net.WebClient).DownloadFile( + 'https://github.com/sbt/sbt/releases/download/v1.0.4/sbt-1.0.4.zip', + 'C:\sbt-bin.zip' + ) + [System.IO.Compression.ZipFile]::ExtractToDirectory("C:\sbt-bin.zip", "C:\sbt") + } + - SET PATH=C:\sbt\sbt\bin;%PATH% + - SET SBT_OPTS=-XX:MaxPermSize=2g -Xmx4g -Dfile.encoding=UTF8 +test_script: + - sbt "scripted actions/* server/*" diff --git a/build.sbt b/build.sbt index ec5065942..dcedffd62 100644 --- a/build.sbt +++ b/build.sbt @@ -34,6 +34,7 @@ def buildLevelSettings: Seq[Setting[_]] = scmInfo := Some(ScmInfo(url("https://github.com/sbt/sbt"), "git@github.com:sbt/sbt.git")), resolvers += Resolver.mavenLocal, scalafmtOnCompile := true, + scalafmtOnCompile in Sbt := false, scalafmtVersion := "1.3.0", )) @@ -310,8 +311,7 @@ lazy val commandProj = (project in file("main-command")) .settings( testedBaseSettings, name := "Command", - libraryDependencies ++= Seq(launcherInterface, sjsonNewScalaJson.value, templateResolverApi, - jna, jnaPlatform), + libraryDependencies ++= Seq(launcherInterface, sjsonNewScalaJson.value, templateResolverApi), managedSourceDirectories in Compile += baseDirectory.value / "src" / "main" / "contraband-scala", sourceManaged in (Compile, generateContrabands) := baseDirectory.value / "src" / "main" / "contraband-scala", @@ -463,10 +463,9 @@ lazy val sbtIgnoredProblems = { } def runNpm(command: String, base: File, log: sbt.internal.util.ManagedLogger) = { - val npm = if (sbt.internal.util.Util.isWindows) "npm.cmd" else "npm" import scala.sys.process._ try { - val exitCode = Process(s"$npm $command", Option(base)) ! log + val exitCode = Process(s"npm $command", Option(base)) ! log if (exitCode != 0) throw new Exception("Process returned exit code: " + exitCode) } catch { case e: java.io.IOException => log.warn("failed to run npm " + e.getMessage) diff --git a/main-actions/src/main/scala/sbt/compiler/Eval.scala b/main-actions/src/main/scala/sbt/compiler/Eval.scala index a075f61a8..a9d97246a 100644 --- a/main-actions/src/main/scala/sbt/compiler/Eval.scala +++ b/main-actions/src/main/scala/sbt/compiler/Eval.scala @@ -485,12 +485,10 @@ private[sbt] object Eval { def filesModifiedBytes(fs: Array[File]): Array[Byte] = if (fs eq null) filesModifiedBytes(Array[File]()) else seqBytes(fs)(fileModifiedBytes) def fileModifiedBytes(f: File): Array[Byte] = - (if (f.isDirectory) filesModifiedBytes(f listFiles classDirFilter) + (if (f.isDirectory) + filesModifiedBytes(f listFiles classDirFilter) else - bytes( - try IO.getModifiedTime(f) - catch { case _: java.io.FileNotFoundException => 0L })) ++ - bytes(f.getAbsolutePath) + bytes(IO.getModifiedTimeOrZero(f))) ++ bytes(f.getAbsolutePath) def fileExistsBytes(f: File): Array[Byte] = bytes(f.exists) ++ bytes(f.getAbsolutePath) diff --git a/main-command/src/main/java/sbt/internal/NGWin32NamedPipeLibrary.java b/main-command/src/main/java/sbt/internal/NGWin32NamedPipeLibrary.java index ba535691f..dd4d8f15a 100644 --- a/main-command/src/main/java/sbt/internal/NGWin32NamedPipeLibrary.java +++ b/main-command/src/main/java/sbt/internal/NGWin32NamedPipeLibrary.java @@ -29,7 +29,7 @@ import com.sun.jna.ptr.IntByReference; import com.sun.jna.win32.W32APIOptions; -public interface NGWin32NamedPipeLibrary extends WinNT { +public interface NGWin32NamedPipeLibrary extends Library, WinNT { int PIPE_ACCESS_DUPLEX = 3; int PIPE_UNLIMITED_INSTANCES = 255; int FILE_FLAG_FIRST_PIPE_INSTANCE = 524288; diff --git a/main-command/src/main/scala/sbt/internal/server/Server.scala b/main-command/src/main/scala/sbt/internal/server/Server.scala index 4fb8a7cb4..521c8d399 100644 --- a/main-command/src/main/scala/sbt/internal/server/Server.scala +++ b/main-command/src/main/scala/sbt/internal/server/Server.scala @@ -97,7 +97,7 @@ private[sbt] object Server { case Failure(e) => () case Success(socket) => socket.close() - throw new IOException("sbt server is already running.") + throw new AlreadyRunningException() } } else () } @@ -210,3 +210,5 @@ private[sbt] case class ServerConnection( } } } + +private[sbt] class AlreadyRunningException extends IOException("sbt server is already running.") diff --git a/main/src/main/scala/sbt/Defaults.scala b/main/src/main/scala/sbt/Defaults.scala index b006c115d..4fd8d1585 100755 --- a/main/src/main/scala/sbt/Defaults.scala +++ b/main/src/main/scala/sbt/Defaults.scala @@ -2315,7 +2315,7 @@ object Classpaths { case Some(period) => val fullUpdateOutput = cacheDirectory / "out" val now = System.currentTimeMillis - val diff = now - IO.getModifiedTime(fullUpdateOutput) + val diff = now - IO.getModifiedTimeOrZero(fullUpdateOutput) val elapsedDuration = new FiniteDuration(diff, TimeUnit.MILLISECONDS) fullUpdateOutput.exists() && elapsedDuration > period } diff --git a/main/src/main/scala/sbt/Main.scala b/main/src/main/scala/sbt/Main.scala index 6c605056e..da44d0cb8 100644 --- a/main/src/main/scala/sbt/Main.scala +++ b/main/src/main/scala/sbt/Main.scala @@ -128,6 +128,9 @@ object StandardMain { def initialState(configuration: xsbti.AppConfiguration, initialDefinitions: Seq[Command], preCommands: Seq[String]): State = { + // This is to workaround https://github.com/sbt/io/issues/110 + sys.props.put("jna.nosys", "true") + import BasicCommandStrings.isEarlyCommand val userCommands = configuration.arguments.map(_.trim) val (earlyCommands, normalCommands) = (preCommands ++ userCommands).partition(isEarlyCommand) diff --git a/main/src/main/scala/sbt/internal/CommandExchange.scala b/main/src/main/scala/sbt/internal/CommandExchange.scala index 39259ee28..d7b52280d 100644 --- a/main/src/main/scala/sbt/internal/CommandExchange.scala +++ b/main/src/main/scala/sbt/internal/CommandExchange.scala @@ -10,7 +10,7 @@ package internal import java.io.IOException import java.util.concurrent.ConcurrentLinkedQueue -import java.util.concurrent.atomic.AtomicInteger +import java.util.concurrent.atomic._ import scala.collection.mutable.ListBuffer import scala.annotation.tailrec import BasicKeys.{ @@ -26,7 +26,7 @@ import sjsonnew.JsonFormat import sjsonnew.shaded.scalajson.ast.unsafe._ import scala.concurrent.Await import scala.concurrent.duration.Duration -import scala.util.{ Success, Failure } +import scala.util.{ Success, Failure, Try } import sbt.io.syntax._ import sbt.io.{ Hash, IO } import sbt.internal.server._ @@ -48,6 +48,7 @@ private[sbt] final class CommandExchange { } getOrElse true private val lock = new AnyRef {} private var server: Option[ServerInstance] = None + private val firstInstance: AtomicBoolean = new AtomicBoolean(true) private var consoleChannel: Option[ConsoleChannel] = None private val commandQueue: ConcurrentLinkedQueue[Exec] = new ConcurrentLinkedQueue() private val channelBuffer: ListBuffer[CommandChannel] = new ListBuffer() @@ -90,7 +91,6 @@ private[sbt] final class CommandExchange { else s } - private def newChannelName: String = s"channel-${nextChannelId.incrementAndGet()}" private def newNetworkName: String = s"network-${nextChannelId.incrementAndGet()}" /** @@ -132,7 +132,8 @@ private[sbt] final class CommandExchange { subscribe(channel) } server match { - case Some(_) => // do nothing + case Some(_) => // do nothing + case None if !firstInstance.get => // there's another server case _ => val portfile = (new File(".")).getAbsoluteFile / "project" / "target" / "active.json" val h = Hash.halfHashString(IO.toURI(portfile).toString) @@ -149,15 +150,27 @@ private[sbt] final class CommandExchange { socketfile, pipeName) val x = Server.start(connection, onIncomingSocket, s.log) - Await.ready(x.ready, Duration("10s")) + + // don't throw exception when it times out + val d = "10s" + Try(Await.ready(x.ready, Duration(d))) x.ready.value match { case Some(Success(_)) => // rememeber to shutdown only when the server comes up server = Some(x) + case Some(Failure(e: AlreadyRunningException)) => + s.log.warn( + "sbt server could not start because there's another instance of sbt running on this build.") + s.log.warn("Running multiple instances is unsupported") + server = None + firstInstance.set(false) case Some(Failure(e)) => s.log.error(e.toString) server = None - case None => // this won't happen because we awaited + case None => + s.log.warn(s"sbt server could not start in $d") + server = None + firstInstance.set(false) } } s @@ -191,6 +204,7 @@ private[sbt] final class CommandExchange { case xs => lock.synchronized { channelBuffer --= xs + () } } } @@ -247,6 +261,7 @@ private[sbt] final class CommandExchange { case xs => lock.synchronized { channelBuffer --= xs + () } } } @@ -288,6 +303,7 @@ private[sbt] final class CommandExchange { case xs => lock.synchronized { channelBuffer --= xs + () } } } @@ -339,6 +355,7 @@ private[sbt] final class CommandExchange { case xs => lock.synchronized { channelBuffer --= xs + () } } } diff --git a/main/src/main/scala/sbt/internal/LibraryManagement.scala b/main/src/main/scala/sbt/internal/LibraryManagement.scala index fc832ffff..d247a9d99 100644 --- a/main/src/main/scala/sbt/internal/LibraryManagement.scala +++ b/main/src/main/scala/sbt/internal/LibraryManagement.scala @@ -127,7 +127,7 @@ private[sbt] object LibraryManagement { } private[this] def fileUptodate(file: File, stamps: Map[File, Long]): Boolean = - stamps.get(file).forall(_ == IO.getModifiedTime(file)) + stamps.get(file).forall(_ == IO.getModifiedTimeOrZero(file)) private[sbt] def transitiveScratch( lm: DependencyResolution, diff --git a/project/Dependencies.scala b/project/Dependencies.scala index f8e14429f..21de56080 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -12,10 +12,10 @@ object Dependencies { val baseScalaVersion = scala212 // sbt modules - private val ioVersion = "1.1.2" - private val utilVersion = "1.1.1" - private val lmVersion = "1.1.1" - private val zincVersion = "1.1.0-RC3" + private val ioVersion = "1.1.3" + private val utilVersion = "1.1.2" + private val lmVersion = "1.1.2" + private val zincVersion = "1.1.0-RC4" private val sbtIO = "org.scala-sbt" %% "io" % ioVersion @@ -106,8 +106,6 @@ object Dependencies { val specs2 = "org.specs2" %% "specs2-junit" % "4.0.1" val junit = "junit" % "junit" % "4.11" val templateResolverApi = "org.scala-sbt" % "template-resolver" % "0.1" - val jna = "net.java.dev.jna" % "jna" % "4.1.0" - val jnaPlatform = "net.java.dev.jna" % "jna-platform" % "4.1.0" private def scala211Module(name: String, moduleVersion: String) = Def setting ( scalaBinaryVersion.value match { diff --git a/project/Scripted.scala b/project/Scripted.scala index 932d55a0f..788f1d60e 100644 --- a/project/Scripted.scala +++ b/project/Scripted.scala @@ -37,6 +37,9 @@ trait ScriptedKeys { } object Scripted { + // This is to workaround https://github.com/sbt/io/issues/110 + sys.props.put("jna.nosys", "true") + lazy val MavenResolverPluginTest = config("mavenResolverPluginTest") extend Compile lazy val RepoOverrideTest = config("repoOverrideTest") extend Compile diff --git a/project/SiteMap.scala b/project/SiteMap.scala index 2cfb5d8ea..95e038eab 100644 --- a/project/SiteMap.scala +++ b/project/SiteMap.scala @@ -68,8 +68,8 @@ object SiteMap { // generates a string suitable for a sitemap file representing the last modified time of the given File private[this] def lastModifiedString(f: File): String = { val formatter = new java.text.SimpleDateFormat("yyyy-MM-dd") - // TODO: replace lastModified() with sbt.io.Milli.getModifiedTime(), once the build - // has been upgraded to a version of sbt that includes sbt.io.Milli. + // TODO: replace lastModified() with sbt.io.IO.getModifiedTimeOrZero(), once the build + // has been upgraded to a version of sbt that includes that call. formatter.format(new java.util.Date(f.lastModified)) } // writes the provided XML node to `output` and then gzips it to `gzipped` if `gzip` is true diff --git a/project/Util.scala b/project/Util.scala index 0b87b02db..8c68f4ced 100644 --- a/project/Util.scala +++ b/project/Util.scala @@ -105,8 +105,8 @@ object Util { val timestamp = formatter.format(new Date) val content = versionLine(version) + "\ntimestamp=" + timestamp val f = dir / "xsbt.version.properties" - // TODO: replace lastModified() with sbt.io.Milli.getModifiedTime(), once the build - // has been upgraded to a version of sbt that includes sbt.io.Milli. + // TODO: replace lastModified() with sbt.io.IO.getModifiedTimeOrZero(), once the build + // has been upgraded to a version of sbt that includes that call. if (!f.exists || f.lastModified < lastCompilationTime(analysis) || !containsVersion(f, version)) { s.log.info("Writing version information to " + f + " :\n" + content) IO.write(f, content) diff --git a/project/build.properties b/project/build.properties index 94005e587..394cb75cf 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=1.0.0 +sbt.version=1.0.4 diff --git a/project/plugins.sbt b/project/plugins.sbt index 66097a8b9..7ea70f8ea 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,8 +1,7 @@ -scalaVersion := "2.12.3" +scalaVersion := "2.12.4" scalacOptions ++= Seq("-feature", "-language:postfixOps") -addSbtPlugin("org.scala-sbt" % "sbt-houserules" % "0.3.4") +addSbtPlugin("org.scala-sbt" % "sbt-houserules" % "0.3.5") addSbtPlugin("org.scala-sbt" % "sbt-contraband" % "0.3.2") -addSbtPlugin("com.lucidchart" % "sbt-scalafmt" % "1.14") addSbtPlugin("de.heikoseeberger" % "sbt-header" % "3.0.2") addSbtPlugin("com.eed3si9n" % "sbt-buildinfo" % "0.7.0")