aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar Kristóf Marussy <kristof@marussy.com>2023-11-24 20:10:24 +0100
committerLibravatar Kristóf Marussy <kristof@marussy.com>2024-04-07 14:54:54 +0200
commit67eb9cabe53d13a7d17b27bd78e00f378104ae50 (patch)
treed74b5cf32b7fa97059c28ab8baabcdd885eea447
parentfix(test): Windows line endings comparison (diff)
downloadrefinery-67eb9cabe53d13a7d17b27bd78e00f378104ae50.tar.gz
refinery-67eb9cabe53d13a7d17b27bd78e00f378104ae50.tar.zst
refinery-67eb9cabe53d13a7d17b27bd78e00f378104ae50.zip
build: package Z3 solver native libraries
-rw-r--r--buildSrc/src/main/kotlin/tools/refinery/gradle/internal/java-conventions.gradle.kts8
-rw-r--r--buildSrc/src/main/kotlin/tools/refinery/gradle/interpreter-library.gradle.kts1
-rw-r--r--settings.gradle.kts2
-rw-r--r--z3/build.gradle.kts9
-rw-r--r--z3/buildSrc/build.gradle.kts18
-rw-r--r--z3/buildSrc/settings.gradle.kts13
-rw-r--r--z3/buildSrc/src/main/java/tools/refinery/z3/gradle/ClassFilePatcher.java48
-rw-r--r--z3/buildSrc/src/main/kotlin/tools/refinery/z3/gradle/java-library.gradle.kts75
-rw-r--r--z3/docker/.gitignore5
-rw-r--r--z3/docker/README.md14
-rwxr-xr-xz3/docker/build_in_docker.sh21
-rwxr-xr-xz3/docker/cross_build.sh16
-rw-r--r--z3/gradle.properties9
-rw-r--r--z3/gradle/libs.versions.toml15
-rw-r--r--z3/settings.gradle.kts22
-rw-r--r--z3/subprojects/solver-darwin-aarch64/build.gradle.kts36
-rw-r--r--z3/subprojects/solver-darwin-x86-64/build.gradle.kts36
-rw-r--r--z3/subprojects/solver-linux-aarch64/build.gradle.kts14
-rwxr-xr-xz3/subprojects/solver-linux-aarch64/src/main/resources/z3java-linux-aarch64/libz3.sobin0 -> 29479296 bytes
-rw-r--r--z3/subprojects/solver-linux-aarch64/src/main/resources/z3java-linux-aarch64/libz3.so.license6
-rwxr-xr-xz3/subprojects/solver-linux-aarch64/src/main/resources/z3java-linux-aarch64/libz3java.sobin0 -> 295952 bytes
-rw-r--r--z3/subprojects/solver-linux-aarch64/src/main/resources/z3java-linux-aarch64/libz3java.so.license6
-rw-r--r--z3/subprojects/solver-linux-x86-64/build.gradle.kts36
-rw-r--r--z3/subprojects/solver-win32-x86-64/build.gradle.kts38
-rw-r--r--z3/subprojects/solver/build.gradle.kts107
-rw-r--r--z3/subprojects/solver/src/main/java/tools/refinery/z3/Z3SolverLoader.java154
-rw-r--r--z3/subprojects/solver/src/test/java/tools/refinery/z3/Z3SolverLoaderTest.java30
27 files changed, 738 insertions, 1 deletions
diff --git a/buildSrc/src/main/kotlin/tools/refinery/gradle/internal/java-conventions.gradle.kts b/buildSrc/src/main/kotlin/tools/refinery/gradle/internal/java-conventions.gradle.kts
index 3df17d67..b149a483 100644
--- a/buildSrc/src/main/kotlin/tools/refinery/gradle/internal/java-conventions.gradle.kts
+++ b/buildSrc/src/main/kotlin/tools/refinery/gradle/internal/java-conventions.gradle.kts
@@ -101,6 +101,14 @@ tasks {
101publishing.publications { 101publishing.publications {
102 create<MavenPublication>("mavenJava") { 102 create<MavenPublication>("mavenJava") {
103 from(components["java"]) 103 from(components["java"])
104 pom {
105 licenses {
106 license {
107 name = "Eclipse Public License - v 2.0"
108 url = "https://www.eclipse.org/legal/epl-2.0/"
109 }
110 }
111 }
104 } 112 }
105} 113}
106 114
diff --git a/buildSrc/src/main/kotlin/tools/refinery/gradle/interpreter-library.gradle.kts b/buildSrc/src/main/kotlin/tools/refinery/gradle/interpreter-library.gradle.kts
index ae30ff1b..8bafa1a5 100644
--- a/buildSrc/src/main/kotlin/tools/refinery/gradle/interpreter-library.gradle.kts
+++ b/buildSrc/src/main/kotlin/tools/refinery/gradle/interpreter-library.gradle.kts
@@ -8,7 +8,6 @@ package tools.refinery.gradle
8import tools.refinery.gradle.utils.SonarPropertiesUtils 8import tools.refinery.gradle.utils.SonarPropertiesUtils
9 9
10plugins { 10plugins {
11 id("maven-publish")
12 id("tools.refinery.gradle.java-library") 11 id("tools.refinery.gradle.java-library")
13 id("tools.refinery.gradle.sonarqube") 12 id("tools.refinery.gradle.sonarqube")
14} 13}
diff --git a/settings.gradle.kts b/settings.gradle.kts
index 6e5a72e9..11b188ea 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -33,3 +33,5 @@ for (project in rootProject.children) {
33 project.name = "${rootProject.name}-$projectName" 33 project.name = "${rootProject.name}-$projectName"
34 project.projectDir = file("subprojects/$projectName") 34 project.projectDir = file("subprojects/$projectName")
35} 35}
36
37includeBuild("z3")
diff --git a/z3/build.gradle.kts b/z3/build.gradle.kts
new file mode 100644
index 00000000..74ee6bc8
--- /dev/null
+++ b/z3/build.gradle.kts
@@ -0,0 +1,9 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7plugins {
8 alias(libs.plugins.versions)
9}
diff --git a/z3/buildSrc/build.gradle.kts b/z3/buildSrc/build.gradle.kts
new file mode 100644
index 00000000..adc19562
--- /dev/null
+++ b/z3/buildSrc/build.gradle.kts
@@ -0,0 +1,18 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7plugins {
8 `kotlin-dsl`
9 alias(libs.plugins.versions)
10}
11
12repositories {
13 mavenCentral()
14}
15
16dependencies {
17 implementation(libs.asm)
18}
diff --git a/z3/buildSrc/settings.gradle.kts b/z3/buildSrc/settings.gradle.kts
new file mode 100644
index 00000000..095ab24c
--- /dev/null
+++ b/z3/buildSrc/settings.gradle.kts
@@ -0,0 +1,13 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7dependencyResolutionManagement {
8 versionCatalogs {
9 create("libs") {
10 from(files("../gradle/libs.versions.toml"))
11 }
12 }
13}
diff --git a/z3/buildSrc/src/main/java/tools/refinery/z3/gradle/ClassFilePatcher.java b/z3/buildSrc/src/main/java/tools/refinery/z3/gradle/ClassFilePatcher.java
new file mode 100644
index 00000000..5ac56005
--- /dev/null
+++ b/z3/buildSrc/src/main/java/tools/refinery/z3/gradle/ClassFilePatcher.java
@@ -0,0 +1,48 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6package tools.refinery.z3.gradle;
7
8import org.objectweb.asm.*;
9
10import java.io.File;
11import java.io.FileInputStream;
12import java.io.FileOutputStream;
13import java.io.IOException;
14
15public final class ClassFilePatcher {
16 private ClassFilePatcher() {
17 throw new IllegalStateException("This is a static utility class and should not be instantiated directly");
18 }
19
20 public static void removeClassInitializer(File classFile) throws IOException {
21 byte[] resultBytes;
22 try (var fileReader = new FileInputStream(classFile)) {
23 var classReader = new ClassReader(fileReader);
24 var classWriter = new ClassWriter(classReader, 0);
25 var classVisitor = new Visitor(classWriter);
26 classReader.accept(classVisitor, 0);
27 resultBytes = classWriter.toByteArray();
28 }
29 try (var fileWriter = new FileOutputStream(classFile)) {
30 fileWriter.write(resultBytes);
31 }
32 }
33
34 private static class Visitor extends ClassVisitor {
35 protected Visitor(ClassVisitor classVisitor) {
36 super(Opcodes.ASM9, classVisitor);
37 }
38
39 @Override
40 public MethodVisitor visitMethod(int access, String name, String descriptor, String signature,
41 String[] exceptions) {
42 if (name.equals("<clinit>")) {
43 return null;
44 }
45 return super.visitMethod(access, name, descriptor, signature, exceptions);
46 }
47 }
48}
diff --git a/z3/buildSrc/src/main/kotlin/tools/refinery/z3/gradle/java-library.gradle.kts b/z3/buildSrc/src/main/kotlin/tools/refinery/z3/gradle/java-library.gradle.kts
new file mode 100644
index 00000000..75d64a6f
--- /dev/null
+++ b/z3/buildSrc/src/main/kotlin/tools/refinery/z3/gradle/java-library.gradle.kts
@@ -0,0 +1,75 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6package tools.refinery.z3.gradle
7
8plugins {
9 `java-library`
10 `maven-publish`
11}
12
13java {
14 toolchain {
15 languageVersion.set(JavaLanguageVersion.of(21))
16 }
17}
18
19repositories {
20 mavenCentral()
21
22 // Configuration based on https://stackoverflow.com/a/34327202 to pretend that GitHub is an Ivy repository
23 // in order to take advantage of Gradle dependency caching.
24 val github = ivy {
25 setUrl("https://github.com")
26 patternLayout {
27 artifact("/[organisation]/[module]/releases/download/[module]-[revision]/[classifier].[ext]")
28 artifact("/[organisation]/[module]/archive/refs/tags/[module]-[revision].[ext]")
29 }
30 metadataSources {
31 artifact()
32 }
33 }
34
35 exclusiveContent {
36 forRepositories(github)
37 filter {
38 includeGroup("Z3Prover")
39 }
40 }
41}
42
43val z3: Provider<Configuration> by configurations.registering {
44 isCanBeConsumed = false
45 isCanBeResolved = true
46}
47
48tasks {
49 jar {
50 manifest {
51 attributes(
52 "Bundle-SymbolicName" to "${project.group}.${project.name}",
53 "Bundle-Version" to project.version
54 )
55 }
56 }
57}
58
59publishing.publications {
60 register<MavenPublication>("mavenJava") {
61 from(components["java"])
62 pom {
63 licenses {
64 license {
65 name = "MIT License"
66 url = "https://raw.githubusercontent.com/Z3Prover/z3/master/LICENSE.txt"
67 }
68 license {
69 name = "The Apache License, Version 2.0"
70 url = "http://www.apache.org/licenses/LICENSE-2.0.txt"
71 }
72 }
73 }
74 }
75}
diff --git a/z3/docker/.gitignore b/z3/docker/.gitignore
new file mode 100644
index 00000000..d5b53f81
--- /dev/null
+++ b/z3/docker/.gitignore
@@ -0,0 +1,5 @@
1# SPDX-FileCopyrightText: 2023 The Refinery Authors
2#
3# SPDX-License-Identifier: CC0-1.0
4
5out/
diff --git a/z3/docker/README.md b/z3/docker/README.md
new file mode 100644
index 00000000..d979fd5e
--- /dev/null
+++ b/z3/docker/README.md
@@ -0,0 +1,14 @@
1<!--
2 SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3
4 SPDX-License-Identifier: Apache-2.0
5-->
6
7# Scripts for cross-building Z3 for linux-aarch64
8
9We intentionally build in Ubuntu Focal to avoid a dependency on glibc 2.35.
10See https://github.com/Z3Prover/z3/issues/6572 for details.
11
12This should not be necessary once Z3 4.12.3 is released, as now Z3 nighly
13includes linux-aarch64 builds.
14See https://github.com/Z3Prover/z3/issues/6835 for details.
diff --git a/z3/docker/build_in_docker.sh b/z3/docker/build_in_docker.sh
new file mode 100755
index 00000000..cd28bd32
--- /dev/null
+++ b/z3/docker/build_in_docker.sh
@@ -0,0 +1,21 @@
1#!/usr/bin/env bash
2
3# SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
4#
5# SPDX-License-Identifier: Apache-2.0
6
7set -euo pipefail
8
9z3_version="$1"
10target_uid="$2"
11target_gid="$3"
12
13apt-get update
14apt-get install -y python3 make gcc-aarch64-linux-gnu g++-aarch64-linux-gnu unzip
15wget "https://github.com/Z3Prover/z3/archive/refs/tags/z3-${z3_version}.zip"
16unzip "z3-${z3_version}.zip"
17cd "z3-z3-${z3_version}"
18CXX=aarch64-linux-gnu-g++ CC=aarch64-linux-gnu-gcc python3 scripts/mk_unix_dist.py -f --nodotnet --arch=arm64
19cp --preserve=all "./dist/z3-${z3_version}-arm64-glibc-2.31/bin"/*.so /data/out/
20chown "${target_uid}:${target_gid}" /data/out/*
21
diff --git a/z3/docker/cross_build.sh b/z3/docker/cross_build.sh
new file mode 100755
index 00000000..fe5edd2f
--- /dev/null
+++ b/z3/docker/cross_build.sh
@@ -0,0 +1,16 @@
1#!/usr/bin/env bash
2
3# SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
4#
5# SPDX-License-Identifier: Apache-2.0
6
7set -euo pipefail
8
9z3_version="$(grep '^version=' ../gradle.properties | cut -d'=' -f2)"
10
11rm -rf out
12mkdir out
13docker run --platform linux/amd64 --rm -it -v "${PWD}:/data" --entrypoint /bin/bash docker.io/eclipse-temurin:17-jdk-focal /data/build_in_docker.sh "${z3_version}" "$(id -u)" "$(id -g)"
14rm -rf ../subprojects/solver-linux-aarch64/src/main/resources/z3java-linux-aarch64/*
15cp ./out/* ../subprojects/solver-linux-aarch64/src/main/resources/z3java-linux-aarch64/
16
diff --git a/z3/gradle.properties b/z3/gradle.properties
new file mode 100644
index 00000000..3ef49dc6
--- /dev/null
+++ b/z3/gradle.properties
@@ -0,0 +1,9 @@
1# SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
2#
3# SPDX-License-Identifier: Apache-2.0
4
5file.encoding=UTF-8
6group=tools.refinery.z3
7org.gradle.configuration-cache=false
8org.gradle.parallel=true
9version=4.12.2
diff --git a/z3/gradle/libs.versions.toml b/z3/gradle/libs.versions.toml
new file mode 100644
index 00000000..88cf8083
--- /dev/null
+++ b/z3/gradle/libs.versions.toml
@@ -0,0 +1,15 @@
1# SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
2#
3# SPDX-License-Identifier: Apache-2.0
4
5[versions]
6junit = "5.10.1"
7
8[libraries]
9asm = { group = "org.ow2.asm", name = "asm", version = "9.6" }
10jna = { group = "net.java.dev.jna", name = "jna", version = "5.13.0" }
11junit-api = { group = "org.junit.jupiter", name = "junit-jupiter-api", version.ref = "junit" }
12junit-engine = { group = "org.junit.jupiter", name = "junit-jupiter-engine", version.ref = "junit" }
13
14[plugins]
15versions = { id = "com.github.ben-manes.versions", version = "0.50.0" }
diff --git a/z3/settings.gradle.kts b/z3/settings.gradle.kts
new file mode 100644
index 00000000..a857f5ce
--- /dev/null
+++ b/z3/settings.gradle.kts
@@ -0,0 +1,22 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7rootProject.name = "refinery-z3"
8
9include(
10 "solver",
11 "solver-darwin-aarch64",
12 "solver-darwin-x86-64",
13 "solver-linux-aarch64",
14 "solver-linux-x86-64",
15 "solver-win32-x86-64",
16)
17
18for (project in rootProject.children) {
19 val projectName = project.name
20 project.name = "${rootProject.name}-$projectName"
21 project.projectDir = file("subprojects/$projectName")
22}
diff --git a/z3/subprojects/solver-darwin-aarch64/build.gradle.kts b/z3/subprojects/solver-darwin-aarch64/build.gradle.kts
new file mode 100644
index 00000000..21c9739c
--- /dev/null
+++ b/z3/subprojects/solver-darwin-aarch64/build.gradle.kts
@@ -0,0 +1,36 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7plugins {
8 id("tools.refinery.z3.gradle.java-library")
9}
10
11val classifier = "z3-${version}-arm64-osx-11.0"
12val library = "z3java-darwin-aarch64"
13
14dependencies {
15 z3("Z3Prover:z3:${version}:${classifier}@zip")
16}
17
18val extractZ3Libs by tasks.registering(Sync::class) {
19 dependsOn(configurations.z3)
20 from({
21 val zipFile = configurations.z3.map { it.singleFile }
22 zipTree(zipFile).matching {
23 include("${classifier}/bin/*.so")
24 includeEmptyDirs = false
25 }
26 })
27 eachFile {
28 val pathInBin = relativePath.segments.drop(2).toTypedArray()
29 relativePath = RelativePath(true, library, *pathInBin)
30 }
31 into(layout.buildDirectory.dir("z3-extracted"))
32}
33
34sourceSets.main {
35 resources.srcDir(extractZ3Libs)
36}
diff --git a/z3/subprojects/solver-darwin-x86-64/build.gradle.kts b/z3/subprojects/solver-darwin-x86-64/build.gradle.kts
new file mode 100644
index 00000000..75e59267
--- /dev/null
+++ b/z3/subprojects/solver-darwin-x86-64/build.gradle.kts
@@ -0,0 +1,36 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7plugins {
8 id("tools.refinery.z3.gradle.java-library")
9}
10
11val classifier = "z3-${version}-x64-osx-10.16"
12val library = "z3java-darwin-x86-64"
13
14dependencies {
15 z3("Z3Prover:z3:${version}:${classifier}@zip")
16}
17
18val extractZ3Libs by tasks.registering(Sync::class) {
19 dependsOn(configurations.z3)
20 from({
21 val zipFile = configurations.z3.map { it.singleFile }
22 zipTree(zipFile).matching {
23 include("${classifier}/bin/*.so")
24 includeEmptyDirs = false
25 }
26 })
27 eachFile {
28 val pathInBin = relativePath.segments.drop(2).toTypedArray()
29 relativePath = RelativePath(true, library, *pathInBin)
30 }
31 into(layout.buildDirectory.dir("z3-extracted"))
32}
33
34sourceSets.main {
35 resources.srcDir(extractZ3Libs)
36}
diff --git a/z3/subprojects/solver-linux-aarch64/build.gradle.kts b/z3/subprojects/solver-linux-aarch64/build.gradle.kts
new file mode 100644
index 00000000..a8e7e259
--- /dev/null
+++ b/z3/subprojects/solver-linux-aarch64/build.gradle.kts
@@ -0,0 +1,14 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7plugins {
8 id("tools.refinery.z3.gradle.java-library")
9}
10
11tasks.jar {
12 // License information is redundant here, since it already gets added to the POM.
13 exclude("**/*.license")
14}
diff --git a/z3/subprojects/solver-linux-aarch64/src/main/resources/z3java-linux-aarch64/libz3.so b/z3/subprojects/solver-linux-aarch64/src/main/resources/z3java-linux-aarch64/libz3.so
new file mode 100755
index 00000000..f7013e4f
--- /dev/null
+++ b/z3/subprojects/solver-linux-aarch64/src/main/resources/z3java-linux-aarch64/libz3.so
Binary files differ
diff --git a/z3/subprojects/solver-linux-aarch64/src/main/resources/z3java-linux-aarch64/libz3.so.license b/z3/subprojects/solver-linux-aarch64/src/main/resources/z3java-linux-aarch64/libz3.so.license
new file mode 100644
index 00000000..6f1e5a96
--- /dev/null
+++ b/z3/subprojects/solver-linux-aarch64/src/main/resources/z3java-linux-aarch64/libz3.so.license
@@ -0,0 +1,6 @@
1Copyright (c) Microsoft Corporations
2
3SPDX-License-Identifier: MIT
4
5This file was created from the Z3 4.12.2 release sources using the
6z3/docker/cross_build.sh script in this repository.
diff --git a/z3/subprojects/solver-linux-aarch64/src/main/resources/z3java-linux-aarch64/libz3java.so b/z3/subprojects/solver-linux-aarch64/src/main/resources/z3java-linux-aarch64/libz3java.so
new file mode 100755
index 00000000..e37c7cdb
--- /dev/null
+++ b/z3/subprojects/solver-linux-aarch64/src/main/resources/z3java-linux-aarch64/libz3java.so
Binary files differ
diff --git a/z3/subprojects/solver-linux-aarch64/src/main/resources/z3java-linux-aarch64/libz3java.so.license b/z3/subprojects/solver-linux-aarch64/src/main/resources/z3java-linux-aarch64/libz3java.so.license
new file mode 100644
index 00000000..6f1e5a96
--- /dev/null
+++ b/z3/subprojects/solver-linux-aarch64/src/main/resources/z3java-linux-aarch64/libz3java.so.license
@@ -0,0 +1,6 @@
1Copyright (c) Microsoft Corporations
2
3SPDX-License-Identifier: MIT
4
5This file was created from the Z3 4.12.2 release sources using the
6z3/docker/cross_build.sh script in this repository.
diff --git a/z3/subprojects/solver-linux-x86-64/build.gradle.kts b/z3/subprojects/solver-linux-x86-64/build.gradle.kts
new file mode 100644
index 00000000..89c737b6
--- /dev/null
+++ b/z3/subprojects/solver-linux-x86-64/build.gradle.kts
@@ -0,0 +1,36 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7plugins {
8 id("tools.refinery.z3.gradle.java-library")
9}
10
11val classifier = "z3-${version}-x64-glibc-2.31"
12val library = "z3java-linux-x86-64"
13
14dependencies {
15 z3("Z3Prover:z3:${version}:${classifier}@zip")
16}
17
18val extractZ3Libs by tasks.registering(Sync::class) {
19 dependsOn(configurations.z3)
20 from({
21 val zipFile = configurations.z3.map { it.singleFile }
22 zipTree(zipFile).matching {
23 include("${classifier}/bin/*.so")
24 includeEmptyDirs = false
25 }
26 })
27 eachFile {
28 val pathInBin = relativePath.segments.drop(2).toTypedArray()
29 relativePath = RelativePath(true, library, *pathInBin)
30 }
31 into(layout.buildDirectory.dir("z3-extracted"))
32}
33
34sourceSets.main {
35 resources.srcDir(extractZ3Libs)
36}
diff --git a/z3/subprojects/solver-win32-x86-64/build.gradle.kts b/z3/subprojects/solver-win32-x86-64/build.gradle.kts
new file mode 100644
index 00000000..c5fa0421
--- /dev/null
+++ b/z3/subprojects/solver-win32-x86-64/build.gradle.kts
@@ -0,0 +1,38 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7plugins {
8 id("tools.refinery.z3.gradle.java-library")
9}
10
11val classifier = "z3-${version}-x64-win"
12val library = "z3java-win32-x86-64"
13
14dependencies {
15 z3("Z3Prover:z3:${version}:${classifier}@zip")
16}
17
18val extractZ3Libs by tasks.registering(Sync::class) {
19 dependsOn(configurations.z3)
20 from({
21 val zipFile = configurations.z3.map { it.singleFile }
22 zipTree(zipFile).matching {
23 include("${classifier}/bin/*.dll")
24 // Do not include .NET assembly.
25 exclude("${classifier}/bin/Microsoft.Z3.dll")
26 includeEmptyDirs = false
27 }
28 })
29 eachFile {
30 val pathInBin = relativePath.segments.drop(2).toTypedArray()
31 relativePath = RelativePath(true, library, *pathInBin)
32 }
33 into(layout.buildDirectory.dir("z3-extracted"))
34}
35
36sourceSets.main {
37 resources.srcDir(extractZ3Libs)
38}
diff --git a/z3/subprojects/solver/build.gradle.kts b/z3/subprojects/solver/build.gradle.kts
new file mode 100644
index 00000000..7e898b3e
--- /dev/null
+++ b/z3/subprojects/solver/build.gradle.kts
@@ -0,0 +1,107 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7import tools.refinery.z3.gradle.ClassFilePatcher
8
9plugins {
10 id("tools.refinery.z3.gradle.java-library")
11}
12
13val classifier = "z3-${version}-x64-glibc-2.31"
14val extractedClassesDir = layout.buildDirectory.dir("z3-extracted")
15val extractedSourcesDir = layout.buildDirectory.dir("z3-sources")
16
17java {
18 withJavadocJar()
19 withSourcesJar()
20}
21
22val z3Source: Configuration by configurations.creating {
23 isCanBeConsumed = false
24 isCanBeResolved = true
25}
26
27val extractZ3Jar by tasks.registering(Sync::class) {
28 dependsOn(configurations.z3)
29 from({
30 val zipFile = configurations.z3.map { it.singleFile }
31 val jarFile = zipTree(zipFile).matching {
32 include("${classifier}/bin/com.microsoft.z3.jar")
33 }.singleFile
34 zipTree(jarFile).matching {
35 exclude("META-INF/MANIFEST.MF")
36 includeEmptyDirs = false
37 }
38 })
39 into(extractedClassesDir)
40 doLast {
41 // The class initializer off {@see com.microsoft.z3.Native} will try to load the Z3 native libraries
42 // from the system default library path unless the {@code z3.skipLibraryLoad} system property is set.
43 // Since we don't control the library path or system properties, we remove the class initializer entirely.
44 val nativeClassFile = extractedClassesDir.get().file("com/microsoft/z3/Native.class").asFile
45 ClassFilePatcher.removeClassInitializer(nativeClassFile)
46 }
47}
48
49val extractZ3Source by tasks.registering(Sync::class) {
50 dependsOn(z3Source)
51 from({
52 val zipFile = z3Source.singleFile
53 zipTree(zipFile).matching {
54 include("z3-z3-${version}/src/api/java/**/*")
55 includeEmptyDirs = false
56 }
57 })
58 eachFile {
59 val pathInBin = relativePath.segments.drop(4).toTypedArray()
60 relativePath = RelativePath(true, "com", "microsoft", "z3", *pathInBin)
61 }
62 into(extractedSourcesDir)
63}
64
65tasks.jar {
66 // Add class files to our jar manually.
67 from(extractZ3Jar)
68}
69
70tasks.test {
71 useJUnitPlatform()
72}
73
74tasks.named<Jar>("sourcesJar") {
75 from(extractZ3Source)
76}
77
78tasks.named<Javadoc>("javadoc") {
79 source(sourceSets.main.map { it.allJava })
80 source(fileTree(extractedSourcesDir) {
81 builtBy(extractZ3Source)
82 include("**/*.java")
83 })
84 options {
85 this as StandardJavadocDocletOptions
86 addBooleanOption("Xdoclint:none", true)
87 // {@code -Xmaxwarns 0} will print all warnings, so we must keep at least one.
88 addStringOption("Xmaxwarns", "1")
89 quiet()
90 }
91}
92
93dependencies {
94 z3("Z3Prover:z3:${version}:${classifier}@zip")
95 z3Source("Z3Prover:z3:${version}@zip")
96 // This dependency doesn't get added to Maven metadata, so we have to add the class files to our jar manually.
97 api(files(extractZ3Jar))
98 implementation(libs.jna)
99 implementation(project(":refinery-z3-solver-darwin-aarch64"))
100 implementation(project(":refinery-z3-solver-darwin-x86-64"))
101 implementation(project(":refinery-z3-solver-linux-aarch64"))
102 implementation(project(":refinery-z3-solver-linux-x86-64"))
103 implementation(project(":refinery-z3-solver-win32-x86-64"))
104 testImplementation(libs.junit.api)
105 testRuntimeOnly(libs.junit.engine)
106 testRuntimeOnly("org.junit.platform:junit-platform-launcher")
107}
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 */
12package tools.refinery.z3;
13
14import com.sun.jna.Platform;
15
16import java.io.IOException;
17import java.net.URI;
18import java.net.URISyntaxException;
19import java.nio.file.*;
20import java.nio.file.attribute.BasicFileAttributes;
21import java.util.Map;
22
23public 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}
diff --git a/z3/subprojects/solver/src/test/java/tools/refinery/z3/Z3SolverLoaderTest.java b/z3/subprojects/solver/src/test/java/tools/refinery/z3/Z3SolverLoaderTest.java
new file mode 100644
index 00000000..df3139cc
--- /dev/null
+++ b/z3/subprojects/solver/src/test/java/tools/refinery/z3/Z3SolverLoaderTest.java
@@ -0,0 +1,30 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6package tools.refinery.z3;
7
8import com.microsoft.z3.*;
9import org.junit.jupiter.api.Test;
10
11import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
12import static org.junit.jupiter.api.Assertions.assertEquals;
13
14class Z3SolverLoaderTest {
15 @Test
16 void testLoad() {
17 assertDoesNotThrow(Z3SolverLoader::loadNativeLibraries);
18 try (var context = new Context()) {
19 var solver = context.mkSolver();
20 var a = context.mkConst("a", context.getIntSort());
21 var b = context.mkConst("b", context.getIntSort());
22 solver.add(context.mkEq(a, context.mkInt(3)));
23 solver.add(context.mkEq(b, context.mkMul(context.mkInt(2), a)));
24 assertEquals(Status.SATISFIABLE, solver.check());
25 var model = solver.getModel();
26 var bValue = (IntNum) model.getConstInterp(b);
27 assertEquals(6, bValue.getInt());
28 }
29 }
30}