1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
|
package native
import java.io.File
import sbt._
import sbt.Keys._
import scala.util.Try
/** A platform is a the representation of an os-architecture combination */
case class Platform(kernel: String, arch: String) {
val id = kernel + "-" + arch
}
object Platform {
/** Create a platform with spaces stripped and case normalized. */
def normalize(kernel: String, arch: String) = Platform(
kernel.toLowerCase.filter(!_.isWhitespace),
arch
)
/** Run 'uname' to determine current platform. Returns None if uname does not exist. */
lazy val uname: Option[Platform] = {
val lines = Try { Process("uname -sm").lines.head }.toOption
lines.map { line =>
val parts = line.split(" ")
if (parts.length != 2) {
sys.error("Could not determine platform: 'uname -sm' returned unexpected string: " + line)
} else {
Platform.normalize(parts(0), parts(1))
}
}
}
}
object NativeKeys {
val Native = config("native")
val platform = settingKey[Platform]("Platform of the system this build is being run on.")
//fat jar settings
val libraryPrefix = settingKey[String]("A string to be prepended to native products when packaged.")
val libraryManifest = settingKey[String]("Name of a file that will contain a list of all native products.")
val libraryResourceDirectory = settingKey[File](
"Directory that contains native products when they treated as resources."
)
}
/** Provides implementations of wrapper tasks suitable for projects using Autotools */
object Autotools {
import NativeKeys._
import sbt.Def.Initialize
private val clean: Initialize[Task[Unit]] = Def.task {
val log = streams.value.log
val src = (sourceDirectory in Native).value
Process("make distclean", src) #|| Process("make clean", src) ! log
}
private val lib: Initialize[Task[File]] = Def.task {
val log = streams.value.log
val src = (sourceDirectory in Native).value
val out = (target in Native).value
val outPath = out.getAbsolutePath
val configure = if ((src / "config.status").exists) {
Process("./config.status", src)
} else {
Process(
//Disable producing versioned library files, not needed for fat jars.
s"./configure --prefix=$outPath --libdir=$outPath --disable-versioned-lib",
src
)
}
val make = Process("make", src)
val makeInstall = Process("make install", src)
val ev = configure #&& make #&& makeInstall ! log
if (ev != 0)
throw new RuntimeException(s"Building native library failed. Exit code: ${ev}")
val products: List[File] = (out ** ("*" -- "*.la")).get.filter(_.isFile).toList
//only one produced library is expected
products match {
case Nil =>
sys.error("No files were created during compilation, " +
"something went wrong with the autotools configuration.")
case head :: Nil =>
head
case head :: tail =>
log.warn("More than one file was created during compilation, " +
s"only the first one (${head.getAbsolutePath}) will be used.")
head
}
}
val settings: Seq[Setting[_]] = Seq(
Keys.clean in Native := Autotools.clean.value,
Keys.compile in Native := {
lib.value
sbt.inc.Analysis.Empty
},
Keys.packageBin in Native := {
lib.value
}
)
}
object NativeDefaults {
import NativeKeys._
/** Copy native product to resource directory and create manifest */
private val libraryResources = Def.task {
val out = (libraryResourceDirectory in Compile).value
val product = (packageBin in Native).value
val productResource = out / product.name
val manifestResource = out / (libraryManifest in Compile).value
IO.copyFile(product, productResource)
IO.write(manifestResource, productResource.name)
Seq(productResource, manifestResource)
}
private val fatJarSettings = Seq(
libraryPrefix in Compile := "",
libraryManifest in Compile := "library",
libraryResourceDirectory in Compile := (resourceManaged in Compile).value /
(libraryPrefix in Compile).value / (platform in Native).value.id,
unmanagedResourceDirectories in Compile += (baseDirectory).value / "lib_native",
resourceGenerators in Compile += libraryResources.taskValue
)
val settings: Seq[Setting[_]] = Seq(
platform in Native := Platform.uname.getOrElse {
System.err.println("Warning: Cannot determine platform! It will be set to 'unknown'.")
Platform("unknown", "unknown")
},
target in Native := target.value / "native" / (platform in Native).value.id
) ++ fatJarSettings ++ Autotools.settings
}
|