Skip to content

Commit 035b96e

Browse files
authored
Add support of multiple native outputs (#186)
* Add support of multiple native outputs * Make the new behavior configurable and compatible with the previous release so there is no implicit behavior change
1 parent a2677b8 commit 035b96e

File tree

8 files changed

+61
-52
lines changed

8 files changed

+61
-52
lines changed

README.md

+7-1
Original file line numberDiff line numberDiff line change
@@ -153,12 +153,18 @@ Since this plugin is basically a command-line wrapper, native build tools must f
153153

154154
An initial, compatible build template can be obtained by running `sbt nativeInit <tool>`. Once the native build tool initialised, projects are built by calling the `sbt nativeCompile` task.
155155

156-
Source and output directories are configurable
156+
Source and output directories are configurable:
157157
```scala
158158
nativeCompile / sourceDirectory := sourceDirectory.value / "native"
159159
nativeCompile / target := target.value / "native" / nativePlatform.value
160160
```
161161

162+
Some JNI projects may produce more than a single output. If that's an intended behavior it's possible to include all of the produced
163+
binaries into the native package:
164+
```scala
165+
nativeMultipleOutputs := true
166+
```
167+
162168
#### CMake
163169

164170
A regular `CMake` native project definition usually looks this following way:

plugin/src/main/scala/com/github/sbt/jni/build/BuildTool.scala

+29-2
Original file line numberDiff line numberDiff line change
@@ -70,15 +70,42 @@ trait BuildTool {
7070
*/
7171
def library(
7272
targetDirectory: File
73-
): File
73+
): List[File]
7474

7575
}
7676

7777
/**
7878
* Get an instance (build configuration) of this tool, in the specified directory.
7979
*/
80-
def getInstance(baseDirectory: File, buildDirectory: File, logger: Logger): Instance
80+
def getInstance(baseDirectory: File, buildDirectory: File, logger: Logger, multipleOutputs: Boolean): Instance
8181

82+
/**
83+
* At least one produced library is expected.
84+
*/
85+
def validate(list: List[File], multipleOutputs: Boolean, logger: Logger): List[File] = {
86+
list match {
87+
case Nil =>
88+
sys.error(
89+
s"No files were created during compilation, " +
90+
s"something went wrong with the $name configuration."
91+
)
92+
case list @ _ :: Nil =>
93+
list
94+
95+
case head :: _ if !multipleOutputs =>
96+
logger.warn(
97+
s"""
98+
|More than one file was created during compilation, only the first one (${head.getAbsolutePath}) will be used.
99+
|Consider setting nativeMultipleOutputs := true.
100+
|""".stripMargin
101+
)
102+
List(head)
103+
104+
case list =>
105+
logger.info("More than one file was created during compilation.")
106+
list
107+
}
108+
}
82109
}
83110

84111
object BuildTool {

plugin/src/main/scala/com/github/sbt/jni/build/CMake.scala

+2-1
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,12 @@ class CMake(protected val configuration: Seq[String]) extends BuildTool with Con
1414
"/com/github/sbt/jni/templates/CMakeLists.txt" -> "CMakeLists.txt"
1515
)
1616

