Permalink
Browse files

Merge pull request #115 from mkneissl/master

Run javah to generate JNI C header files.
  • Loading branch information...
2 parents 13a89e5 + 6e2943c commit 053ca2d4dec13d478f3212cbdf4a8f52e17a3b8e @jberkel committed Feb 8, 2012
Showing with 111 additions and 7 deletions.
  1. +97 −7 src/main/scala/AndroidNdk.scala
  2. +14 −0 src/main/scala/AndroidNdkKeys.scala
@@ -24,13 +24,17 @@ object AndroidNdk {
val DefaultObjDirectoryName = "obj"
/** The list of environment variables to check for the NDK. */
val DefaultEnvs = List("ANDROID_NDK_HOME", "ANDROID_NDK_ROOT")
-
+ /** The make environment variable name for the javah generated header directory. */
+ val DefaultJavahOutputEnv = "SBT_MANAGED_JNI_INCLUDE"
lazy val defaultSettings: Seq[Setting[_]] = inConfig(Android) (Seq (
ndkBuildName := DefaultNdkBuildName,
jniDirectoryName := DefaultJniDirectoryName,
objDirectoryName := DefaultObjDirectoryName,
- ndkEnvs := DefaultEnvs
+ ndkEnvs := DefaultEnvs,
+ javahName := "javah",
+ javahOutputEnv := DefaultJavahOutputEnv,
+ javahOutputFile := None
))
// ndk-related paths
@@ -48,25 +52,111 @@ object AndroidNdk {
} yield b
paths.headOption getOrElse (sys.error("Android NDK not found. " +
"You might need to set " + envs.mkString(" or ")))
- }
+ },
+
+ javahPath <<= (javaHome, javahName) apply { (home, name) =>
+ home map ( h => (h / "bin" / name).absolutePath ) getOrElse name
+ },
+
+ javahOutputDirectory <<= (sourceManaged)(_ / "main" / DefaultJniDirectoryName )
+
))
+ private def split(file: File) = {
+ val parentsBottomToTop = Iterator.iterate(file)(_.getParentFile).takeWhile(_ != null).map(_.getName).toSeq
+ parentsBottomToTop.reverse
+ }
+
+ private def compose(parent: File, child: File): File = {
+ if (child.isAbsolute) {
+ child
+ } else {
+ split(child).foldLeft(parent)(new File(_,_))
+ }
+ }
+
+ private def javahTask(
+ javahPath: String,
+ classpath: Seq[File],
+ classes: Seq[String],
+ outputDirectory: File,
+ outputFile: Option[File],
+ streams: TaskStreams) {
+
+ val log = streams.log
+ if (classes.isEmpty) {
+ log.debug("No JNI classes, skipping javah")
+ } else {
+ outputDirectory.mkdirs()
+ val classpathArgument = classpath.map(_.getAbsolutePath()).mkString(File.pathSeparator)
+ val outputArguments = outputFile match {
+ case Some(file) =>
+ val outputFile = compose(outputDirectory, file)
+ // Neither javah nor RichFile.relativeTo will work unless the directories exist.
+ Option(outputFile.getParentFile) foreach (_.mkdirs())
+ if (! (outputFile relativeTo outputDirectory).isDefined) {
+ log.warn("javah output file [" + outputFile + "] is not within javah output directory [" +
+ outputDirectory + "], continuing anyway")
+ }
+
+ Seq("-o", outputFile.absolutePath)
+ case None => Seq("-d", outputDirectory.absolutePath)
+ }
+ val javahCommandLine = Seq(
+ javahPath,
+ "-classpath", classpathArgument) ++
+ outputArguments ++ classes
+ log.debug("Running javah: " + (javahCommandLine mkString " "))
+ val exitCode = Process(javahCommandLine) ! log
+
+ if (exitCode != 0) {
+ sys.error("javah exited with " + exitCode)
+ }
+ }
+ }
private def ndkBuildTask(targets: String*) =
- (ndkBuildPath, nativeOutputPath) map { (ndkBuildPath, obj) =>
- val exitValue = Process(ndkBuildPath.absolutePath :: "-C" :: obj.absolutePath :: targets.toList) !
+ (ndkBuildPath, javahOutputEnv, javahOutputDirectory, nativeOutputPath) map {
+ (ndkBuildPath, javahOutputEnv, javahOutputDirectory, obj) =>
+ val exitValue = Process(ndkBuildPath.absolutePath :: "-C" :: obj.absolutePath ::
+ (javahOutputEnv + "=" + javahOutputDirectory.absolutePath) ::
+ targets.toList) !
if(exitValue != 0) sys.error("ndk-build failed with nonzero exit code (" + exitValue + ")")
()
}
lazy val settings: Seq[Setting[_]] = defaultSettings ++ pathSettings ++ inConfig(Android) (Seq (
+ javah <<= (
+ (compile in Compile),
+ javahPath,
+ (classDirectory in Compile), (internalDependencyClasspath in Compile), (externalDependencyClasspath in Compile),
+ jniClasses,
+ javahOutputDirectory, javahOutputFile,
+ streams) map ((
+ _, // we only depend on a side effect (built classes) of compile
+ javahPath,
+ classDirectory, internalDependencyClasspath, externalDependencyClasspath,
+ jniClasses,
+ javahOutputDirectory,
+ javahOutputFile,
+ streams) =>
+ javahTask(
+ javahPath,
+ Seq(classDirectory) ++ internalDependencyClasspath.files ++ externalDependencyClasspath.files,
+ jniClasses,
+ javahOutputDirectory, javahOutputFile,
+ streams)
+ ),
ndkBuild <<= ndkBuildTask(),
+ ndkBuild <<= ndkBuild.dependsOn(javah),
ndkClean <<= ndkBuildTask("clean"),
- (compile in Compile) <<= (ndkBuild in Android, compile in Compile) map { (ndkBuild, compile) => ndkBuild ; compile }
+ jniClasses := Seq.empty,
+ (products in Compile) <<= (products in Compile).dependsOn(ndkBuild),
+ javahClean <<= (javahOutputDirectory) map IO.delete
)) ++ Seq (
cleanFiles <+= (nativeObjectPath in Android),
- clean <<= (clean, ndkClean in Android) map { (clean, ndkClean) => ndkClean ; clean }
+ clean <<= clean.dependsOn(ndkClean in Android, javahClean in Android)
)
}
@@ -20,4 +20,18 @@ object AndroidNdkKeys {
val ndkBuild = TaskKey[Unit]("ndk-build", "Compile native C/C++ sources.")
val ndkClean = TaskKey[Unit]("ndk-clean", "Clean resources built from native C/C++ sources.")
+ val javahName = SettingKey[String]("javah-name", "The name of the javah command for generating JNI headers")
+ val javahPath = SettingKey[String]("javah-path", "The path to the javah executable")
+ val javah = TaskKey[Unit]("javah", "Produce C headers from Java classes with native methods")
+ val javahClean = TaskKey[Unit]("javah-clean", "Clean C headers built from Java classes with native methods")
+
+ val javahOutputDirectory = SettingKey[File]("javah-output-directory",
+ "The directory where JNI headers are written to.")
+ val javahOutputFile = SettingKey[Option[File]]("javah-output-file",
+ "filename for the generated C header, relative to javah-output-directory")
+ val javahOutputEnv = SettingKey[String]("javah-output-env",
+ "Name of the make environment variable to bind to the javah-output-directory")
+
+ val jniClasses = SettingKey[Seq[String]]("jni-classes",
+ "Fully qualified names of classes with native methods for which JNI headers are to be generated.")
}

0 comments on commit 053ca2d

Please sign in to comment.