Skip to content

Commit

Permalink
Issue bertramdev#287: Add dynamic native library loading
Browse files Browse the repository at this point in the history
  • Loading branch information
longwa committed Dec 3, 2021
1 parent 8e60c7e commit f7a6093
Show file tree
Hide file tree
Showing 12 changed files with 201 additions and 69 deletions.
2 changes: 0 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,3 @@ asset-pipeline-site/src/assets/html/apidoc/
.jruby-container
.sass-cache
.sass-work
node_modules/
sass-dart-asset-pipeline/src/main/resources/js/
1 change: 1 addition & 0 deletions .sdkmanrc
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
gradle=6.5
java=8.0.312-zulu
43 changes: 31 additions & 12 deletions asset-pipeline-core.ipr
Original file line number Diff line number Diff line change
Expand Up @@ -357,14 +357,12 @@
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.beust/jcommander/1.72/7ef123d5dfb6f839b41265648ff1be34982d50f8/jcommander-1.72-sources.jar!/" />
</SOURCES>
</library>
<library name="Gradle: com.caoccao.javet:javet-macos:1.0.4">
<library name="Gradle: com.caoccao.javet:javet-core:1.0.6">
<CLASSES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.caoccao.javet/javet-macos/1.0.4/166b4a9e90f9b976bc3f2060f7406159e813da5c/javet-macos-1.0.4.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/com/caoccao/javet/javet-core/1.0.6/javet-core-1.0.6.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.caoccao.javet/javet-macos/1.0.4/1c3edf0b46947fe5f5d61ce74bdd5e448eb1b3a0/javet-macos-1.0.4-sources.jar!/" />
</SOURCES>
<SOURCES />
</library>
<library name="Gradle: com.fasterxml.jackson.core:jackson-annotations:2.11.2">
<CLASSES>
Expand Down Expand Up @@ -1478,9 +1476,12 @@
<library name="Gradle: javax.activation:javax.activation-api:1.2.0">
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/javax/activation/javax.activation-api/1.2.0/javax.activation-api-1.2.0.jar!/" />
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/javax.activation/javax.activation-api/1.2.0/85262acf3ca9816f9537ca47d5adeabaead7cb16/javax.activation-api-1.2.0.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
<SOURCES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/javax.activation/javax.activation-api/1.2.0/1b4c6e77d35adab19794746d8ea753df30d77dca/javax.activation-api-1.2.0-sources.jar!/" />
</SOURCES>
</library>
<library name="Gradle: javax.annotation:javax.annotation-api:1.3.2">
<CLASSES>
Expand Down Expand Up @@ -1566,9 +1567,12 @@
<library name="Gradle: javax.xml.bind:jaxb-api:2.3.1">
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/javax/xml/bind/jaxb-api/2.3.1/jaxb-api-2.3.1.jar!/" />
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/javax.xml.bind/jaxb-api/2.3.1/8531ad5ac454cc2deb9d4d32c40c4d7451939b5d/jaxb-api-2.3.1.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
<SOURCES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/javax.xml.bind/jaxb-api/2.3.1/e47264d065cecbc572ba99856fcc7da19a470ae3/jaxb-api-2.3.1-sources.jar!/" />
</SOURCES>
</library>
<library name="Gradle: jline:jline:2.14.6">
<CLASSES>
Expand Down Expand Up @@ -1695,16 +1699,22 @@
<library name="Gradle: org.apache.commons:commons-lang3:3.8.1">
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/apache/commons/commons-lang3/3.8.1/commons-lang3-3.8.1.jar!/" />
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-lang3/3.8.1/6505a72a097d9270f7a9e7bf42c4238283247755/commons-lang3-3.8.1.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
<SOURCES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-lang3/3.8.1/e9748d7783594da3735ad9724390a10e7cebc01d/commons-lang3-3.8.1-sources.jar!/" />
</SOURCES>
</library>
<library name="Gradle: org.apache.commons:commons-lang3:3.9">
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/apache/commons/commons-lang3/3.9/commons-lang3-3.9.jar!/" />
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-lang3/3.9/122c7cee69b53ed4a7681c03d4ee4c0e2765da5/commons-lang3-3.9.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
<SOURCES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-lang3/3.9/8f1cb192e229bc4cd1c900c51171d96706e6d195/commons-lang3-3.9-sources.jar!/" />
</SOURCES>
</library>
<library name="Gradle: org.apache.commons:commons-text:1.8">
<CLASSES>
Expand Down Expand Up @@ -2204,9 +2214,12 @@
<library name="Gradle: org.fusesource.jansi:jansi:1.18">
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/fusesource/jansi/jansi/1.18/jansi-1.18.jar!/" />
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.fusesource.jansi/jansi/1.18/d9205bbcd4b5f9cd1effe752d18f73bd942d783f/jansi-1.18.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
<SOURCES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.fusesource.jansi/jansi/1.18/9553a21528688c4a0b1248036dbd878c16afe47d/jansi-1.18-sources.jar!/" />
</SOURCES>
</library>
<library name="Gradle: org.glassfish:javax.el:3.0.0">
<CLASSES>
Expand Down Expand Up @@ -2794,9 +2807,12 @@
<library name="Gradle: org.opentest4j:opentest4j:1.1.1">
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/opentest4j/opentest4j/1.1.1/opentest4j-1.1.1.jar!/" />
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.opentest4j/opentest4j/1.1.1/efd9f971e91074491ea55b19f67b13470cf4fcdd/opentest4j-1.1.1.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
<SOURCES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.opentest4j/opentest4j/1.1.1/88a3a2cb15c413565462cea99f201b67bc6d2f10/opentest4j-1.1.1-sources.jar!/" />
</SOURCES>
</library>
<library name="Gradle: org.reactivestreams:reactive-streams:1.0.0">
<CLASSES>
Expand Down Expand Up @@ -2909,9 +2925,12 @@
<library name="Gradle: org.slf4j:slf4j-api:1.7.30">
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/slf4j/slf4j-api/1.7.30/slf4j-api-1.7.30.jar!/" />
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.30/b5a4b6d16ab13e34a88fae84c35cd5d68cac922c/slf4j-api-1.7.30.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
<SOURCES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.30/71a1fdefa224da50f1db6306ce609981e20186c9/slf4j-api-1.7.30-sources.jar!/" />
</SOURCES>
</library>
<library name="Gradle: org.slf4j:slf4j-simple:1.7.10">
<CLASSES>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,6 @@ interface AssetPipelineExtension {
@Input
@Optional
List<String> getResolvers()
void setResolvers(List value)
void setResolvers(List<String> value)

}
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ class AssetPipelinePlugin implements Plugin<Project> {

def defaultConfiguration = project.extensions.create('assets', AssetPipelineExtensionImpl)
def config = AssetPipelineConfigHolder.config != null ? AssetPipelineConfigHolder.config : [:]
config.cacheLocation = "${project.buildDir}/.assCache"
config.cacheLocation = "${project.buildDir}/.assetcache"
if (project.extensions.findByName('grails')) {

defaultConfiguration.assetsPath = 'grails-app/assets'
Expand Down
3 changes: 3 additions & 0 deletions sass-dart-asset-pipeline/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
node_modules/
src/main/resources/js/
javet-version.txt
22 changes: 16 additions & 6 deletions sass-dart-asset-pipeline/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,16 @@ apply plugin: 'groovy'
apply plugin: 'maven-publish'
apply plugin: 'java-library'
apply plugin: 'idea'

group = 'com.bertramlabs.plugins'
ext.isReleaseVersion = !version.endsWith("SNAPSHOT")
sourceCompatibility = '1.8'
targetCompatibility = '1.8'

ext {
javetVersion = "1.0.6"
isReleaseVersion = !version.endsWith("SNAPSHOT")
}

repositories {
mavenLocal()
mavenCentral()
Expand Down Expand Up @@ -45,10 +50,7 @@ apply plugin: 'com.github.node-gradle.node'

dependencies {
implementation 'org.codehaus.groovy:groovy-all:2.4.19'
// implementation 'com.eclipsesource.j2v8:j2v8:6.2.1'
// implementation 'com.eclipsesource.j2v8:j2v8_macosx_x86_64:4.6.0'
// implementation "com.caoccao.javet:javet:1.0.4"
implementation 'com.caoccao.javet:javet-macos:1.0.4' // Mac OS (x86_64 Only)
implementation "com.caoccao.javet:javet-core:${javetVersion}"
api project(':asset-pipeline-core')
api 'org.slf4j:slf4j-api:1.7.28'
testImplementation 'org.codehaus.groovy:groovy-all:2.4.19'
Expand Down Expand Up @@ -107,6 +109,14 @@ publishing {
}


task copyJavetVersion() {
doLast {
new File("${projectDir}/src/main/resources", "javet-version.txt").text = javetVersion
}
}

compileGroovy.dependsOn copyJavetVersion

task(console, dependsOn: 'classes', type: JavaExec) {
main = 'groovy.ui.Console'
classpath = sourceSets.main.runtimeClasspath
Expand All @@ -121,5 +131,5 @@ test {

node {
download = true
version = "14.18.1"
version = "16.13.0"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package asset.pipeline.dart

import asset.pipeline.AssetPipelineConfigHolder
import com.caoccao.javet.enums.JSRuntimeType
import groovy.transform.CompileStatic
import groovy.util.logging.Slf4j

import java.nio.channels.Channels
import java.nio.channels.FileChannel
import java.nio.channels.ReadableByteChannel
import java.nio.file.Files
import java.nio.file.Paths
import java.nio.file.StandardCopyOption
import java.util.jar.JarEntry
import java.util.jar.JarFile

@Slf4j
@CompileStatic
class NativeLibraryUtil {
static final String JAVET_VERSION = NativeLibraryUtil.class.classLoader.getResource("javet-version.txt").openStream().text
static final String BASE_URL = AssetPipelineConfigHolder.config?.javetBaseUrl ?: "https://repo1.maven.org/maven2/com/caoccao/javet/javet-macos/${JAVET_VERSION}/"

static final String TMP_DIR = System.getProperty("java.io.tmpdir")
static final String OS_ARCH = System.getProperty("os.arch")
static final String OS_NAME = System.getProperty("os.name")

static final boolean IS_LINUX = OS_NAME.startsWith("Linux")
static final boolean IS_MACOS = OS_NAME.startsWith("Mac OS")
static final boolean IS_WINDOWS = OS_NAME.startsWith("Windows")
static final boolean IS_ARM64 = OS_ARCH.startsWith("arm64") || OS_ARCH.startsWith("armv8") || OS_ARCH == "aarch64"
static final boolean IS_X86_64 = OS_ARCH.matches(/^(x8664|amd64|ia32e|em64t|x64|x86_64)$/)

static final String ARCH_NAME = IS_ARM64 ? 'arm64' : 'x86_64'

static URL getJavetJARUrl() {
new URL(BASE_URL + getJavetFileName())
}

static String getJavetFileName() {
IS_MACOS ? "javet-macos-${JAVET_VERSION}.jar" : "javet-${JAVET_VERSION}.jar"
}

static String getLibraryName(JSRuntimeType runtimeType) {
if (IS_MACOS) {
return "libjavet-${runtimeType.name}-macos-${ARCH_NAME}.v.${JAVET_VERSION}.dylib"
}
if (IS_LINUX) {
return "libjavet-${runtimeType.name}-linux-${ARCH_NAME}.v.${JAVET_VERSION}.so"
}
if (IS_WINDOWS) {
return "libjavet-${runtimeType.name}-windows-${ARCH_NAME}.v.${JAVET_VERSION}.dll"
}

throw new IllegalStateException("Platform type not supported: ${OS_NAME} (${OS_ARCH}), expected Mac OS, Linux, or Windows")
}

static File downloadJavetJar(JSRuntimeType runtimeType) {
File jarFile = new File(TMP_DIR, javetFileName)
if (jarFile.exists()) {
log.debug("Jar file ${jarFile.path} exists, skipping download")
return jarFile
}

// Download the file
URL jarUrl = getJavetJARUrl()
log.info("Downloading sass-dart native library integration: ${jarUrl}")

copyToFile(jarUrl.openStream(), jarFile)

log.debug("Downloaded $jarUrl to $jarFile (${jarFile.size()} bytes)")
jarFile
}

static File extractNativeLibrary(JSRuntimeType runtimeType) {
File file = downloadJavetJar(runtimeType)
if (!file.exists()) {
throw new IllegalStateException("Javet jar file $file does not exist, perhaps the download failed")
}

String libraryName = getLibraryName(runtimeType)
File jniLibrary = new File(TMP_DIR, libraryName)
if (jniLibrary.exists()) {
log.debug("Native library $libraryName already exists, skipping extract")
return jniLibrary
}

// Extract from the JAR file, should be in the root
JarFile jarFile = new JarFile(file)
JarEntry jarEntry = jarFile.getJarEntry(libraryName)
if (!jarEntry) {
throw new IllegalStateException("Could not load native library: $libraryName")
}

copyToFile(jarFile.getInputStream(jarEntry), jniLibrary)

log.debug("Extracted native library $libraryName to ${jniLibrary.path}")
jniLibrary
}

static private void copyToFile(InputStream inputStream, File targetFile) {
ReadableByteChannel readableByteChannel = Channels.newChannel(inputStream)
FileOutputStream fileOutputStream = new FileOutputStream(targetFile)
FileChannel fileChannel = fileOutputStream.getChannel()
fileChannel.transferFrom(readableByteChannel, 0, Long.MAX_VALUE)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package asset.pipeline.dart

import asset.pipeline.AssetFile
import asset.pipeline.AssetHelper
import asset.pipeline.CacheManager
import com.caoccao.javet.annotations.V8Function
import groovy.transform.CompileStatic
import groovy.util.logging.Slf4j
Expand Down Expand Up @@ -30,19 +31,17 @@ class SassAssetFileLoader {
*
* @param url the import it appears in the source file
* @prev either 'stdin' for the first level imports or the original url from the parent for nested
* @param assetFilePath the original assetFile.path that started the import chain
* @return https://sass-lang.com/documentation/js-api/modules#LegacyImporterResult
*/
@V8Function
@SuppressWarnings('unused')
Map resolveImport(String url, String prev, String assetFilePath) {
log.debug("Resolving import for url [{}], prev [{}], asset file path [{}]", url, prev, assetFilePath)
println " > Importing [${url}], prev [$prev], asset file [${assetFilePath}]"
Map resolveImport(String url, String prev) {
log.debug("Importing for url [{}], prev [{}], base file [{}]", url, prev, baseFile?.path)

// The initial import has a path of stdin, but we need to convert that to the proper base path
// Otherwise, if we have a parent, append that to form the correct URL as the importer syntax doesn't send what's expected
if (prev == 'stdin') {
prev = assetFilePath
prev = baseFile.path
}
else {
// Resolve the real base path for this import
Expand All @@ -60,6 +59,8 @@ class SassAssetFileLoader {
importMap[url] = prev

AssetFile imported = getAssetFromScssImport(prev, url)
CacheManager.addCacheDependency(baseFile.path, imported)

return [contents: imported.inputStream.text]
}

Expand Down
Loading

0 comments on commit f7a6093

Please sign in to comment.