aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJakob Odersky <jodersky@gmail.com>2014-01-18 17:21:14 +0100
committerJakob Odersky <jodersky@gmail.com>2014-01-18 17:21:14 +0100
commit17589dcc1899a615d5d31ba54b698e06c45cd13f (patch)
treef26abb735de4c657589ecfea910a1d30875fd561
parent506e9436a6225b804fb3df9f8ce8d5cb62480106 (diff)
downloadakka-serial-17589dcc1899a615d5d31ba54b698e06c45cd13f.tar.gz
akka-serial-17589dcc1899a615d5d31ba54b698e06c45cd13f.tar.bz2
akka-serial-17589dcc1899a615d5d31ba54b698e06c45cd13f.zip
update readmev1.1.0-RC1
-rw-r--r--README.md44
-rw-r--r--documentation/building.md40
-rw-r--r--project/FlowBuild.scala59
-rw-r--r--project/nativefat.scala44
-rw-r--r--project/nativepack.scala37
5 files changed, 154 insertions, 70 deletions
diff --git a/README.md b/README.md
index 762af9d..90c2387 100644
--- a/README.md
+++ b/README.md
@@ -2,26 +2,50 @@
Serial communication library for Scala, designed to be reactive, lightweight and easily integrable with Akka applications.
## Motivation
-The main reason for yet another serial communication library for the JVM is that all other libraries I tested used blocking IO and/or consumed enormous amounts of CPU while being idle, between 2% and 15%. Flow's main goal is therefore to provide a lightweight library that only does work when communication is required. Furthermore, this reactive concept integrates well with the Akka IO layer therefore making flow an ideal library for extending it.
+The main reason for yet another serial communication library for the JVM is that all other libraries tested used blocking IO and/or consumed enormous amounts of CPU while being idle. Flow's main goal is therefore to provide a lightweight library that only does work when communication is required. This reactive concept integrates well with the Akka IO layer therefore making flow an ideal library for extending it.
## Basic usage
For a short guide on how to use flow see the file "documentation/basics.md", accessible on github [here](https://github.com/jodersky/flow/blob/master/documentation/basics.md).
-## Currently supported platforms
+Flow is built and its examples run with SBT. To get started, include a dependency to flow in your project:
-| OS (tested on) | Architecture | Notes |
-|-------------------|-------------------------|-----------------------------------------------------------------------|
-| Linux (3.2.0) | x86<br>x86_64<br>armv7 | A user accessing a serial port may need to be in the 'dialout' group. |
-| Mac OS X (10.6.8) | x86_64 | Use /dev/cu* device instead of /dev/tty*. |
+ libraryDependencies += "com.github.jodersky" % "flow" % "1.1.0"
+
+ATTENTION: flow uses native libraries to back serial communication, therefore before you can run any application depending on flow you must include flow's native library! To do so, you have two options.
+
+1. The easy way: add a second dependency to your project: (this dependency is not yet available on maven, please run ```flow-pack/publishLocal``` before)
+
+ libraryDependencies += "com.github.jodersky" % "flow-pack" % "1.1.0"
+
+ This will add a jar to your classpath containing native libraries for various platforms. At run time, the correct library for the current platform is selected, extracted and loaded. This solution enables running applications seamlessly, as if they were pure JVM applications. However, since the JVM does not enable full determination of the current platform (only OS and rough architecture are known), only a couple of platforms are supported through this solution. Currently, these are given in the table below.
+
+ | OS (tested on) | Architecture | Notes |
+ |-------------------|----------------------|------------------------------------------------------------------------|
+ | Linux (3.2.0) | x86<br>x86_64<br>ARM | ARM specification: v7, hard float ABI (may work on other versions too, any feedback is welcome!)<br>A user accessing a serial port may need to be in the dialout group|
+ | Mac OS X (10.6.8) | x86_64 | Use /dev/cu* device instead of /dev/tty*. |
+
+
+2. Maximum scalability: do not include the second dependency above. Instead, for every end-user application that relies on flow, manually add the native library for the current platform to the JVM's library path. This can be achieved through various ways, notably:
+ - Per application:
+ Run your program with the command-line option ```-Djava.library.path=".:<folder containing libflow.so>"```. E.g. ```java -Djava.library.path=".:/home/<folder containing libflow.so>" -jar your-app.jar```
+
+ - System- or user-wide:
+ Copy the native library to a place that is on the default java library path and run your application normally. Such places usually include /usr/lib and /usr/local/lib
+
+ The native library can either be obtained by building flow (see section Build) or by taking a pre-compiled one, found in releases in the github project.
+
+It is recomended that you use the first option only for testing purposes or end-user applications. The second option is recomended for libraries, since it leaves more choice to the end-user.
+
+## Examples
+Examples on flow's usage are located in the flow-samples directory. The examples may be run by switching to the corresponding project in sbt: `project flow-samples-<sample_name>` and typing `run`. Be sure to connect a serial device before running an example.
+
+Since flow integrates into the Akka-IO framework, a good resource on its general design is the framework's documentation at http://doc.akka.io/docs/akka/2.2.3/scala/io.html
## Native side
Since hardware is involved in serial communication, a Scala-only solution is not possible. Nevertherless, the native code is kept simple and minimalistic with the burden of dealing with threads left to Scala. The code aims to be POSIX compliant and therefore easily portable.
## Build
-The build is currently being restructured for less cumbersome native compilation and easier cross-compilation (e.g. for targeting embedded platforms like the Raspberry Pi).
-Current status: building works, however the production of fat jars containing native libraries is not yet supported.
-
-See documentation/embedded-building.md for a description on how to build the native part of flow on memory restricted devices, requiring only a C compiler and linker.
+See detailed documentation in documentation/build.md (at github [here](https://github.com/jodersky/flow/blob/master/documentation/building.md)) on how to build flow. Note that flow can also be built on devices with sparse resources not able to run SBT (such as the Raspberry Pi), see documentation/embedded-building.md [here](https://github.com/jodersky/flow/blob/master/documentation/embedded-building.md).
## License
flow is released under the terms of the 3-clause BSD license. See LICENSE for details. \ No newline at end of file
diff --git a/documentation/building.md b/documentation/building.md
new file mode 100644
index 0000000..43a3678
--- /dev/null
+++ b/documentation/building.md
@@ -0,0 +1,40 @@
+# Building flow
+A complete build of flow involves two parts
+
+ 1. compiling scala sources (as in a regular project)
+ 2. compiling native sources
+
+As any java or scala project, the first part results in a platform independant artifact. However, the second part yields a binary that may only be used on systems resembling the platform for which it was compiled. To understand how flow can achieve such a mix, it is helpful to look at the project's directory layout.
+
+ .
+ ├── documentation
+ ├── flow
+ │   └── src
+ │ └── main
+ │ ├── java
+ │ ├── native
+ │ └── scala
+ ├── flow-pack
+ │   └── lib_native
+ ├── flow-samples
+ └── project
+
+The directories of interest in a build are:
+
+ - flow/src/main/scala and flow/src/main/java:
+ Contains java/scala sources that constitute the main library.
+
+ - flow/src/main/native:
+ Contains native sources used in combination with JNI to support interacting with serial hardware. The directory itself is subdivided into:
+ - include: general headers describing the native side of the serial API
+ - posix: source code implementing the native serial API for posix-compliant operating systems
+
+With this structure in mind, building a complete distribution of flow involves (sbt commands are given in code tags):
+
+ 1. compiling and packaging java/scala sources: `flow/packageBin`
+ This simply compiles any scala and java sources as with any standard sbt project and produces a jar ready for being used.
+
+ 2. compiling and linking native sources for various platforms: `flow/nativeLink`
+ This is the most complicated and error-prone step in the build. It involves compiling and cross-compiling the native sources for various platforms and linking them.
+ Note that for this step to work, native builds for the current operating system have to be defined. Take a look at the build file to see how this is done (below ```trait Host``` in the file).
+ After completing this step, native libraries for the different platforms are available to be copied and included in end-user applications. \ No newline at end of file
diff --git a/project/FlowBuild.scala b/project/FlowBuild.scala
index 0de60bb..42b9cda 100644
--- a/project/FlowBuild.scala
+++ b/project/FlowBuild.scala
@@ -2,25 +2,26 @@ import sbt._
import Keys._
import JniKeys._
import NativeKeys._
+import NativePackKeys._
object FlowBuild extends Build {
val Organization = "com.github.jodersky"
val ScalaVersion = "2.10.3"
- val Version = "1.1.1" //version of flow library
+ val Version = "1.1.0" //version of flow library
val NativeMajorVersion = 2 //major version of native API
val NativeMinorVersionPosix = 0 //minor version of native posix implementation
val NativeVersionPosix = NativeMajorVersion + "." + NativeMinorVersionPosix
- val release = settingKey[Boolean]("Indicates if this build is a release.")
val gitHeadCommitSha = settingKey[String]("Current commit sha.")
lazy val commonSettings: Seq[Setting[_]] = Seq(
organization := Organization,
scalaVersion := ScalaVersion,
- release in ThisBuild := sys.props("release") == "true",
+ isSnapshot := sys.props("release") != "true",
gitHeadCommitSha in ThisBuild := Process("git rev-parse HEAD").lines.head,
- version in ThisBuild:= { if (release.value ) Version else Version + "-" + gitHeadCommitSha.value },
+ version in ThisBuild:= { if (!isSnapshot.value) Version else Version + "-" + gitHeadCommitSha.value },
licenses := Seq(("BSD-3-Clause", url("http://opensource.org/licenses/BSD-3-Clause"))),
+ homepage := Some(url("http://github.com/jodersky/flow")),
resolvers += "Typesafe Repo" at "http://repo.typesafe.com/typesafe/releases/",
scalacOptions ++= Seq("-deprecation", "-unchecked", "-feature"))
@@ -31,13 +32,12 @@ object FlowBuild extends Build {
)
lazy val root: Project = (
- Project("root", file(".")).aggregate(flow)
+ Project("root", file(".")).aggregate(flow, flowPack)
settings(
publish := (),
publishLocal := ()
)
)
-
lazy val flow: Project = (
Project("flow", file("flow"))
@@ -51,8 +51,15 @@ object FlowBuild extends Build {
javahClasses := Seq("com.github.jodersky.flow.internal.NativeSerial"),
javahHeaderDirectory := (sourceDirectory in Compile).value / "native" / "include",
compileOrder in Compile := CompileOrder.Mixed,
- publishTo := Some("Sonatype Snapshots Nexus" at "https://oss.sonatype.org/content/repositories/snapshots"),
- credentials += Credentials(Path.userHome / ".ivy2" / ".credentials"),
+ publishMavenStyle := true,
+ publishTo := {
+ val nexus = "https://oss.sonatype.org/"
+ if (isSnapshot.value)
+ Some("snapshots" at nexus + "content/repositories/snapshots")
+ else
+ Some("releases" at nexus + "service/local/staging/deploy/maven2")
+ },
+ pomIncludeRepository := { _ => false },
libraryDependencies ++= Seq(
Dependencies.akkaActor,
Dependencies.ioCore,
@@ -67,15 +74,15 @@ object FlowBuild extends Build {
val compiler = "gcc"
val linker = compiler
- val cFlags = Seq("-O2", "-fPIC")
-
- val linkerFlags = Seq("-shared", s"-Wl,-soname,libflow.so.${NativeMajorVersion}")
- val binary = s"libflow.so"
+ val cFlags = List("-O2", "-fPIC")
+ val linkerFlags = List("-shared", s"-Wl,-soname,libflow.so.${NativeMajorVersion}")
+ val binary = "libflow.so"
val builds = List(
- NativeBuild("amd64-linux", "gcc", cFlags :+ "-m64", "gcc", linkerFlags :+ "-m64", binary),
- NativeBuild("x86-linux", "gcc", cFlags :+ "-m32", "gcc", linkerFlags :+ "-m32", binary),
- NativeBuild("arm-linux", "arm-linux-gnueabihf-gcc", cFlags, "arm-linux-gnueabihf-gcc", linkerFlags, binary)
+ NativeBuild("x86_64-linux-gnu", "gcc", "-m64" :: cFlags, "gcc", "-m64" :: linkerFlags, binary),
+ NativeBuild("x86-linux-gnu", "gcc", "-m32" :: cFlags, "gcc", "-m32" :: linkerFlags, binary),
+ NativeBuild("arm-linux-gnueabihf", "arm-linux-gnueabihf-gcc", cFlags, "arm-linux-gnueabihf-gcc", linkerFlags, binary),
+ NativeBuild("arm-linux-gnueabi", "arm-linux-gnueabi-gcc", cFlags, "arm-linux-gnueabi-gcc", linkerFlags, binary)
//add other build configurations here or adapt existing ones to your needs
)
@@ -128,12 +135,32 @@ object FlowBuild extends Build {
}
}
+ lazy val flowPack: Project = (
+ Project("flow-pack", file("flow-pack"))
+ settings (commonSettings: _*)
+ settings (NativePackDefaults.settings: _*)
+ settings (
+ nativePackLinkages := {
+ val linkMappings = Map(
+ "x86_64-linux-gnu" -> "amd64-linux",
+ "x86-linux-gnu" -> "x86-linux",
+ "arm-linux-gnueabihf" -> "arm-linux"
+ )
+ val ls: Seq[(NativeBuild, File)] = (nativeLink in flow).value.toSeq
+ for ((build, binary) <- ls; n <- linkMappings.get(build.name)) yield {
+ (build.copy(name = n), binary)
+ }
+ }
+ )
+ //settings(NativeFatDefaults.settings: _*)
+ dependsOn(flow)
+ )
lazy val samplesTerminal = (
Project("flow-samples-terminal", file("flow-samples") / "flow-samples-terminal")
settings(commonSettings: _*)
settings(runSettings: _*)
- dependsOn(flow)
+ dependsOn(flowPack)
)
diff --git a/project/nativefat.scala b/project/nativefat.scala
deleted file mode 100644
index 02de33b..0000000
--- a/project/nativefat.scala
+++ /dev/null
@@ -1,44 +0,0 @@
-import sbt._
-import Keys._
-import NativeKeys._
-import java.io.File
-import scala.collection.mutable.HashSet
-
-object NativeFatKeys {
- val packageFat = taskKey[File]("Create a fat jar containing native binaries.")
- val packageFatSuffix = settingKey[String]("Suffix to add to name of fat jar.")
- val packageFatUnmanaged = settingKey[File]("Directory containing any pre-compiled native binaries.")
-}
-
-object NativeFatDefaults {
- import NativeFatKeys._
-
- val mappingsImpl = Def.task {
- val links = nativeLink.value //nativeLink produces native shared libraries for different platforms
- val unamanagedDir = packageFatUnmanaged.value
-
- val managed: Seq[(File, String)] = for ( (build, binary) <- links.toSeq) yield {
- binary -> ("native/" + build.name + "/" + binary.name)
- }
-
- val unmanaged: Seq[(File, String)] = for (file <- (unamanagedDir ** "*").get; if file.isFile) yield {
- file -> ("native/" + (file relativeTo unamanagedDir).get.getPath)
- }
-
- managed ++ unmanaged
- }
-
- def settings = sbt.Defaults.packageTaskSettings(packageFat, sbt.Defaults.packageBinMappings) ++
- Seq(
- packageFatSuffix := "-fat",
- packageFatUnmanaged := baseDirectory.value / "lib_native",
- products in packageFat := (products in Compile).value,
- artifact in packageFat := {
- val prev = (artifact in packageBin).value
- prev.copy(name = prev.name + packageFatSuffix.value)
- },
- mappings in packageFat ++= mappingsImpl.value,
- publishArtifact in packageFat := true
- ) ++ addArtifact(artifact in packageFat, packageFat)
-
-} \ No newline at end of file
diff --git a/project/nativepack.scala b/project/nativepack.scala
new file mode 100644
index 0000000..293848d
--- /dev/null
+++ b/project/nativepack.scala
@@ -0,0 +1,37 @@
+import sbt._
+import Keys._
+import NativeKeys._
+import java.io.File
+import scala.collection.mutable.HashSet
+
+object NativePackKeys {
+
+ val nativePackLinkages = taskKey[Seq[(NativeBuild, File)]]("")
+ val nativePackUnmanaged = settingKey[File]("Directory containing any pre-compiled native binaries.")
+
+}
+
+object NativePackDefaults {
+ import NativePackKeys._
+
+ val mappingsImpl = Def.task {
+ val links = nativePackLinkages.value
+ val unamanagedDir = nativePackUnmanaged.value
+
+ val managed: Seq[(File, String)] = for ( (build, binary) <- links.toSeq) yield {
+ binary -> ("native/" + build.name + "/" + binary.name)
+ }
+
+ val unmanaged: Seq[(File, String)] = for (file <- (unamanagedDir ** "*").get; if file.isFile) yield {
+ file -> ("native/" + (file relativeTo unamanagedDir).get.getPath)
+ }
+
+ managed ++ unmanaged
+ }
+
+ def settings = Seq(
+ nativePackUnmanaged := baseDirectory.value / "lib_native",
+ mappings in (Compile, packageBin) ++= mappingsImpl.value
+ )
+
+} \ No newline at end of file