diff options
Diffstat (limited to 'z3/subprojects/solver/src/main/java/tools/refinery/z3/Z3SolverLoader.java')
-rw-r--r-- | z3/subprojects/solver/src/main/java/tools/refinery/z3/Z3SolverLoader.java | 154 |
1 files changed, 154 insertions, 0 deletions
diff --git a/z3/subprojects/solver/src/main/java/tools/refinery/z3/Z3SolverLoader.java b/z3/subprojects/solver/src/main/java/tools/refinery/z3/Z3SolverLoader.java new file mode 100644 index 00000000..84f99540 --- /dev/null +++ b/z3/subprojects/solver/src/main/java/tools/refinery/z3/Z3SolverLoader.java | |||
@@ -0,0 +1,154 @@ | |||
1 | /* | ||
2 | * Copyright 2010-2022 Google LLC | ||
3 | * Copyright 2023 The Refinery Authors <https://refinery.tools/> | ||
4 | * | ||
5 | * SPDX-License-Identifier: Apache-2.0 | ||
6 | * | ||
7 | * This file is based on | ||
8 | * https://github.com/google/or-tools/blob/f3d1d5f6a67356ec38a7fd2ab607624eea8ad3a6/ortools/java/com/google/ortools/Loader.java | ||
9 | * We adapted the loader logic to extract the JNI libraries of Z3 and the corresponding dependencies instead of the | ||
10 | * Google OR-Tools JNI libraries. | ||
11 | */ | ||
12 | package tools.refinery.z3; | ||
13 | |||
14 | import com.sun.jna.Platform; | ||
15 | |||
16 | import java.io.IOException; | ||
17 | import java.net.URI; | ||
18 | import java.net.URISyntaxException; | ||
19 | import java.nio.file.*; | ||
20 | import java.nio.file.attribute.BasicFileAttributes; | ||
21 | import java.util.Map; | ||
22 | |||
23 | public final class Z3SolverLoader { | ||
24 | private static final String JNI_LIBRARY_NAME = "z3java"; | ||
25 | private static final String SOLVER_LIBRARY_NAME = "z3"; | ||
26 | |||
27 | private static boolean loaded; | ||
28 | |||
29 | private Z3SolverLoader() { | ||
30 | throw new IllegalStateException("This is a static utility class and should not be instantiated directly"); | ||
31 | } | ||
32 | |||
33 | public static synchronized void loadNativeLibraries() { | ||
34 | if (loaded) { | ||
35 | return; | ||
36 | } | ||
37 | try { | ||
38 | System.loadLibrary(getOsSpecificLibraryName(JNI_LIBRARY_NAME)); | ||
39 | loaded = true; | ||
40 | return; | ||
41 | } catch (UnsatisfiedLinkError e) { | ||
42 | // Continue, we'll have to extract the libraries from the classpath. | ||
43 | } | ||
44 | try { | ||
45 | extractAndLoad(); | ||
46 | loaded = true; | ||
47 | } catch (IOException e) { | ||
48 | throw new IllegalStateException("Could not extract and load " + JNI_LIBRARY_NAME, e); | ||
49 | } | ||
50 | } | ||
51 | |||
52 | private static void extractAndLoad() throws IOException { | ||
53 | var resourceName = JNI_LIBRARY_NAME + "-" + Platform.RESOURCE_PREFIX; | ||
54 | var resource = Z3SolverLoader.class.getClassLoader().getResource(resourceName); | ||
55 | if (resource == null) { | ||
56 | throw new IllegalStateException("Resource %s was not found".formatted(resourceName)); | ||
57 | } | ||
58 | URI resourceUri; | ||
59 | try { | ||
60 | resourceUri = resource.toURI(); | ||
61 | } catch (URISyntaxException e) { | ||
62 | throw new IllegalStateException("Invalid resource URI: " + resource); | ||
63 | } | ||
64 | FileSystem fileSystem = null; | ||
65 | boolean newFileSystem = false; | ||
66 | Path extractedPath; | ||
67 | try { | ||
68 | try { | ||
69 | fileSystem = FileSystems.newFileSystem(resourceUri, Map.of()); | ||
70 | newFileSystem = true; | ||
71 | } catch (FileSystemAlreadyExistsException e) { | ||
72 | fileSystem = FileSystems.getFileSystem(resourceUri); | ||
73 | } | ||
74 | var resourcePath = fileSystem.provider().getPath(resourceUri); | ||
75 | if (fileSystem.equals(FileSystems.getDefault())) { | ||
76 | extractedPath = resourcePath; | ||
77 | } else { | ||
78 | extractedPath = extract(resourcePath); | ||
79 | } | ||
80 | } finally { | ||
81 | if (newFileSystem) { | ||
82 | fileSystem.close(); | ||
83 | } | ||
84 | } | ||
85 | // We can't rely on RPATH, so we load libraries in reverse dependency order manually. | ||
86 | try { | ||
87 | loadFromPath(extractedPath, SOLVER_LIBRARY_NAME); | ||
88 | } catch (UnsatisfiedLinkError e) { | ||
89 | if (Platform.isWindows()) { | ||
90 | // Try again with our packaged msvcp140 if the system one is missing. | ||
91 | loadFromPathExactName(extractedPath, "vcruntime140.dll"); | ||
92 | loadFromPathExactName(extractedPath, "vcruntime140_1.dll"); | ||
93 | loadFromPathExactName(extractedPath, "msvcp140.dll"); | ||
94 | loadFromPath(extractedPath, SOLVER_LIBRARY_NAME); | ||
95 | } else { | ||
96 | throw e; | ||
97 | } | ||
98 | } | ||
99 | loadFromPath(extractedPath, JNI_LIBRARY_NAME); | ||
100 | } | ||
101 | |||
102 | private static Path extract(Path resourcePath) throws IOException { | ||
103 | var tempDir = Files.createTempDirectory(JNI_LIBRARY_NAME).toAbsolutePath(); | ||
104 | tempDir.toFile().deleteOnExit(); | ||
105 | Files.walkFileTree(resourcePath, new SimpleFileVisitor<>() { | ||
106 | @Override | ||
107 | public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { | ||
108 | var result = super.preVisitDirectory(dir, attrs); | ||
109 | var targetPath = getTargetPath(dir, resourcePath, tempDir); | ||
110 | if (!Files.exists(targetPath)) { | ||
111 | Files.createDirectory(targetPath); | ||
112 | targetPath.toFile().deleteOnExit(); | ||
113 | } | ||
114 | return result; | ||
115 | } | ||
116 | |||
117 | @Override | ||
118 | public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { | ||
119 | var result = super.visitFile(file, attrs); | ||
120 | var targetPath = getTargetPath(file, resourcePath, tempDir); | ||
121 | Files.copy(file, targetPath); | ||
122 | targetPath.toFile().deleteOnExit(); | ||
123 | return result; | ||
124 | } | ||
125 | }); | ||
126 | return tempDir; | ||
127 | } | ||
128 | |||
129 | private static Path getTargetPath(Path sourcePath, Path resourcePath, Path tempDir) { | ||
130 | var targetPath = tempDir.resolve(resourcePath.relativize(sourcePath).toString()).normalize(); | ||
131 | if (!targetPath.startsWith(tempDir)) { | ||
132 | throw new IllegalStateException("Target path '%s' for '%s' is outside '%s'" | ||
133 | .formatted(targetPath, sourcePath, tempDir)); | ||
134 | } | ||
135 | return targetPath; | ||
136 | } | ||
137 | |||
138 | private static String getOsSpecificLibraryName(String libraryName) { | ||
139 | var osSpecificLibraryNamePrefix = Platform.isWindows() ? "lib" : ""; | ||
140 | return osSpecificLibraryNamePrefix + libraryName; | ||
141 | } | ||
142 | |||
143 | private static void loadFromPath(Path extractedPath, String libraryName) { | ||
144 | var osSpecificLibraryName = getOsSpecificLibraryName(libraryName); | ||
145 | loadFromPathExactName(extractedPath, System.mapLibraryName(osSpecificLibraryName)); | ||
146 | } | ||
147 | |||
148 | private static void loadFromPathExactName(Path extractedPath, String exactName) { | ||
149 | var library = extractedPath.resolve(exactName) | ||
150 | .toAbsolutePath() | ||
151 | .toString(); | ||
152 | System.load(library); | ||
153 | } | ||
154 | } | ||