17-
override def getInstance(baseDir: File, buildDir: File, logger: Logger) = new Instance {
17+
override def getInstance(baseDir: File, buildDir: File, logger: Logger, nativeMultipleOutputs: Boolean) = new Instance {
1818

1919
override def log = logger
2020
override def baseDirectory = baseDir
2121
override def buildDirectory = buildDir
22+
override def multipleOutputs = nativeMultipleOutputs
2223

2324
def cmakeProcess(args: String*): ProcessBuilder = Process("cmake" +: args, buildDirectory)
2425

plugin/src/main/scala/com/github/sbt/jni/build/Cargo.scala

+5-20
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,10 @@ class Cargo(protected val configuration: Seq[String]) extends BuildTool {
1818

1919
def release: Boolean = configuration.exists(_.toLowerCase.contains("release"))
2020

21-
def getInstance(baseDirectory: File, buildDirectory: File, logger: sbt.Logger): Instance =
22-
new Instance(baseDirectory, logger)
21+
def getInstance(baseDirectory: File, buildDirectory: File, logger: sbt.Logger, multipleOutputs: Boolean): Instance =
22+
new Instance(baseDirectory, logger, multipleOutputs)
2323

24-
class Instance(protected val baseDirectory: File, protected val logger: sbt.Logger) extends super.Instance {
24+
class Instance(protected val baseDirectory: File, protected val logger: sbt.Logger, protected val multipleOutputs: Boolean) extends super.Instance {
2525
// IntelliJ friendly logger, IntelliJ doesn't start tests if a line is printed as "error", which Cargo does for regular output
2626
protected val log: ProcessLogger = new ProcessLogger {
2727
def out(s: => String): Unit = logger.info(s)
@@ -31,7 +31,7 @@ class Cargo(protected val configuration: Seq[String]) extends BuildTool {
3131

3232
def clean(): Unit = Process("cargo clean", baseDirectory) ! log
3333

34-
def library(targetDirectory: File): File = {
34+
def library(targetDirectory: File): List[File] = {
3535
val configurationString = (configuration ++ Seq("--target-dir", targetDirectory.getAbsolutePath)).mkString(" ").trim
3636
val ev =
3737
Process(
@@ -44,22 +44,7 @@ class Cargo(protected val configuration: Seq[String]) extends BuildTool {
4444
val products: List[File] =
4545
(targetDirectory / subdir * ("*.so" | "*.dylib")).get.filter(_.isFile).toList
4646

47-
// only one produced library is expected
48-
products match {
49-
case Nil =>
50-
sys.error(
51-
s"No files were created during compilation, " +
52-
s"something went wrong with the $name configuration."
53-
)
54-
case head :: Nil =>
55-
head
56-
case head :: _ =>
57-
logger.warn(
58-
"More than one file was created during compilation, " +
59-
s"only the first one (${head.getAbsolutePath}) will be used."
60-
)
61-
head
62-
}
47+
validate(products, multipleOutputs, logger)
6348
}
6449
}
6550
}

plugin/src/main/scala/com/github/sbt/jni/build/ConfigureMakeInstall.scala

+3-17
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ trait ConfigureMakeInstall { self: BuildTool =>
1313
trait Instance extends self.Instance {
1414

1515
def log: Logger
16+
def multipleOutputs: Boolean
1617
def baseDirectory: File
1718
def buildDirectory: File
1819

@@ -25,7 +26,7 @@ trait ConfigureMakeInstall { self: BuildTool =>
2526

2627
def library(
2728
targetDirectory: File
28-
): File = {
29+
): List[File] = {
2930

3031
val ev: Int = (
3132
configure(targetDirectory) #&& make() #&& install()
@@ -36,22 +37,7 @@ trait ConfigureMakeInstall { self: BuildTool =>
3637
val products: List[File] =
3738
(targetDirectory ** ("*.so" | "*.dylib")).get.filter(_.isFile).toList
3839

39-
// only one produced library is expected
40-
products match {
41-
case Nil =>
42-
sys.error(
43-
s"No files were created during compilation, " +
44-
s"something went wrong with the ${name} configuration."
45-
)
46-
case head :: Nil =>
47-
head
48-
case head :: tail =>
49-
log.warn(
50-
"More than one file was created during compilation, " +
51-
s"only the first one (${head.getAbsolutePath}) will be used."
52-
)
53-
head
54-
}
40+
validate(products, multipleOutputs, log)
5541
}
5642
}
5743

plugin/src/main/scala/com/github/sbt/jni/build/Meson.scala

+3-2
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,12 @@ class Meson(protected val configuration: Seq[String]) extends BuildTool with Con
1515
"/com/github/sbt/jni/templates/meson.options" -> "meson.options"
1616
)
1717

18-
override def getInstance(baseDir: File, buildDir: File, logger: Logger) = new Instance {
18+
override def getInstance(baseDir: File, buildDir: File, logger: Logger, nativeMultipleOutputs: Boolean) = new Instance {
1919

2020
override def log = logger
2121
override def baseDirectory = baseDir
2222
override def buildDirectory = buildDir
23+
override def multipleOutputs = nativeMultipleOutputs
2324

2425
def mesonProcess(args: String*): ProcessBuilder = Process("meson" +: args, buildDirectory)
2526

@@ -30,7 +31,7 @@ class Meson(protected val configuration: Seq[String]) extends BuildTool with Con
3031
override def configure(target: File) = {
3132
mesonProcess(
3233
Seq("setup", "--prefix", target.getAbsolutePath) ++ configuration ++ Seq(
33-
mesonVersion.toString,
34+
mesonVersion,
3435
baseDirectory.getAbsolutePath
3536
): _*
3637
)

plugin/src/main/scala/com/github/sbt/jni/plugins/JniNative.scala

+11-6
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ object JniNative extends AutoPlugin {
1414
object autoImport {
1515

1616
// Main task, inspect this first
17-
val nativeCompile = taskKey[File](
17+
val nativeCompile = taskKey[Seq[File]](
1818
"Builds a native library by calling the native build tool."
1919
)
2020

@@ -30,6 +30,10 @@ object JniNative extends AutoPlugin {
3030
"Initialize a native build script from a template."
3131
)
3232

33+
val nativeMultipleOutputs = taskKey[Boolean](
34+
"Enable multiple native outputs support. Disabled by default."
35+
)
36+
3337
}
3438
import autoImport._
3539

@@ -79,8 +83,8 @@ object JniNative extends AutoPlugin {
7983
tools.map(_.name).mkString(",")
8084
)
8185
)
82-
8386
},
87+
nativeMultipleOutputs := false,
8488
nativeBuildToolInstance := {
8589
val tool = nativeBuildTool.value
8690
val srcDir = (nativeCompile / sourceDirectory).value
@@ -89,7 +93,8 @@ object JniNative extends AutoPlugin {
8993
tool.getInstance(
9094
baseDirectory = srcDir,
9195
buildDirectory = buildDir,
92-
logger = streams.value.log
96+
logger = streams.value.log,
97+
multipleOutputs = nativeMultipleOutputs.value
9398
)
9499
},
95100
nativeCompile / clean := {
@@ -114,9 +119,9 @@ object JniNative extends AutoPlugin {
114119
IO.createDirectory(targetDir)
115120

116121
log.info(s"Building library with native build tool ${tool.name}")
117-
val lib = toolInstance.library(targetDir)
118-
log.success(s"Library built in ${lib.getAbsolutePath}")
119-
lib
122+
val libs = toolInstance.library(targetDir)
123+
log.success(s"Libraries built in ${libs.map(_.getAbsolutePath).mkString(", ")}")
124+
libs
120125
},
121126

122127
// also clean native sources

plugin/src/main/scala/com/github/sbt/jni/plugins/JniPackage.scala

+1-3
Original file line numberDiff line numberDiff line change
@@ -74,10 +74,8 @@ object JniPackage extends AutoPlugin {
7474
.taskDyn[Seq[(File, String)]] {
7575
val enableManaged = enableNativeCompilation.value
7676
if (enableManaged) Def.task {
77-
val library: File = nativeCompile.value
7877
val platform = nativePlatform.value
79-
80-
Seq(library -> s"/native/$platform/${library.name}")
78+
nativeCompile.value.map { library => library -> s"/native/$platform/${library.name}" }
8179
}
8280
else
8381
Def.task {

0 commit comments

Comments
 (0)