-
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
285 additions
and
265 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
281 changes: 281 additions & 0 deletions
281
src/main/java/com/caoccao/javet/swc4j/SWCLibLoader.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,281 @@ | ||
/* | ||
* Copyright (c) 2024. caoccao.com Sam Cao | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package com.caoccao.javet.swc4j; | ||
|
||
import com.caoccao.javet.swc4j.utils.OSUtils; | ||
import com.caoccao.javet.swc4j.utils.StringUtils; | ||
|
||
import java.io.File; | ||
import java.io.FileOutputStream; | ||
import java.io.InputStream; | ||
import java.text.MessageFormat; | ||
|
||
final class SWCLibLoader { | ||
private static final String ANDROID_ABI_ARM = "armeabi-v7a"; | ||
private static final String ANDROID_ABI_ARM64 = "arm64-v8a"; | ||
private static final String ANDROID_ABI_X86 = "x86"; | ||
private static final String ANDROID_ABI_X86_64 = "x86_64"; | ||
private static final String ARCH_ARM = "arm"; | ||
private static final String ARCH_ARM64 = "arm64"; | ||
private static final String ARCH_X86 = "x86"; | ||
private static final String ARCH_X86_64 = "x86_64"; | ||
private static final int BUFFER_LENGTH = 4096; | ||
private static final String CHMOD = "chmod"; | ||
private static final String LIB_FILE_EXTENSION_ANDROID = "so"; | ||
private static final String LIB_FILE_EXTENSION_LINUX = "so"; | ||
private static final String LIB_FILE_EXTENSION_MACOS = "dylib"; | ||
private static final String LIB_FILE_EXTENSION_WINDOWS = "dll"; | ||
private static final String LIB_FILE_NAME_FORMAT = "libswc4j-{0}-{1}.v.{2}.{3}"; | ||
private static final String LIB_FILE_NAME_PREFIX = "lib"; | ||
private static final String LIB_NAME = "swc4j"; | ||
private static final String LIB_VERSION = "0.1.0"; | ||
private static final long MIN_LAST_MODIFIED_GAP_IN_MILLIS = 60L * 1000L; // 1 minute | ||
private static final String OS_ANDROID = "android"; | ||
private static final String OS_LINUX = "linux"; | ||
private static final String OS_MACOS = "macos"; | ||
private static final String OS_WINDOWS = "windows"; | ||
private static final String RESOURCE_NAME_FORMAT = "/{0}"; | ||
private static final String XRR = "755"; | ||
|
||
SWCLibLoader() { | ||
} | ||
|
||
private void deployLibFile(String resourceFileName, File libFile) { | ||
boolean isLibFileLocked = false; | ||
if (libFile.exists() && libFile.canWrite()) { | ||
try { | ||
//noinspection ResultOfMethodCallIgnored | ||
libFile.delete(); | ||
} catch (Throwable t) { | ||
isLibFileLocked = true; | ||
System.err.println(MessageFormat.format( | ||
"Failed to delete {0} because it is locked.", | ||
libFile.getAbsolutePath())); | ||
} | ||
} | ||
if (!isLibFileLocked) { | ||
byte[] buffer = new byte[BUFFER_LENGTH]; | ||
try (InputStream inputStream = SWCNative.class.getResourceAsStream(resourceFileName); | ||
FileOutputStream outputStream = new FileOutputStream(libFile.getAbsolutePath())) { | ||
if (inputStream != null) { | ||
while (true) { | ||
int length = inputStream.read(buffer); | ||
if (length == -1) { | ||
break; | ||
} | ||
outputStream.write(buffer, 0, length); | ||
} | ||
if (OSUtils.IS_LINUX || OSUtils.IS_MACOS || OSUtils.IS_ANDROID) { | ||
try { | ||
Runtime.getRuntime().exec(new String[]{CHMOD, XRR, libFile.getAbsolutePath()}).waitFor(); | ||
} catch (Throwable ignored) { | ||
} | ||
} | ||
} | ||
} catch (Throwable t) { | ||
System.err.println(MessageFormat.format( | ||
"Failed to write to {0} because it is locked.", | ||
libFile.getAbsolutePath())); | ||
} | ||
} | ||
} | ||
|
||
private String getAndroidABI() { | ||
if (OSUtils.IS_ANDROID) { | ||
if (OSUtils.IS_ARM) { | ||
return ANDROID_ABI_ARM; | ||
} else if (OSUtils.IS_ARM64) { | ||
return ANDROID_ABI_ARM64; | ||
} else if (OSUtils.IS_X86) { | ||
return ANDROID_ABI_X86; | ||
} else if (OSUtils.IS_X86_64) { | ||
return ANDROID_ABI_X86_64; | ||
} | ||
} | ||
return null; | ||
} | ||
|
||
private String getFileExtension() { | ||
if (OSUtils.IS_WINDOWS) { | ||
return LIB_FILE_EXTENSION_WINDOWS; | ||
} else if (OSUtils.IS_LINUX) { | ||
return LIB_FILE_EXTENSION_LINUX; | ||
} else if (OSUtils.IS_MACOS) { | ||
return LIB_FILE_EXTENSION_MACOS; | ||
} else if (OSUtils.IS_ANDROID) { | ||
return LIB_FILE_EXTENSION_ANDROID; | ||
} | ||
return null; | ||
} | ||
|
||
private String getLibFileName() { | ||
String fileExtension = getFileExtension(); | ||
String osName = getOSName(); | ||
if (fileExtension == null || osName == null || OSUtils.IS_ANDROID) { | ||
throw new RuntimeException(MessageFormat.format("OS {0} is not supported", OSUtils.OS_NAME)); | ||
} | ||
String osArch = getOSArch(); | ||
if (osArch == null) { | ||
throw new RuntimeException(MessageFormat.format("Arch {0} is not supported", OSUtils.OS_ARCH)); | ||
} | ||
return MessageFormat.format( | ||
LIB_FILE_NAME_FORMAT, | ||
osName, | ||
osArch, | ||
LIB_VERSION, | ||
fileExtension); | ||
} | ||
|
||
private String getOSArch() { | ||
if (OSUtils.IS_WINDOWS) { | ||
return ARCH_X86_64; | ||
} else if (OSUtils.IS_LINUX) { | ||
return OSUtils.IS_ARM64 ? ARCH_ARM64 : ARCH_X86_64; | ||
} else if (OSUtils.IS_MACOS) { | ||
return OSUtils.IS_ARM64 ? ARCH_ARM64 : ARCH_X86_64; | ||
} else if (OSUtils.IS_ANDROID) { | ||
if (OSUtils.IS_ARM) { | ||
return ARCH_ARM; | ||
} else if (OSUtils.IS_ARM64) { | ||
return ARCH_ARM64; | ||
} else if (OSUtils.IS_X86) { | ||
return ARCH_X86; | ||
} else if (OSUtils.IS_X86_64) { | ||
return ARCH_X86_64; | ||
} | ||
} | ||
return null; | ||
} | ||
|
||
private String getOSName() { | ||
if (OSUtils.IS_WINDOWS) { | ||
return OS_WINDOWS; | ||
} else if (OSUtils.IS_LINUX) { | ||
return OS_LINUX; | ||
} else if (OSUtils.IS_MACOS) { | ||
return OS_MACOS; | ||
} else if (OSUtils.IS_ANDROID) { | ||
return OS_ANDROID; | ||
} | ||
return null; | ||
} | ||
|
||
private String getResourceFileName() { | ||
String resourceFileName = MessageFormat.format(RESOURCE_NAME_FORMAT, OSUtils.IS_ANDROID | ||
? StringUtils.join("/", LIB_FILE_NAME_PREFIX, getAndroidABI(), getLibFileName()) | ||
: getLibFileName()); | ||
if (SWCNative.class.getResource(resourceFileName) == null) { | ||
throw new RuntimeException(MessageFormat.format("Lib {0} is not found", resourceFileName)); | ||
} | ||
return resourceFileName; | ||
} | ||
|
||
void load() { | ||
String libFilePath = null; | ||
try { | ||
File libPath = new File(OSUtils.TEMP_DIRECTORY, LIB_NAME); | ||
purge(libPath); | ||
File rootLibPath; | ||
if (OSUtils.IS_ANDROID) { | ||
rootLibPath = libPath; | ||
} else { | ||
rootLibPath = new File(libPath, Long.toString(OSUtils.PROCESS_ID)); | ||
} | ||
if (!rootLibPath.exists()) { | ||
if (!rootLibPath.mkdirs()) { | ||
throw new RuntimeException( | ||
MessageFormat.format("Failed to create {0}.", rootLibPath.getAbsolutePath())); | ||
} | ||
} | ||
String resourceFileName = getResourceFileName(); | ||
File libFile = new File(rootLibPath, getLibFileName()).getAbsoluteFile(); | ||
libFilePath = libFile.getAbsolutePath(); | ||
deployLibFile(resourceFileName, libFile); | ||
System.load(libFilePath); | ||
} catch (Throwable t) { | ||
t.printStackTrace(System.err); | ||
throw new RuntimeException(MessageFormat.format("Failed to load {0}", libFilePath)); | ||
} | ||
} | ||
|
||
private void purge(File rootLibPath) { | ||
try { | ||
if (rootLibPath.exists()) { | ||
if (rootLibPath.isDirectory()) { | ||
File[] files = rootLibPath.listFiles(); | ||
if (files != null && files.length > 0) { | ||
for (File libFileOrPath : files) { | ||
if (libFileOrPath.lastModified() + MIN_LAST_MODIFIED_GAP_IN_MILLIS > System.currentTimeMillis()) { | ||
continue; | ||
} | ||
boolean toBeDeleted = false; | ||
if (libFileOrPath.isDirectory()) { | ||
try { | ||
File[] libFiles = libFileOrPath.listFiles(); | ||
if (libFiles != null && libFiles.length > 0) { | ||
for (File libFile : libFiles) { | ||
if (libFile.delete()) { | ||
System.out.println(MessageFormat.format( | ||
"Deleted {0}.", | ||
libFile.getAbsolutePath())); | ||
} else { | ||
System.out.println(MessageFormat.format( | ||
"{0} is locked.", | ||
libFile.getAbsolutePath())); | ||
toBeDeleted = true; | ||
break; | ||
} | ||
} | ||
} else { | ||
toBeDeleted = true; | ||
} | ||
} catch (Throwable t) { | ||
System.err.println(MessageFormat.format( | ||
"Failed to delete {0}.", | ||
libFileOrPath.getAbsolutePath())); | ||
} | ||
} else if (libFileOrPath.isFile()) { | ||
toBeDeleted = true; | ||
} | ||
if (toBeDeleted) { | ||
if (libFileOrPath.delete()) { | ||
System.out.println(MessageFormat.format( | ||
"Deleted {0}.", | ||
libFileOrPath.getAbsolutePath())); | ||
} else { | ||
System.out.println(MessageFormat.format( | ||
"{0} is locked.", | ||
libFileOrPath.getAbsolutePath())); | ||
} | ||
} | ||
} | ||
} | ||
} else { | ||
if (!rootLibPath.delete()) { | ||
System.err.println(MessageFormat.format( | ||
"Failed to delete {0}.", | ||
rootLibPath.getAbsolutePath())); | ||
} | ||
} | ||
} | ||
} catch (Throwable t) { | ||
System.err.println(MessageFormat.format( | ||
"Failed to clean up {0}.", | ||
rootLibPath.getAbsolutePath())); | ||
} | ||
} | ||
} |
Oops, something went wrong.