diff --git a/.travis.yml b/.travis.yml index d5ee2f6..11c7e3d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,4 @@ +dist: trusty sudo: false language: scala jdk: diff --git a/build.sbt b/build.sbt index 21e13a6..04c96cc 100644 --- a/build.sbt +++ b/build.sbt @@ -23,8 +23,6 @@ publishArtifact in Test := false pomIncludeRepository := { _ => false } -libraryDependencies += "org.bytedeco" % "javacpp" % "1.4.3" - scalacOptions ++= Seq("-unchecked", "-deprecation", "-feature", "-Xlint", "-Xlog-free-terms") publishTo := { diff --git a/src/main/scala/org/bytedeco/sbt/javacpp/JavaCppPlugin.scala b/src/main/scala/org/bytedeco/sbt/javacpp/JavaCppPlugin.scala new file mode 100644 index 0000000..af15040 --- /dev/null +++ b/src/main/scala/org/bytedeco/sbt/javacpp/JavaCppPlugin.scala @@ -0,0 +1,140 @@ +package org.bytedeco.sbt.javacpp + +import java.net.URLClassLoader +import java.util.Properties + +import sbt.Keys._ +import sbt._ + +import scala.language.{ postfixOps, reflectiveCalls } +import scala.util.Try + +object JavaCppPlugin extends AutoPlugin { + + override def projectSettings: Seq[Setting[_]] = { + import autoImport._ + Seq( + autoCompilerPlugins := true, + javaCppClasses := Seq.empty, + javaCppCustomizer := identity, + javaCppPlatform := Platform.current, + javaCppPresetLibs := Seq.empty, + javaCppVersion := Versions.javaCppVersion, + libraryDependencies += { + "org.bytedeco" % "javacpp" % javaCppVersion.value jar + }, + javaCppPresetDependencies, + javaCppBuild := javaCpp.value, + products in Compile := (products in Compile).dependsOn(javaCppBuild).value) + } + + object Versions { + val javaCppVersion = "1.4.3" + } + + object autoImport { + type JavaCppBuilder = { + def classPaths(classPath: String): this.type + def classPaths(classPath: Array[String]): this.type + def encoding(encoding: String): this.type + def outputDirectory(outputDirectory: String): this.type + def outputDirectory(outputDirectory: File): this.type + def clean(clean: Boolean): this.type + def generate(generate: Boolean): this.type + def compile(compile: Boolean): this.type + def deleteJniFiles(deleteJniFiles: Boolean): this.type + def header(header: Boolean): this.type + def copyLibs(copyLibs: Boolean): this.type + def copyResources(copyResources: Boolean): this.type + def outputName(outputName: String): this.type + def jarPrefix(jarPrefix: String): this.type + def properties(platform: String): this.type + def properties(properties: Properties): this.type + def propertyFile(propertyFile: String): this.type + def propertyFile(propertyFile: File): this.type + def property(keyValue: String): this.type + def property(key: String, value: String): this.type + def classesOrPackages(classes: Array[String]): this.type + def buildCommand(buildCommand: Array[String]): this.type + def workingDirectory(workingDirectory: String): this.type + def workingDirectory(workingDirectory: File): this.type + def environmentVariables(environmentVariables: java.util.Map[String, String]): this.type + def compilerOptions(options: Array[String]): this.type + def build(): Array[File] + def printHelp(): Unit + def getClass(): Class[_] + } + + val javaCppBuild = TaskKey[Seq[File]]("javaCppBuild", "Build Java CPP products") + val javaCppClasses = SettingKey[Seq[String]]("javaCppClasses", "A list of Java CPP classes. Suffix of '.*' ('.**') can be used to match all classes under the specified package (and any subpackages)") + val javaCppCustomizer = SettingKey[JavaCppBuilder => JavaCppBuilder]("javaCppCustomization", "Customize the Java CPP builder") + val javaCppPlatform = SettingKey[Seq[String]]("javaCppPlatform", """The platform that you want to compile for (defaults to the platform of the current computer). You can also set this via the "sbt.javacpp.platform" System Property """) + val javaCppPresetLibs = SettingKey[Seq[(String, String)]]("javaCppPresetLibs", "List of additional JavaCPP presets that you would wish to bind lazily, defaults to an empty list") + val javaCppVersion = SettingKey[String]("javaCppVersion", s"Version of Java CPP that you want to use, defaults to ${Versions.javaCppVersion}") + } + + override def requires: Plugins = plugins.JvmPlugin + + override def trigger: PluginTrigger = allRequirements + + private def javaCppPresetDependencies: Def.Setting[Seq[ModuleID]] = { + import autoImport._ + libraryDependencies ++= { + lazy val cppPresetVersion = buildPresetVersion(javaCppVersion.value) + javaCppPresetLibs.value.flatMap { + case (libName, libVersion) => + val generic = "org.bytedeco.javacpp-presets" % libName % s"$libVersion-$cppPresetVersion" classifier "" + val platformSpecific = javaCppPlatform.value.map { platform => + "org.bytedeco.javacpp-presets" % libName % s"$libVersion-$cppPresetVersion" classifier platform + } + generic +: platformSpecific + } + } + } + + /** + * Before javacpp 1.4 + * Given a version string, simply drops the patch level and returns the major-minor version only + * + * Starting from javacpp 1.4 + * The version number of the presets are equal to the javacpp version. + * + * @param version eg. "1.4.2" + */ + private def buildPresetVersion(version: String): String = + version match { + case VersionSplit(a :: b :: _) if a < 2 & b < 4 => s"$a.$b" + case VersionSplit(_) => version + case _ => throw new IllegalArgumentException("Version format not recognized") + } + + private object VersionSplit { + def unapply(arg: String): Option[List[Int]] = + Try(arg.split('.').map(_.toInt).toList).toOption + } + + private def javaCpp = Def.task { + import autoImport._ + val classes = javaCppClasses.value + val customizer = javaCppCustomizer.value + val dependencies = (dependencyClasspath in Compile).value + val output = (classDirectory in Compile).value + val _ = (compile in Compile).value + + val cl = new URLClassLoader(dependencies.map(_.data.toURI.toURL).toArray, null) + val thread = Thread.currentThread() + thread.setContextClassLoader(cl) + val builder = cl.loadClass("org.bytedeco.javacpp.tools.Builder").newInstance().asInstanceOf[JavaCppBuilder] + val saved = thread.getContextClassLoader + + try { + customizer( + builder + .classPaths(output.getAbsolutePath) + .classPaths(dependencies.map(_.data.absolutePath).toArray) + .classesOrPackages(classes.toArray)).build() + } finally { + thread.setContextClassLoader(saved) + } + } +} diff --git a/src/main/scala/org/bytedeco/sbt/javacpp/Platform.scala b/src/main/scala/org/bytedeco/sbt/javacpp/Platform.scala index bfbd0a0..3536421 100644 --- a/src/main/scala/org/bytedeco/sbt/javacpp/Platform.scala +++ b/src/main/scala/org/bytedeco/sbt/javacpp/Platform.scala @@ -1,11 +1,8 @@ package org.bytedeco.sbt.javacpp -import org.bytedeco.javacpp.Loader - /** * Created by Lloyd on 2/22/16. */ - object Platform { private val platformOverridePropertyKey: String = "sbt.javacpp.platform" @@ -20,7 +17,28 @@ object Platform { */ val current: Seq[String] = sys.props.get(platformOverridePropertyKey) match { case Some(platform) if platform.trim().nonEmpty => platform.split(' ') - case _ => Seq(Loader.getPlatform) + case _ => + val jvmName = System.getProperty("java.vm.name", "").toLowerCase + var osName = System.getProperty("os.name", "").toLowerCase + var osArch = System.getProperty("os.arch", "").toLowerCase + val abiType = System.getProperty("sun.arch.abi", "").toLowerCase + val libPath = System.getProperty("sun.boot.library.path", "").toLowerCase + if (jvmName.startsWith("dalvik") && osName.startsWith("linux")) { + osName = "android" + } else if (jvmName.startsWith("robovm") && osName.startsWith("darwin")) { + osName = "ios" + osArch = "arm" + } else if (osName.startsWith("mac os x") || osName.startsWith("darwin")) { + osName = "macosx" + } else { + val spaceIndex = osName.indexOf(' ') + if (spaceIndex > 0) osName = osName.substring(0, spaceIndex) + } + if (osArch == "i386" || osArch == "i486" || osArch == "i586" || osArch == "i686") osArch = "x86" + else if (osArch == "amd64" || osArch == "x86-64" || osArch == "x64") osArch = "x86_64" + else if (osArch.startsWith("aarch64") || osArch.startsWith("armv8") || osArch.startsWith("arm64")) osArch = "arm64" + else if (osArch.startsWith("arm") && ((abiType == "gnueabihf") || libPath.contains("openjdk-armhf"))) osArch = "armhf" + else if (osArch.startsWith("arm")) osArch = "arm" + Seq(osName + "-" + osArch) } - -} \ No newline at end of file +} diff --git a/src/main/scala/org/bytedeco/sbt/javacpp/Plugin.scala b/src/main/scala/org/bytedeco/sbt/javacpp/Plugin.scala deleted file mode 100644 index edf80df..0000000 --- a/src/main/scala/org/bytedeco/sbt/javacpp/Plugin.scala +++ /dev/null @@ -1,74 +0,0 @@ -package org.bytedeco.sbt.javacpp - -import scala.language.postfixOps -import sbt._ -import sbt.Keys._ - -import scala.util.Try - -object Plugin extends AutoPlugin { - - override def projectSettings: Seq[Setting[_]] = { - import autoImport._ - Seq( - autoCompilerPlugins := true, - javaCppPlatform := Platform.current, - javaCppVersion := Versions.javaCppVersion, - javaCppPresetLibs := Seq.empty, - libraryDependencies += { - "org.bytedeco" % "javacpp" % javaCppVersion.value jar - }, - javaCppPresetDependencies) - } - - object Versions { - val javaCppVersion = "1.4.3" - } - - object autoImport { - val javaCppPlatform = SettingKey[Seq[String]]("javaCppPlatform", """The platform that you want to compile for (defaults to the platform of the current computer). You can also set this via the "sbt.javacpp.platform" System Property """) - val javaCppVersion = SettingKey[String]("javaCppVersion", s"Version of Java CPP that you want to use, defaults to ${Versions.javaCppVersion}") - val javaCppPresetLibs = SettingKey[Seq[(String, String)]]("javaCppPresetLibs", "List of additional JavaCPP presets that you would wish to bind lazily, defaults to an empty list") - } - - override def requires: Plugins = plugins.JvmPlugin - - override def trigger: PluginTrigger = allRequirements - - private def javaCppPresetDependencies: Def.Setting[Seq[ModuleID]] = { - import autoImport._ - libraryDependencies ++= { - val cppPresetVersion = buildPresetVersion(javaCppVersion.value) - javaCppPresetLibs.value.flatMap { - case (libName, libVersion) => - val generic = "org.bytedeco.javacpp-presets" % libName % s"$libVersion-$cppPresetVersion" classifier "" - val platformSpecific = javaCppPlatform.value.map { platform => - "org.bytedeco.javacpp-presets" % libName % s"$libVersion-$cppPresetVersion" classifier platform - } - generic +: platformSpecific - } - } - } - - /** - * Before javacpp 1.4 - * Given a version string, simply drops the patch level and returns the major-minor version only - * - * Starting from javacpp 1.4 - * The version number of the presets are equal to the javacpp version. - * - * @param version eg. "1.4.2" - */ - private def buildPresetVersion(version: String): String = - version match { - case VersionSplit(a :: b :: _) if a < 2 & b < 4 => s"$a.$b" - case VersionSplit(_) => version - case _ => throw new IllegalArgumentException("Version format not recognized") - } - - private object VersionSplit { - def unapply(arg: String): Option[List[Int]] = - Try(arg.split('.').map(_.toInt).toList).toOption - } - -} \ No newline at end of file diff --git a/src/sbt-test/sbt-javacpp/simple/build.sbt b/src/sbt-test/sbt-javacpp/simple/build.sbt index c37a302..ad57313 100644 --- a/src/sbt-test/sbt-javacpp/simple/build.sbt +++ b/src/sbt-test/sbt-javacpp/simple/build.sbt @@ -2,4 +2,10 @@ version := "0.1" scalaVersion := "2.11.12" -libraryDependencies += "com.novocode" % "junit-interface" % "0.11" % "test" \ No newline at end of file +javaCppClasses := Seq("javacpp.*") + +javaCppVersion := "1.5.1" + +javaCppCustomizer := { builder => builder.compilerOptions(Array("-std=c++11")) } + +libraryDependencies += "org.scalatest" %% "scalatest" % "3.0.8" % Test diff --git a/src/sbt-test/sbt-javacpp/simple/src/main/java/javacpp/PointerVectorVector.java b/src/sbt-test/sbt-javacpp/simple/src/main/java/javacpp/PointerVectorVector.java new file mode 100644 index 0000000..82c1f8a --- /dev/null +++ b/src/sbt-test/sbt-javacpp/simple/src/main/java/javacpp/PointerVectorVector.java @@ -0,0 +1,31 @@ +package javacpp; + +import org.bytedeco.javacpp.*; +import org.bytedeco.javacpp.annotation.*; + +@Platform(include="") +@Name("std::vector >") +public class PointerVectorVector extends Pointer { + static { Loader.load(); } + public PointerVectorVector() { allocate(); } + public PointerVectorVector(long n) { allocate(n); } + public PointerVectorVector(Pointer p) { super(p); } // this = (vector >*)p + private native void allocate(); // this = new vector >() + private native void allocate(long n); // this = new vector >(n) + @Name("operator=") + public native @ByRef PointerVectorVector put(@ByRef PointerVectorVector x); + + @Name("operator[]") + public native @StdVector PointerPointer get(long n); + public native @StdVector PointerPointer at(long n); + + public native long size(); + public native @Cast("bool") boolean empty(); + public native void resize(long n); + public native @Index long size(long i); // return (*this)[i].size() + public native @Index @Cast("bool") boolean empty(long i); // return (*this)[i].empty() + public native @Index void resize(long i, long n); // (*this)[i].resize(n) + + public native @Index Pointer get(long i, long j); // return (*this)[i][j] + public native void put(long i, long j, Pointer p); // (*this)[i][j] = p +} diff --git a/src/sbt-test/sbt-javacpp/simple/src/main/scala/Main.scala b/src/sbt-test/sbt-javacpp/simple/src/main/scala/Main.scala deleted file mode 100644 index be54157..0000000 --- a/src/sbt-test/sbt-javacpp/simple/src/main/scala/Main.scala +++ /dev/null @@ -1,3 +0,0 @@ -object Main extends App { - println("hello") -} \ No newline at end of file diff --git a/src/sbt-test/sbt-javacpp/simple/src/test/java/javacpp/VectorTest.java b/src/sbt-test/sbt-javacpp/simple/src/test/java/javacpp/VectorTest.java deleted file mode 100644 index aec1635..0000000 --- a/src/sbt-test/sbt-javacpp/simple/src/test/java/javacpp/VectorTest.java +++ /dev/null @@ -1,54 +0,0 @@ -package javacpp; - -import java.io.File; -import org.bytedeco.javacpp.*; -import org.bytedeco.javacpp.annotation.*; -import org.bytedeco.javacpp.tools.Builder; -import org.junit.Test; -import static org.junit.Assert.*; -import org.junit.BeforeClass; - -@Platform(include="") -public class VectorTest { - - @BeforeClass public static void setUpClass() throws Exception { - Class c = VectorTest.class; - Builder builder = new Builder().classesOrPackages(c.getName()); - File[] outputFiles = builder.build(); - Loader.load(c); - } - - @Name("std::vector >") - public static class PointerVectorVector extends Pointer { - static { Loader.load(); } - public PointerVectorVector() { allocate(); } - public PointerVectorVector(long n) { allocate(n); } - public PointerVectorVector(Pointer p) { super(p); } // this = (vector >*)p - private native void allocate(); // this = new vector >() - private native void allocate(long n); // this = new vector >(n) - @Name("operator=") - public native @ByRef PointerVectorVector put(@ByRef PointerVectorVector x); - - @Name("operator[]") - public native @StdVector PointerPointer get(long n); - public native @StdVector PointerPointer at(long n); - - public native long size(); - public native @Cast("bool") boolean empty(); - public native void resize(long n); - public native @Index long size(long i); // return (*this)[i].size() - public native @Index @Cast("bool") boolean empty(long i); // return (*this)[i].empty() - public native @Index void resize(long i, long n); // (*this)[i].resize(n) - - public native @Index Pointer get(long i, long j); // return (*this)[i][j] - public native void put(long i, long j, Pointer p); // (*this)[i][j] = p - } - - @Test public void testPointerVectorVector() { - PointerVectorVector v = new PointerVectorVector(13); - v.resize(0, 42); // v[0].resize(42) - Pointer p = new Pointer() { { address = 0xDEADBEEFL; } }; - v.put(0, 0, p); // v[0][0] = p - assertTrue(!v.empty()); - } -} \ No newline at end of file diff --git a/src/sbt-test/sbt-javacpp/simple/src/test/scala/javacpp/VectorTest.scala b/src/sbt-test/sbt-javacpp/simple/src/test/scala/javacpp/VectorTest.scala new file mode 100644 index 0000000..abfb77a --- /dev/null +++ b/src/sbt-test/sbt-javacpp/simple/src/test/scala/javacpp/VectorTest.scala @@ -0,0 +1,17 @@ +package javacpp; + +import org.scalatest._ +import org.bytedeco.javacpp._ + +class VectorTest extends FlatSpec with BeforeAndAfterAll { + + it should "test PointerVectorVector" in { + val v = new PointerVectorVector(13) + v.resize(0, 42) // v[0].resize(42) + val p = new Pointer() { + address = 0xDEADBEEFL + } + v.put(0, 0, p) // v[0][0] = p + assert(!v.empty) + } +}