mirror of
https://github.com/apple/pkl.git
synced 2026-08-27 06:04:03 +02:00
Introduce C library for Pkl (#1238)
This uses native-image to generate a C library for Pkl. This generated library from native-image is wrapped with our own library, in `pkl.h`. This produces a static and a dynamic library for each os/arch variant that Pkl currently supports. Co-authored-by: Kushal Pisavadia <kushal.p@apple.com> Co-authored-by: Jen Basch <jbasch94@gmail.com> Co-authored-by: Islon Scherer <i_desouzascherer@apple.com>
This commit is contained in:
co-authored by
Kushal Pisavadia
Jen Basch
Islon Scherer
parent
175e2b6273
commit
67df676359
@@ -16,49 +16,64 @@
|
||||
import javax.inject.Inject
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.file.ConfigurableFileCollection
|
||||
import org.gradle.api.file.DirectoryProperty
|
||||
import org.gradle.api.file.FileCollection
|
||||
import org.gradle.api.file.RegularFileProperty
|
||||
import org.gradle.api.model.ObjectFactory
|
||||
import org.gradle.api.provider.ListProperty
|
||||
import org.gradle.api.provider.MapProperty
|
||||
import org.gradle.api.provider.Property
|
||||
import org.gradle.api.provider.Provider
|
||||
import org.gradle.api.services.BuildService
|
||||
import org.gradle.api.services.BuildServiceParameters
|
||||
import org.gradle.api.tasks.ClasspathNormalizer
|
||||
import org.gradle.api.tasks.Input
|
||||
import org.gradle.api.tasks.InputFile
|
||||
import org.gradle.api.tasks.InputFiles
|
||||
import org.gradle.api.tasks.OutputFile
|
||||
import org.gradle.api.tasks.Internal
|
||||
import org.gradle.api.tasks.Optional
|
||||
import org.gradle.api.tasks.OutputFiles
|
||||
import org.gradle.api.tasks.PathSensitivity
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
import org.gradle.kotlin.dsl.registerIfAbsent
|
||||
import org.gradle.kotlin.dsl.withNormalizer
|
||||
import org.gradle.process.ExecOperations
|
||||
|
||||
enum class Architecture {
|
||||
AMD64,
|
||||
AARCH64,
|
||||
}
|
||||
|
||||
abstract class NativeImageBuildService : BuildService<BuildServiceParameters.None>
|
||||
|
||||
abstract class NativeImageBuild : DefaultTask() {
|
||||
@get:Input abstract val imageName: Property<String>
|
||||
@get:Input abstract val outputName: Property<String>
|
||||
|
||||
@get:Input abstract val extraNativeImageArgs: ListProperty<String>
|
||||
|
||||
@get:Input abstract val arch: Property<Architecture>
|
||||
@get:Input abstract val arch: Property<Target.Arch>
|
||||
|
||||
@get:Input abstract val mainClass: Property<String>
|
||||
/**
|
||||
* The main class entrypoint for the executable.
|
||||
*
|
||||
* This option is not necessary if [sharedLibrary] is true.
|
||||
*/
|
||||
@get:Optional @get:Input abstract val mainClass: Property<String>
|
||||
|
||||
/** Create a shared library, instead of an executable. */
|
||||
@get:Input abstract val sharedLibrary: Property<Boolean>
|
||||
|
||||
@get:InputFiles abstract val classpath: ConfigurableFileCollection
|
||||
|
||||
private val outputDir = project.layout.buildDirectory.dir("executable")
|
||||
@get:Input abstract val envVars: MapProperty<String, String>
|
||||
|
||||
@get:OutputFile val outputFile = outputDir.flatMap { it.file(imageName) }
|
||||
@get:InputFile @get:Optional abstract val nativeCompilerPath: RegularFileProperty
|
||||
|
||||
@get:Internal abstract val outputDir: DirectoryProperty
|
||||
|
||||
@get:Inject protected abstract val execOperations: ExecOperations
|
||||
|
||||
@get:Inject protected abstract val objectFactory: ObjectFactory
|
||||
|
||||
private val graalVm: Provider<BuildInfo.GraalVm> = arch.map { a ->
|
||||
when (a) {
|
||||
Architecture.AMD64 -> buildInfo.graalVmAmd64
|
||||
Architecture.AARCH64 -> buildInfo.graalVmAarch64
|
||||
Target.Arch.AMD64 -> buildInfo.graalVmAmd64
|
||||
Target.Arch.AARCH64 -> buildInfo.graalVmAarch64
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,6 +103,10 @@ abstract class NativeImageBuild : DefaultTask() {
|
||||
// CPU resources).
|
||||
usesService(buildService)
|
||||
|
||||
sharedLibrary.convention(false)
|
||||
|
||||
outputDir.convention(project.layout.buildDirectory.dir("executable"))
|
||||
|
||||
group = "build"
|
||||
|
||||
inputs
|
||||
@@ -98,6 +117,51 @@ abstract class NativeImageBuild : DefaultTask() {
|
||||
.files(nativeImageExecutable)
|
||||
.withPropertyName("graalVmNativeImage")
|
||||
.withPathSensitivity(PathSensitivity.ABSOLUTE)
|
||||
if (nativeCompilerPath.isPresent) {
|
||||
inputs.file(nativeCompilerPath)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("unused")
|
||||
@OutputFiles
|
||||
fun getEffectiveOutputFiles(): FileCollection {
|
||||
return objectFactory
|
||||
.fileCollection()
|
||||
.from(
|
||||
sharedLibrary.map { isLibrary ->
|
||||
val dir = outputDir.get()
|
||||
val libraryName = outputName.get()
|
||||
// if building a library, native-image outputs the shared library plus four headers
|
||||
// (and, on Windows, an import library); otherwise, it outputs just the one file
|
||||
if (isLibrary) {
|
||||
val sharedLibraryExtension = buildInfo.os.sharedLibraryExtension
|
||||
val staticLibraryExtension = buildInfo.os.staticLibraryExtension
|
||||
dir.files(
|
||||
buildList {
|
||||
add("$libraryName.$sharedLibraryExtension")
|
||||
if (buildInfo.os.isWindows) {
|
||||
// the DLL's import library, which consumers must link against instead of
|
||||
// the .dll itself
|
||||
add("$libraryName.$staticLibraryExtension")
|
||||
// a genuine, self-contained static archive with no runtime DLL dependency,
|
||||
// produced by build_windows.bat
|
||||
add("${libraryName}_s.$staticLibraryExtension")
|
||||
} else {
|
||||
// a genuine, self-contained static archive with no runtime dependency,
|
||||
// produced by build_unix.sh
|
||||
add("$libraryName.$staticLibraryExtension")
|
||||
}
|
||||
add("$libraryName.h")
|
||||
add("${libraryName}_dynamic.h")
|
||||
add("graal_isolate.h")
|
||||
add("graal_isolate_dynamic.h")
|
||||
}
|
||||
)
|
||||
} else {
|
||||
dir.file(libraryName)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
@@ -111,6 +175,7 @@ abstract class NativeImageBuild : DefaultTask() {
|
||||
workingDir(outputDir)
|
||||
|
||||
args = buildList {
|
||||
add("--color=always")
|
||||
// must be emitted before any experimental options are used
|
||||
add("-H:+UnlockExperimentalVMOptions")
|
||||
// currently gives a deprecation warning, but we've been told
|
||||
@@ -126,9 +191,14 @@ abstract class NativeImageBuild : DefaultTask() {
|
||||
add("-H:IncludeResourceBundles=org.pkl.core.errorMessages")
|
||||
add("-H:IncludeResourceBundles=org.pkl.parser.errorMessages")
|
||||
add("-H:IncludeResources=org/pkl/commons/cli/PklCARoots.pem")
|
||||
add("-H:Class=${mainClass.get()}")
|
||||
if (mainClass.isPresent) {
|
||||
add("-H:Class=${mainClass.get()}")
|
||||
}
|
||||
if (sharedLibrary.get()) {
|
||||
add("--shared")
|
||||
}
|
||||
add("-o")
|
||||
add(imageName.get())
|
||||
add(outputName.get())
|
||||
// the actual limit (currently) used by native-image is this number + 1400 (idea is to
|
||||
// compensate for Truffle's own nodes)
|
||||
add("-H:MaxRuntimeCompileMethods=1800")
|
||||
@@ -138,10 +208,11 @@ abstract class NativeImageBuild : DefaultTask() {
|
||||
// disable automatic support for JVM CLI options (puts our main class in full control of
|
||||
// argument parsing)
|
||||
add("-H:-ParseRuntimeOptions")
|
||||
// quick build mode: 40% faster compilation, 20% smaller (but presumably also slower)
|
||||
// executable
|
||||
if (!buildInfo.isReleaseBuild) {
|
||||
add("-Ob")
|
||||
// disable all optimizations
|
||||
add("-O0")
|
||||
// generate debugging information
|
||||
add("-g")
|
||||
}
|
||||
if (buildInfo.isNativeArch) {
|
||||
add("-march=native")
|
||||
@@ -157,8 +228,12 @@ abstract class NativeImageBuild : DefaultTask() {
|
||||
// make sure dev machine stays responsive (15% slowdown on my laptop)
|
||||
val processors =
|
||||
Runtime.getRuntime().availableProcessors() /
|
||||
if (buildInfo.os.isMacOsX && !buildInfo.isCiBuild) 4 else 1
|
||||
if (buildInfo.os.isMacOS && !buildInfo.isCiBuild) 4 else 1
|
||||
add("-J-XX:ActiveProcessorCount=${processors}")
|
||||
addAll(envVars.get().entries.map { "-E${it.key}=${it.value}" })
|
||||
if (nativeCompilerPath.isPresent) {
|
||||
add("--native-compiler-path=${nativeCompilerPath.get().asFile}")
|
||||
}
|
||||
// Pass through all `HOMEBREW_` prefixed environment variables to allow build with shimmed
|
||||
// tools.
|
||||
addAll(environment.keys.filter { it.startsWith("HOMEBREW_") }.map { "-E$it" })
|
||||
|
||||
Reference in New Issue
Block a user