From 458971f834a3af0dbf2fffe527352fa11e7d8168 Mon Sep 17 00:00:00 2001 From: Jakob Odersky Date: Tue, 16 Dec 2014 00:09:12 +0100 Subject: generate mavlink files in build --- project/Build.scala | 20 ++-- project/build.properties | 2 +- .../scala/com/github/jodersky/mavlink/Main.scala | 41 +++++++ .../com/github/jodersky/mavlink/parsing/Crc.scala | 24 ++++ .../jodersky/mavlink/parsing/FieldTypes.scala | 27 +++++ .../com/github/jodersky/mavlink/parsing/Name.scala | 14 +++ .../github/jodersky/mavlink/parsing/parsing.scala | 85 ++++++++++++++ .../src/main/twirl/org/mavlink/Crc.scala.txt | 23 ++++ .../src/main/twirl/org/mavlink/Packet.scala.txt | 59 ++++++++++ .../src/main/twirl/org/mavlink/Parser.scala.txt | 128 +++++++++++++++++++++ .../src/main/twirl/org/mavlink/_header.scala.txt | 5 + .../twirl/org/mavlink/messages/Message.scala.txt | 70 +++++++++++ .../org/mavlink/messages/_message_class.scala.txt | 14 +++ .../scala/com/github/jodersky/sbt/SbtMavlink.scala | 24 ++++ .../github/jodersky/sbt/mavlink/MavlinkKeys.scala | 14 +++ project/plugins.sbt | 7 -- project/project/BuildBuild.scala | 35 ++++++ project/project/plugins.sbt | 1 + 18 files changed, 572 insertions(+), 21 deletions(-) create mode 100644 project/mavlink-library/src/main/scala/com/github/jodersky/mavlink/Main.scala create mode 100644 project/mavlink-library/src/main/scala/com/github/jodersky/mavlink/parsing/Crc.scala create mode 100644 project/mavlink-library/src/main/scala/com/github/jodersky/mavlink/parsing/FieldTypes.scala create mode 100644 project/mavlink-library/src/main/scala/com/github/jodersky/mavlink/parsing/Name.scala create mode 100644 project/mavlink-library/src/main/scala/com/github/jodersky/mavlink/parsing/parsing.scala create mode 100644 project/mavlink-library/src/main/twirl/org/mavlink/Crc.scala.txt create mode 100644 project/mavlink-library/src/main/twirl/org/mavlink/Packet.scala.txt create mode 100644 project/mavlink-library/src/main/twirl/org/mavlink/Parser.scala.txt create mode 100644 project/mavlink-library/src/main/twirl/org/mavlink/_header.scala.txt create mode 100644 project/mavlink-library/src/main/twirl/org/mavlink/messages/Message.scala.txt create mode 100644 project/mavlink-library/src/main/twirl/org/mavlink/messages/_message_class.scala.txt create mode 100644 project/mavlink-plugin/src/main/scala/com/github/jodersky/sbt/SbtMavlink.scala create mode 100644 project/mavlink-plugin/src/main/scala/com/github/jodersky/sbt/mavlink/MavlinkKeys.scala delete mode 100644 project/plugins.sbt create mode 100644 project/project/BuildBuild.scala create mode 100644 project/project/plugins.sbt (limited to 'project') diff --git a/project/Build.scala b/project/Build.scala index 2728c48..4ca4b3b 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -7,18 +7,16 @@ import scala.scalajs.sbtplugin.ScalaJSPlugin import scala.scalajs.sbtplugin.ScalaJSPlugin.ScalaJSKeys._ import Dependencies._ +import com.github.jodersky.sbt.mavlink.MavlinkKeys._ +import com.github.jodersky.sbt.SbtMavlink object ApplicationBuild extends Build { //settings common to all projects val common = Seq( scalaVersion := "2.11.4", - scalacOptions ++= Seq("-feature", "-deprecation") - ) - - //settings for js/jvm projects using shared sources - val shared = Seq( - unmanagedSourceDirectories in Compile += (scalaSource in (mavlink, Compile)).value + scalacOptions ++= Seq("-feature", "-deprecation"), + mavlinkDialect := file(".") / "concise.xml" ) lazy val root = Project("root", file(".")).aggregate( @@ -27,15 +25,10 @@ object ApplicationBuild extends Build { frontend ) - lazy val mavlink = ( - Project("vfd-mavlink", file("vfd-mavlink")) - settings(common: _*) - ) - lazy val uav = ( Project("vfd-uav", file("vfd-uav")) + enablePlugins(SbtMavlink) settings(common: _*) - settings(shared: _*) settings( libraryDependencies ++= Seq( akkaActor, @@ -48,6 +41,7 @@ object ApplicationBuild extends Build { lazy val backend = ( Project("vfd-backend", file("vfd-backend")) enablePlugins(PlayScala) + enablePlugins(SbtMavlink) settings(common: _*) settings( resolvers += Resolver.url("scala-js-releases", url("http://dl.bintray.com/content/scala-js/scala-js-releases"))(Resolver.ivyStylePatterns), @@ -64,8 +58,8 @@ object ApplicationBuild extends Build { lazy val frontend = ( Project("vfd-frontend", file("vfd-frontend")) settings(ScalaJSPlugin.scalaJSSettings: _*) + enablePlugins(SbtMavlink) settings(common: _*) - settings(shared: _*) settings( libraryDependencies ++= Seq( rx, diff --git a/project/build.properties b/project/build.properties index be6c454..748703f 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=0.13.5 +sbt.version=0.13.7 diff --git a/project/mavlink-library/src/main/scala/com/github/jodersky/mavlink/Main.scala b/project/mavlink-library/src/main/scala/com/github/jodersky/mavlink/Main.scala new file mode 100644 index 0000000..5f77c1a --- /dev/null +++ b/project/mavlink-library/src/main/scala/com/github/jodersky/mavlink/Main.scala @@ -0,0 +1,41 @@ +package com.github.jodersky.mavlink + +import scala.xml.XML +import com.github.jodersky.mavlink.parsing.Protocol +import java.io.FileWriter +import java.io.BufferedWriter +import scalax.file.Path +import java.io.File + +object Main { + + def prettify(str: String) = str.replaceAll("(\\s*\n)(\\s*\n)+", "\n\n") + + private def processTemplates(protocol: Protocol, rootOut: Path) = { + val root = rootOut / Path.fromString("org/mavlink") + val mappings: List[(String, Path)] = List( + org.mavlink.txt.Crc().body -> Path("Crc.scala"), + org.mavlink.txt.Packet(protocol.messages).body -> Path("Packet.scala"), + org.mavlink.txt.Parser().body -> Path("Parser.scala"), + org.mavlink.messages.txt.Message(protocol.messages).body -> Path.fromString("messages/messages.scala") + ) + + for ((str, file) <- mappings) yield { + val out = root / file + out.createFile(true, false) + out.write(prettify(str)) + out + } + } + + def run(dialect: File, out: File) = { + val xml = XML.loadFile(dialect) + val protocol = Protocol.parse(xml) + processTemplates(protocol, Path(out)).map(_.fileOption.get) + } + + def main(args: Array[String]): Unit = { + run(new File(args(0)), new File(args(1))) + } + +} \ No newline at end of file diff --git a/project/mavlink-library/src/main/scala/com/github/jodersky/mavlink/parsing/Crc.scala b/project/mavlink-library/src/main/scala/com/github/jodersky/mavlink/parsing/Crc.scala new file mode 100644 index 0000000..c54b7e9 --- /dev/null +++ b/project/mavlink-library/src/main/scala/com/github/jodersky/mavlink/parsing/Crc.scala @@ -0,0 +1,24 @@ +package com.github.jodersky.mavlink.parsing + +case class Crc(val crc: Int = 0xffff) extends AnyVal { + + def accumulate(datum: Byte): Crc = { + val d = datum & 0xff + var tmp = d ^ (crc & 0xff) + tmp ^= (tmp << 4) & 0xff; + Crc( + ((crc >> 8) & 0xff) ^ (tmp << 8) ^ (tmp << 3) ^ ((tmp >> 4) & 0xff)) + } + + def accumulate(data: Seq[Byte]): Crc = { + var next = this + for (d <- data) { + next = next.accumulate(d) + } + next + } + + def lsb: Byte = crc.toByte + def msb: Byte = (crc >> 8).toByte + +} \ No newline at end of file diff --git a/project/mavlink-library/src/main/scala/com/github/jodersky/mavlink/parsing/FieldTypes.scala b/project/mavlink-library/src/main/scala/com/github/jodersky/mavlink/parsing/FieldTypes.scala new file mode 100644 index 0000000..0cf29b4 --- /dev/null +++ b/project/mavlink-library/src/main/scala/com/github/jodersky/mavlink/parsing/FieldTypes.scala @@ -0,0 +1,27 @@ +package com.github.jodersky.mavlink.parsing + +import scala.xml.Node + +object FieldTypes { + + sealed trait Type { + def width: Int + def scalaType: String + def nativeType: String + } + + case class IntType(signed: Boolean, width: Int, scalaType: String, nativeType: String) extends Type + + def parse(xml: Node): Type = xml.text match { + case "int8_t" => IntType(true, 1, "Byte", xml.text) + case "int16_t" => IntType(true, 2, "Short", xml.text) + case "int32_t" => IntType(true, 4, "Int", xml.text) + case "int64_t" => IntType(true, 8, "Long", xml.text) + case "uint8_t" => IntType(false, 1, "Byte", xml.text) + case "uint16_t" => IntType(false, 2, "Short", xml.text) + case "uint32_t" => IntType(false, 4, "Int", xml.text) + case "uint64_t" => IntType(false, 8, "Long", xml.text) + case _ => Parser.fatal("unsupported type \"" + xml.text + "\"", xml) + } + +} \ No newline at end of file diff --git a/project/mavlink-library/src/main/scala/com/github/jodersky/mavlink/parsing/Name.scala b/project/mavlink-library/src/main/scala/com/github/jodersky/mavlink/parsing/Name.scala new file mode 100644 index 0000000..6f5b6a7 --- /dev/null +++ b/project/mavlink-library/src/main/scala/com/github/jodersky/mavlink/parsing/Name.scala @@ -0,0 +1,14 @@ +package com.github.jodersky.mavlink.parsing + +object Name { + + def words(raw: String) = raw.split("\\s+|_") + + def className(raw: String): String = words(raw.toLowerCase()).map(_.capitalize).mkString("") + + def varName(raw: String) = { + val (head, tail) = className(raw).splitAt(1) + head.toLowerCase() + tail + } + +} \ No newline at end of file diff --git a/project/mavlink-library/src/main/scala/com/github/jodersky/mavlink/parsing/parsing.scala b/project/mavlink-library/src/main/scala/com/github/jodersky/mavlink/parsing/parsing.scala new file mode 100644 index 0000000..e14e0c3 --- /dev/null +++ b/project/mavlink-library/src/main/scala/com/github/jodersky/mavlink/parsing/parsing.scala @@ -0,0 +1,85 @@ +package com.github.jodersky.mavlink.parsing + +import scala.xml.Node +import scala.util.Try +import FieldTypes._ +import Parser._ +import Name._ + +object Parser { + def fatal(message: String, xml: Node) = throw new RuntimeException("Cannot parse message definition: " + message + " " + xml) +} + +case class Protocol(version: String, enums: Seq[Enum], messages: Seq[Message]) +object Protocol { + def parse(xml: Node) = { + val root = xml \\ "mavlink" + val version = (root \ "version").text + val enums = (root \ "enums" \ "_") map Enum.parse + val messages = (root \ "messages" \ "_") map Message.parse + Protocol(version, enums, messages) + } +} + +case class Enum(name: String, entries: Seq[EnumEntry]) { + def scalaName = className(name) +} +object Enum { + def parse(xml: Node) = { + val name = (xml \ "@name").headOption.map(_.text).getOrElse(fatal("no name defined", xml)) + val entries = (xml \ "entry") map EnumEntry.parse + Enum(name, entries) + } +} + +case class EnumEntry(value: Int, name: String, description: String) { + def scalaName = className(name) +} +object EnumEntry { + def parse(xml: Node) = { + val valueString = (xml \ "@value").headOption.map(_.text).getOrElse(fatal("no value defined", xml)) + val value = Try { Integer.parseInt(valueString) }.getOrElse(fatal("value must be an integer", xml)) + val name = (xml \ "@name").headOption.map(_.text).getOrElse(fatal("no name defined", xml)) + val description = (xml \ "description").text + EnumEntry(value, name, description) + } +} + +case class Message(id: Byte, name: String, description: String, fields: Seq[Field]) { + def scalaName = className(name) + def orderedFields = fields.sortBy(_.tpe.width)(Ordering[Int].reverse) + + lazy val checksum = { + var c = new Crc() + c = c.accumulate((name + " ").getBytes) + for (field <- orderedFields) { + c = c.accumulate((field.tpe.nativeType + " ").getBytes) + c = c.accumulate((field.name + " ").getBytes) + } + (c.lsb ^ c.msb).toByte + } +} +object Message { + def parse(xml: Node): Message = { + val idString = (xml \ "@id").headOption.map(_.text).getOrElse(fatal("no id defined", xml)) + val id = Try { Integer.parseInt(idString).toByte }.getOrElse(fatal("id must be an integer", xml)) + val name = (xml \ "@name").headOption.map(_.text).getOrElse(fatal("no name defined", xml)) + val description = (xml \ "description").text + val fields = (xml \ "field") map Field.parse + Message(id, name, description, fields) + } +} + +case class Field(tpe: Type, name: String, enum: Option[String], description: String) { + def scalaName = varName(name) +} +object Field { + def parse(xml: Node): Field = { + val tpeNode = (xml \ "@type").headOption.getOrElse(fatal("no type defined", xml)) + val tpe = FieldTypes.parse(tpeNode) + val name = (xml \ "@name").headOption.map(_.text).getOrElse(fatal("no name defined", xml)) + val enum = (xml \ "@enum").headOption.map(_.text) + val description = (xml).text + Field(tpe, name, enum, description) + } +} \ No newline at end of file diff --git a/project/mavlink-library/src/main/twirl/org/mavlink/Crc.scala.txt b/project/mavlink-library/src/main/twirl/org/mavlink/Crc.scala.txt new file mode 100644 index 0000000..61b5c6f --- /dev/null +++ b/project/mavlink-library/src/main/twirl/org/mavlink/Crc.scala.txt @@ -0,0 +1,23 @@ +@() + +package org.mavlink + +/** + * X.25 CRC calculation for MAVlink messages. The checksum must be initialized, + * updated with witch field of the message, and then finished with the message + * id. + */ +case class Crc(val crc: Int = 0xffff) extends AnyVal { + + def accumulate(datum: Byte): Crc = { + val d = datum & 0xff + var tmp = d ^ (crc & 0xff) + tmp ^= (tmp << 4) & 0xff; + Crc( + ((crc >> 8) & 0xff) ^ (tmp << 8) ^ (tmp << 3) ^ ((tmp >> 4) & 0xff)) + } + + def lsb: Byte = crc.toByte + def msb: Byte = (crc >> 8).toByte + +} \ No newline at end of file diff --git a/project/mavlink-library/src/main/twirl/org/mavlink/Packet.scala.txt b/project/mavlink-library/src/main/twirl/org/mavlink/Packet.scala.txt new file mode 100644 index 0000000..6f96b40 --- /dev/null +++ b/project/mavlink-library/src/main/twirl/org/mavlink/Packet.scala.txt @@ -0,0 +1,59 @@ +@(messages: Seq[parsing.Message]) + +@messageCrcs() = @{ + for (i <- 0 to 255) yield { + val crc = messages.find(_.id == i).map(_.checksum).getOrElse(0) + crc.toString + } +} + +package org.mavlink + +case class Packet( + seq: Byte, + systemId: Byte, + componentId: Byte, + messageId: Byte, + payload: Seq[Byte] +) { + + lazy val crc = { + var c = new Crc() + c = c.accumulate(payload.length.toByte) + c = c.accumulate(seq) + c = c.accumulate(systemId) + c = c.accumulate(componentId) + c = c.accumulate(messageId) + for (p <- payload) { + c = c.accumulate(p) + } + c = c.accumulate(Packet.extraCrc(messageId)) + c + } + + def toSeq: Seq[Byte] = Seq( + Packet.Stx, + payload.length.toByte, + seq, + systemId, + componentId, + messageId + ) ++ payload ++ Seq( + crc.lsb, + crc.msb + ) + +} + +object Packet { + + final val Stx: Byte = (0xfe).toByte + final val MaxPayloadLength: Int = @messages.map(_.fields.map(_.tpe.width).sum).max + + final val ExtraCrcs: Seq[Byte] = @messageCrcs().mkString("Array[Byte](", ", ", ")") + + def extraCrc(id: Byte) = ExtraCrcs(id & 0xff) + + final val Empty = Packet(0, 0, 0, -1, Array(0: Byte)) + +} \ No newline at end of file diff --git a/project/mavlink-library/src/main/twirl/org/mavlink/Parser.scala.txt b/project/mavlink-library/src/main/twirl/org/mavlink/Parser.scala.txt new file mode 100644 index 0000000..f0e30dc --- /dev/null +++ b/project/mavlink-library/src/main/twirl/org/mavlink/Parser.scala.txt @@ -0,0 +1,128 @@ +@() + +package org.mavlink + +import scala.collection.mutable.ArrayBuffer + +object Parser { + + object ParseStates { + sealed trait State + case object Idle extends State + case object GotStx extends State + case object GotLength extends State + case object GotSeq extends State + case object GotSysId extends State + case object GotCompId extends State + case object GotMsgId extends State + case object GotCrc1 extends State + case object GotPayload extends State + } + + object ParseErrors { + sealed trait ParseError + case object CrcError extends ParseError + case object OverflowError extends ParseError + } +} + +class Parser(receiver: Packet => Unit, error: Parser.ParseErrors.ParseError => Unit = _ => ()) { + import Parser._ + + private var state: ParseStates.State = ParseStates.Idle + + private object inbound { + var length: Int = 0 + var seq: Byte = 0 + var systemId: Byte = 0 + var componentId: Byte = 0 + var messageId: Byte = 0 + var payload = new ArrayBuffer[Byte] + var crc: Crc = new Crc() + } + + def push(c: Byte): Unit = { + import ParseStates._ + + state match { + case Idle => + if (c == Packet.Stx) { + state = GotStx + } + + case GotStx => + inbound.crc = new Crc() + inbound.length = (c & 0xff) + inbound.crc = inbound.crc.accumulate(c) + state = GotLength + + case GotLength => + inbound.seq = c; + inbound.crc = inbound.crc.accumulate(c) + state = GotSeq + + case GotSeq => + inbound.systemId = c + inbound.crc = inbound.crc.accumulate(c) + state = GotSysId + + case GotSysId => + inbound.componentId = c + inbound.crc = inbound.crc.accumulate(c) + state = GotCompId + + case GotCompId => + inbound.messageId = c + inbound.crc = inbound.crc.accumulate(c) + if (inbound.length == 0) { + state = GotPayload + } else { + state = GotMsgId + inbound.payload.clear() + } + + case GotMsgId => + inbound.payload += c + inbound.crc = inbound.crc.accumulate(c) + if(inbound.payload.length >= Packet.MaxPayloadLength) { + state = Idle + error(ParseErrors.OverflowError) + } + if (inbound.payload.length >= inbound.length) { + state = GotPayload + } + + case GotPayload => + inbound.crc = inbound.crc.accumulate(Packet.extraCrc(inbound.messageId)) + if (c != inbound.crc.lsb) { + state = Idle + if (c == Packet.Stx) { + state = GotStx + } + error(ParseErrors.CrcError) + } else { + state = GotCrc1 + } + + case GotCrc1 => + if (c != inbound.crc.msb) { + state = Idle + if (c == Packet.Stx) { + state = GotStx + } + error(ParseErrors.CrcError) + } else { + val packet = Packet( + inbound.seq, + inbound.systemId, + inbound.componentId, + inbound.messageId, + inbound.payload) + state = Idle + inbound.payload = new ArrayBuffer[Byte]() + receiver(packet) + } + } + } + +} diff --git a/project/mavlink-library/src/main/twirl/org/mavlink/_header.scala.txt b/project/mavlink-library/src/main/twirl/org/mavlink/_header.scala.txt new file mode 100644 index 0000000..77ad6d2 --- /dev/null +++ b/project/mavlink-library/src/main/twirl/org/mavlink/_header.scala.txt @@ -0,0 +1,5 @@ +@() + +/** + * This file has been machine generated. + */ \ No newline at end of file diff --git a/project/mavlink-library/src/main/twirl/org/mavlink/messages/Message.scala.txt b/project/mavlink-library/src/main/twirl/org/mavlink/messages/Message.scala.txt new file mode 100644 index 0000000..1507df4 --- /dev/null +++ b/project/mavlink-library/src/main/twirl/org/mavlink/messages/Message.scala.txt @@ -0,0 +1,70 @@ +@(messages: Seq[parsing.Message]) + +@extract(field: parsing.Field, offset: Int) = { + @field.tpe.width match { + case 1 => {payload(@offset)} + case 2 => {((payload(@offset)) | ((payload(@{offset+1}) & 0xff) << 8)).toShort} + case 4 => {(payload(@offset)) | ((payload(@{offset+1}) & 0xff) << 8) | ((payload(@{offset+2}) & 0xff) << 16) | ((payload(@{offset+3}) & 0xff) << 24)} + case 8 => {payload(@offset) | + ((payload(@{offset+2}) & 0xffl) << 8) | + ((payload(@{offset+3}) & 0xffl) << 16) | + ((payload(@{offset+4}) & 0xffl) << 24) | + ((payload(@{offset+5}) & 0xffl) << 32) | + ((payload(@{offset+6}) & 0xffl) << 40) | + ((payload(@{offset+7}) & 0xffl) << 48) | + ((payload(@{offset+8}) & 0xffl) << 56)} + } +} + +@insert(field: parsing.Field, offset: Int) = { + @defining("m." + field.scalaName) { attr => + @if(field.tpe.width >= 1) { arr(@offset) = (@attr & 0xff).toByte } + @if(field.tpe.width >= 2) { arr(@{offset+1}) = ((@attr >> 8) & 0xff).toByte } + @if(field.tpe.width >= 3) { arr(@{offset+2}) = ((@attr >> 16) & 0xff).toByte } + @if(field.tpe.width >= 4) { arr(@{offset+3}) = ((@attr >> 24) & 0xff).toByte } + @if(field.tpe.width >= 5) { arr(@{offset+4}) = ((@attr >> 32) & 0xff).toByte } + @if(field.tpe.width >= 6) { arr(@{offset+5}) = ((@attr >> 40) & 0xff).toByte } + @if(field.tpe.width >= 7) { arr(@{offset+6}) = ((@attr >> 48) & 0xff).toByte } + @if(field.tpe.width >= 8) { arr(@{offset+7}) = ((@attr >> 56) & 0xff).toByte } + } +} + +@org.mavlink.txt._header() + +package org.mavlink.messages + +sealed trait Message +@for(message <- messages) { + @_message_class(message) +} +case class Unknown(id: Byte, payload: Seq[Byte]) extends Message + +object Message { + def unpack(id: Byte, payload: Seq[Byte]) = id match { + @for(message <- messages) { + case @message.id => + @defining(message.orderedFields){ ordered => + @defining(ordered.map(_.tpe.width).scanLeft(0)(_ + _)){ offsets => + @for((field, offset) <- ordered zip offsets) { + val @field.scalaName: @field.tpe.scalaType = @extract(field, offset) + }}} + @{message.scalaName}@message.fields.map(_.scalaName).mkString("(", ", ", ")") + } + + case id => Unknown(id, payload) + } + + def pack(message: Message): (Byte, Seq[Byte]) = message match { + @for(message <- messages) { + case m: @message.scalaName => + val arr = new Array[Byte](@message.fields.map(_.tpe.width).sum) + @defining(message.orderedFields) { ordered => + @defining(ordered.map(_.tpe.width).scanLeft(0)(_ + _)){ offsets => + @for((field, offset) <- ordered zip offsets) { + @insert(field, offset) + } + (@message.id, arr) + }}} + case u: Unknown => (u.id, u.payload) + } +} \ No newline at end of file diff --git a/project/mavlink-library/src/main/twirl/org/mavlink/messages/_message_class.scala.txt b/project/mavlink-library/src/main/twirl/org/mavlink/messages/_message_class.scala.txt new file mode 100644 index 0000000..78c8326 --- /dev/null +++ b/project/mavlink-library/src/main/twirl/org/mavlink/messages/_message_class.scala.txt @@ -0,0 +1,14 @@ +@(message: parsing.Message) + +@arguments(fields: Seq[parsing.Field]) = @{ + fields.map(f => f.scalaName + ": " + f.tpe.scalaType) +} + +@comment(paragraphs: Seq[String]) = @{ + paragraphs.mkString("/**\n * ", "\n * ", "\n */") +} + +@defining(message.description.grouped(120).toSeq){intro => +@defining(message.fields.map(field => "@param " + field.name + " " + field.description)){ fields => +@comment(intro ++ fields)}} +case class @{message.scalaName}@arguments(message.fields).mkString("(", ", ", ")") extends Message \ No newline at end of file diff --git a/project/mavlink-plugin/src/main/scala/com/github/jodersky/sbt/SbtMavlink.scala b/project/mavlink-plugin/src/main/scala/com/github/jodersky/sbt/SbtMavlink.scala new file mode 100644 index 0000000..58692cd --- /dev/null +++ b/project/mavlink-plugin/src/main/scala/com/github/jodersky/sbt/SbtMavlink.scala @@ -0,0 +1,24 @@ +package com.github.jodersky.sbt + +import sbt._ +import Keys._ +import plugins._ +import mavlink.MavlinkKeys._ +import sbt.Project.Initialize +import com.github.jodersky.mavlink.Main + +object SbtMavlink extends AutoPlugin { + + override def requires = JvmPlugin + + lazy val generate = Def.task[Seq[File]] { + streams.value.log.info("Generating mavlink files...") + Main.run((mavlinkDialect in Compile).value, (mavlinkTarget in Compile).value).map(_.getAbsoluteFile) + } + + override val projectSettings: Seq[Setting[_]] = Seq( + mavlinkTarget in Compile := (sourceManaged in Compile).value, + mavlinkGenerate in Compile := generate.value, + sourceGenerators in Compile += (mavlinkGenerate in Compile).taskValue + ) +} diff --git a/project/mavlink-plugin/src/main/scala/com/github/jodersky/sbt/mavlink/MavlinkKeys.scala b/project/mavlink-plugin/src/main/scala/com/github/jodersky/sbt/mavlink/MavlinkKeys.scala new file mode 100644 index 0000000..5cbffd4 --- /dev/null +++ b/project/mavlink-plugin/src/main/scala/com/github/jodersky/sbt/mavlink/MavlinkKeys.scala @@ -0,0 +1,14 @@ +package com.github.jodersky.sbt +package mavlink + +import sbt._ +import sbt.Keys._ +import java.io.File + +object MavlinkKeys { + + lazy val mavlinkDialect = settingKey[File]("Dialect definition from which to generate files.") + lazy val mavlinkTarget = settingKey[File]("Target directory to contain all generated mavlink files.") + lazy val mavlinkGenerate = taskKey[Seq[File]]("Generate mavlink files.") + +} \ No newline at end of file diff --git a/project/plugins.sbt b/project/plugins.sbt deleted file mode 100644 index 2c768c0..0000000 --- a/project/plugins.sbt +++ /dev/null @@ -1,7 +0,0 @@ -resolvers += "Typesafe repository" at "http://repo.typesafe.com/typesafe/releases/" - -// The Play plugin -addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.3.4") - -// Scala.js plugin -addSbtPlugin("org.scala-lang.modules.scalajs" % "scalajs-sbt-plugin" % "0.5.5") \ No newline at end of file diff --git a/project/project/BuildBuild.scala b/project/project/BuildBuild.scala new file mode 100644 index 0000000..201507e --- /dev/null +++ b/project/project/BuildBuild.scala @@ -0,0 +1,35 @@ +import sbt._ +import sbt.Keys._ +import play.twirl.sbt.SbtTwirl +import play.twirl.sbt.Import._ + +object BuildBuild extends Build { + + lazy val root = ( + Project("root", file(".")) + settings( + addSbtPlugin("com.typesafe.play" %% "sbt-plugin" % "2.3.4"), + addSbtPlugin("org.scala-lang.modules.scalajs" % "scalajs-sbt-plugin" % "0.5.5") + ) + dependsOn(mavlinkPlugin) + ) + + lazy val mavlinkLibrary = ( + Project("mavlink-library", file("mavlink-library")) + enablePlugins(SbtTwirl) + settings( + libraryDependencies += "com.github.scala-incubator.io" %% "scala-io-file" % "0.4.2", + TwirlKeys.templateImports += "com.github.jodersky.mavlink._" + ) + ) + + lazy val mavlinkPlugin = ( + Project("mavlink-plugin", file("mavlink-plugin")) + settings( + sbtPlugin := true + ) + dependsOn(mavlinkLibrary) + ) + +} + diff --git a/project/project/plugins.sbt b/project/project/plugins.sbt new file mode 100644 index 0000000..7b458b6 --- /dev/null +++ b/project/project/plugins.sbt @@ -0,0 +1 @@ +addSbtPlugin("com.typesafe.sbt" % "sbt-twirl" % "1.0.4") \ No newline at end of file -- cgit v1.2.3