-
Notifications
You must be signed in to change notification settings - Fork 12
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Build JavaCPP libraries at compile time #20
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
dist: trusty | ||
sudo: false | ||
language: scala | ||
jdk: | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 = { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If the real There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It should work as long as the corresponding methods that are actually invoked are not missing in the newer version of |
||
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) | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 _ => | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What's the advantage of not pulling in JavaCpp at the sbt level and re-using the loader? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I chose this style, so I didn't have to think about whether the potential difference of the libraries would cause any trouble. It should work either way, though. |
||
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) | ||
} | ||
|
||
} | ||
} |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
package javacpp; | ||
|
||
import org.bytedeco.javacpp.*; | ||
import org.bytedeco.javacpp.annotation.*; | ||
|
||
@Platform(include="<vector>") | ||
@Name("std::vector<std::vector<void*> >") | ||
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<vector<void*> >*)p | ||
private native void allocate(); // this = new vector<vector<void*> >() | ||
private native void allocate(long n); // this = new vector<vector<void*> >(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 | ||
} |
This file was deleted.
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why did you create a new file?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not required. But when there is another
Plugin
object, which I'd like to enable/disable, I would not have to specify the package.