Skip to content

Commit

Permalink
Scala Native support (#397)
Browse files Browse the repository at this point in the history
* native support

* native support

* fix ci

* fix ci

* fixed permissions

* updated README
  • Loading branch information
pablf authored Jan 29, 2024
1 parent b621547 commit be0a43b
Show file tree
Hide file tree
Showing 38 changed files with 806 additions and 229 deletions.
10 changes: 7 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,9 @@ jobs:
strategy:
fail-fast: false
matrix:
java: ['[email protected]']
scala: ['2.11.12', '2.12.17', '2.13.8', '3.2.2']
java: ['17']
scala: ['2.12.17', '2.13.8', '3.3.0']
platform: ['JVM', 'Native']
steps:
- name: Checkout current branch
uses: actions/[email protected]
Expand All @@ -64,8 +65,11 @@ jobs:
java-version: ${{ matrix.java }}
- name: Cache scala dependencies
uses: coursier/cache-action@v6
- name: Install libuv
if: matrix.platform == 'Native'
run: sudo apt-get update && sudo apt-get install -y libuv1-dev
- name: Run tests
run: sbt ++${{ matrix.scala }} zioProcess/test
run: sbt ++${{ matrix.scala }} zioProcess${{ matrix.platform }}/test

ci:
runs-on: ubuntu-20.04
Expand Down
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,12 @@ Key features of the ZIO Process:
In order to use this library, we need to add the following line in our `build.sbt` file:

```scala
libraryDependencies += "dev.zio" %% "zio-process" % "0.7.1"
libraryDependencies += "dev.zio" %% "zio-process" % "0.7.2"
```

## Native support
Some features might have a different behaviour when using Scala Native. Creating non-existent commands do not throw a corresponding error. `bash` command might be needed when executing a script. In some cases, using `ZStreams` as standard input might block the process. Instead, a Java `InputStream` can be used to write to the standard input of the process.

## Example

Here is a simple example of using ZIO Process:
Expand Down
42 changes: 30 additions & 12 deletions build.sbt
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import BuildHelper._
import sbtwelcome._
import sbt.addSbtPlugin
//import sbtcrossproject.CrossPlugin.autoImport._

inThisBuild(
List(
Expand Down Expand Up @@ -44,30 +46,47 @@ usefulTasks := Seq(
UsefulTask("", "testOnly *.YourSpec -- -t \"YourLabel\"", "Only runs tests with matching term")
)

val zioVersion = "2.0.9"
val zioVersion = "2.0.21"

lazy val root =
project
.in(file("."))
.settings(publish / skip := true)
.aggregate(zioProcess, docs)
.settings(
publish / skip := true,
crossScalaVersions := Nil
)
.aggregate(zioProcess.jvm, zioProcess.native, docs)

lazy val zioProcess =
project
crossProject(JVMPlatform, NativePlatform)
.in(file("zio-process"))
.settings(stdSettings("zio-process"))
.settings(crossProjectSettings)
.settings(buildInfoSettings("zio.process"))
.settings(
testFrameworks := Seq(new TestFramework("zio.test.sbt.ZTestFramework")),
.jvmSettings(testFrameworks := Seq(new TestFramework("zio.test.sbt.ZTestFramework")))
.jvmSettings(
libraryDependencies ++= Seq(
"dev.zio" %% "zio" % zioVersion,
"dev.zio" %% "zio-streams" % zioVersion,
"org.scala-lang.modules" %% "scala-collection-compat" % "2.9.0",
"dev.zio" %% "zio-test" % zioVersion % "test",
"dev.zio" %% "zio-test-sbt" % zioVersion % "test"
"dev.zio" %% "zio-test" % zioVersion % Test,
"dev.zio" %% "zio-test-sbt" % zioVersion % Test
)
)
.enablePlugins(BuildInfoPlugin)
.jvmSettings(dottySettings)
.nativeSettings(Test / fork := false)
.nativeSettings(testFrameworks := Seq(new TestFramework("zio.test.sbt.ZTestFramework")))
.nativeSettings(
libraryDependencies ++= Seq(
"dev.zio" %%% "zio" % zioVersion,
"dev.zio" %%% "zio-streams" % zioVersion,
"org.scala-lang.modules" %%% "scala-collection-compat" % "2.9.0",
"dev.zio" %%% "zio-test" % zioVersion % Test,
"dev.zio" %%% "zio-test-sbt" % zioVersion % Test,
"io.github.cquiroz" %%% "scala-java-time" % "2.5.0" % Test
)
)

lazy val docs = project
.in(file("zio-process-docs"))
Expand All @@ -76,13 +95,12 @@ lazy val docs = project
moduleName := "zio-process-docs",
scalacOptions -= "-Yno-imports",
scalacOptions -= "-Xfatal-warnings",
crossScalaVersions -= Scala211,
libraryDependencies ++= Seq("dev.zio" %% "zio" % zioVersion),
projectName := "ZIO Process",
mainModuleName := (zioProcess / moduleName).value,
mainModuleName := (zioProcess.jvm / moduleName).value,
projectStage := ProjectStage.ProductionReady,
ScalaUnidoc / unidoc / unidocProjectFilter := inProjects(zioProcess),
ScalaUnidoc / unidoc / unidocProjectFilter := inProjects(zioProcess.jvm),
docsPublishBranch := "series/2.x"
)
.dependsOn(zioProcess)
.dependsOn(zioProcess.jvm)
.enablePlugins(WebsitePlugin)
3 changes: 3 additions & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ In order to use this library, we need to add the following line in our `build.sb
libraryDependencies += "dev.zio" %% "zio-process" % "@VERSION@"
```

## Native support
Some features might have a different behaviour when using Scala Native. Creating non-existent commands do not throw a corresponding error. `bash` command might be needed when executing a script. In some cases, using `ZStreams` as standard input might block the process. Instead, a Java `InputStream` can be used to write to the standard input of the process.

## Example

Here is a simple example of using ZIO Process:
Expand Down
80 changes: 78 additions & 2 deletions project/BuildHelper.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import sbt.Keys._
import sbt._
import sbtbuildinfo.BuildInfoKeys._
import sbtbuildinfo._
import sbtcrossproject.CrossPlugin.autoImport._

object BuildHelper {
private val versions: Map[String, String] = {
Expand All @@ -18,7 +19,6 @@ object BuildHelper {
}.toMap
}

val Scala211: String = versions("2.11")
val Scala212: String = versions("2.12")
val Scala213: String = versions("2.13")
val Scala3: String = versions("3")
Expand Down Expand Up @@ -93,9 +93,85 @@ object BuildHelper {
def stdSettings(prjName: String) = Seq(
name := s"$prjName",
fork := true,
crossScalaVersions := Seq(Scala211, Scala212, Scala213, Scala3),
crossScalaVersions := Seq(Scala212, Scala213),
ThisBuild / scalaVersion := Scala213,
scalacOptions := stdOptions ++ extraOptions(scalaVersion.value),
incOptions ~= (_.withLogRecompileOnMacro(false))
)

def platformSpecificSources(platform: String, conf: String, baseDirectory: File)(versions: String*) =
for {
platform <- List("shared", platform)
version <- "scala" :: versions.toList.map("scala-" + _)
result = baseDirectory.getParentFile / platform.toLowerCase / "src" / conf / version
if result.exists
} yield result

def crossPlatformSources(scalaVer: String, platform: String, conf: String, baseDir: File) = {
val versions = CrossVersion.partialVersion(scalaVer) match {
case Some((2, 11)) =>
List("2.11", "2.11+", "2.11-2.12", "2.x")
case Some((2, 12)) =>
List("2.12", "2.11+", "2.12+", "2.11-2.12", "2.12-2.13", "2.x")
case Some((2, 13)) =>
List("2.13", "2.11+", "2.12+", "2.13+", "2.12-2.13", "2.x")
case Some((3, 0)) =>
List("dotty", "2.11+", "2.12+", "2.13+", "3.x")
case _ =>
List()
}
platformSpecificSources(platform, conf, baseDir)(versions: _*)
}

lazy val crossProjectSettings = Seq(
Compile / unmanagedSourceDirectories ++= {
crossPlatformSources(
scalaVersion.value,
crossProjectPlatform.value.identifier,
"main",
baseDirectory.value
)
},
Test / unmanagedSourceDirectories ++= {
crossPlatformSources(
scalaVersion.value,
crossProjectPlatform.value.identifier,
"test",
baseDirectory.value
)
}
)

val dottySettings = Seq(
crossScalaVersions += Scala3,
scalacOptions ++= {
if (scalaVersion.value == Scala3)
Seq("-noindent")
else
Seq()
},
scalacOptions --= {
if (scalaVersion.value == Scala3)
Seq("-Xfatal-warnings")
else
Seq()
},
Compile / doc / sources := {
val old = (Compile / doc / sources).value
if (scalaVersion.value == Scala3) {
Nil
} else {
old
}
},
Test / parallelExecution := {
val old = (Test / parallelExecution).value
if (scalaVersion.value == Scala3) {
false
} else {
old
}
}
)

}
8 changes: 5 additions & 3 deletions project/plugins.sbt
Original file line number Diff line number Diff line change
@@ -1,22 +1,24 @@
addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.5.0")

addSbtPlugin("pl.project13.scala" % "sbt-jmh" % "0.4.4")
addSbtPlugin("pl.project13.scala" % "sbt-jmh" % "0.4.5")

addSbtPlugin("com.eed3si9n" % "sbt-buildinfo" % "0.11.0")

addSbtPlugin("org.scoverage" % "sbt-scoverage" % "2.0.7")

addSbtPlugin("org.scalameta" % "sbt-mdoc" % "2.3.7")

addSbtPlugin("ch.epfl.scala" % "sbt-bloop" % "1.5.6")
addSbtPlugin("ch.epfl.scala" % "sbt-bloop" % "1.5.9")

addSbtPlugin("com.github.sbt" % "sbt-unidoc" % "0.5.0")

addSbtPlugin("com.github.sbt" % "sbt-ci-release" % "1.5.11")

addSbtPlugin("com.github.reibitto" % "sbt-welcome" % "0.2.2")

addSbtPlugin("dev.zio" % "zio-sbt-website" % "0.3.4")
addSbtPlugin("dev.zio" % "zio-sbt-website" % "0.3.4")
addSbtPlugin("org.portable-scala" % "sbt-scala-native-crossproject" % "1.3.2")
addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.4.17")

libraryDependencies += "org.snakeyaml" % "snakeyaml-engine" % "2.6"

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Copyright 2017-2020 John A. De Goes and the ZIO Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package zio.process

import scala.annotation.nowarn
import java.io.File
import zio.NonEmptyChunk

private[process] trait CommandPlatformSpecific {

@nowarn
protected def checkDirectory(dir: File): Boolean = true

protected def adaptCommand(command: NonEmptyChunk[String]): NonEmptyChunk[String] = command

}
Original file line number Diff line number Diff line change
Expand Up @@ -18,27 +18,20 @@ package zio.process
import zio.stream.ZStream
import zio.{ Chunk, Queue }

import java.io.{ ByteArrayInputStream, File }
import java.io.ByteArrayInputStream
import java.nio.charset.{ Charset, StandardCharsets }
import java.io.File
import java.nio.file.Path
import java.io.InputStream

sealed trait ProcessInput

object ProcessInput {
final case class FromStream(stream: ZStream[Any, CommandError, Byte], flushChunksEagerly: Boolean)
extends ProcessInput

case object Inherit extends ProcessInput
case object Pipe extends ProcessInput

/**
* Returns a ProcessInput from an array of bytes.
*/
def fromByteArray(bytes: Array[Byte]): ProcessInput =
ProcessInput.FromStream(
ZStream.fromInputStream(new ByteArrayInputStream(bytes)).mapError(CommandError.IOError.apply),
flushChunksEagerly = false
)
final case class JavaStream(stream: InputStream, flushChunksEagerly: Boolean) extends ProcessInput
case object Inherit extends ProcessInput
case object Pipe extends ProcessInput

/**
* Returns a ProcessInput from a file.
Expand All @@ -58,6 +51,15 @@ object ProcessInput {
flushChunksEagerly = false
)

/**
* Returns a ProcessInput from an array of bytes.
*/
def fromByteArray(bytes: Array[Byte]): ProcessInput =
ProcessInput.FromStream(
ZStream.fromInputStream(new ByteArrayInputStream(bytes)).mapError(CommandError.IOError.apply),
flushChunksEagerly = false
)

/**
* Returns a ProcessInput from a queue of bytes to send to the process in a controlled manner.
*/
Expand Down Expand Up @@ -87,4 +89,5 @@ object ProcessInput {
ZStream.fromChunks(Chunk.fromArray(text.getBytes(StandardCharsets.UTF_8))),
flushChunksEagerly = false
)

}
Loading

0 comments on commit be0a43b

Please sign in to comment.