diff options
author | Kristóf Marussy <kristof@marussy.com> | 2023-11-24 20:10:24 +0100 |
---|---|---|
committer | Kristóf Marussy <kristof@marussy.com> | 2024-04-07 14:54:54 +0200 |
commit | 67eb9cabe53d13a7d17b27bd78e00f378104ae50 (patch) | |
tree | d74b5cf32b7fa97059c28ab8baabcdd885eea447 | |
parent | fix(test): Windows line endings comparison (diff) | |
download | refinery-67eb9cabe53d13a7d17b27bd78e00f378104ae50.tar.gz refinery-67eb9cabe53d13a7d17b27bd78e00f378104ae50.tar.zst refinery-67eb9cabe53d13a7d17b27bd78e00f378104ae50.zip |
build: package Z3 solver native libraries
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 { | |||
101 | publishing.publications { | 101 | publishing.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 | |||
8 | import tools.refinery.gradle.utils.SonarPropertiesUtils | 8 | import tools.refinery.gradle.utils.SonarPropertiesUtils |
9 | 9 | ||
10 | plugins { | 10 | plugins { |
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 | |||
37 | includeBuild("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 | |||
7 | plugins { | ||
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 | |||
7 | plugins { | ||
8 | `kotlin-dsl` | ||
9 | alias(libs.plugins.versions) | ||
10 | } | ||
11 | |||
12 | repositories { | ||
13 | mavenCentral() | ||
14 | } | ||
15 | |||
16 | dependencies { | ||
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 | |||
7 | dependencyResolutionManagement { | ||
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 | */ | ||
6 | package tools.refinery.z3.gradle; | ||
7 | |||
8 | import org.objectweb.asm.*; | ||
9 | |||
10 | import java.io.File; | ||
11 | import java.io.FileInputStream; | ||
12 | import java.io.FileOutputStream; | ||
13 | import java.io.IOException; | ||
14 | |||
15 | public 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 | */ | ||
6 | package tools.refinery.z3.gradle | ||
7 | |||
8 | plugins { | ||
9 | `java-library` | ||
10 | `maven-publish` | ||
11 | } | ||
12 | |||
13 | java { | ||
14 | toolchain { | ||
15 | languageVersion.set(JavaLanguageVersion.of(21)) | ||
16 | } | ||
17 | } | ||
18 | |||
19 | repositories { | ||
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 | |||
43 | val z3: Provider<Configuration> by configurations.registering { | ||
44 | isCanBeConsumed = false | ||
45 | isCanBeResolved = true | ||
46 | } | ||
47 | |||
48 | tasks { | ||
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 | |||
59 | publishing.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 | |||
5 | out/ | ||
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 | |||
9 | We intentionally build in Ubuntu Focal to avoid a dependency on glibc 2.35. | ||
10 | See https://github.com/Z3Prover/z3/issues/6572 for details. | ||
11 | |||
12 | This should not be necessary once Z3 4.12.3 is released, as now Z3 nighly | ||
13 | includes linux-aarch64 builds. | ||
14 | See 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 | |||
7 | set -euo pipefail | ||
8 | |||
9 | z3_version="$1" | ||
10 | target_uid="$2" | ||
11 | target_gid="$3" | ||
12 | |||
13 | apt-get update | ||
14 | apt-get install -y python3 make gcc-aarch64-linux-gnu g++-aarch64-linux-gnu unzip | ||
15 | wget "https://github.com/Z3Prover/z3/archive/refs/tags/z3-${z3_version}.zip" | ||
16 | unzip "z3-${z3_version}.zip" | ||
17 | cd "z3-z3-${z3_version}" | ||
18 | CXX=aarch64-linux-gnu-g++ CC=aarch64-linux-gnu-gcc python3 scripts/mk_unix_dist.py -f --nodotnet --arch=arm64 | ||
19 | cp --preserve=all "./dist/z3-${z3_version}-arm64-glibc-2.31/bin"/*.so /data/out/ | ||
20 | chown "${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 | |||
7 | set -euo pipefail | ||
8 | |||
9 | z3_version="$(grep '^version=' ../gradle.properties | cut -d'=' -f2)" | ||
10 | |||
11 | rm -rf out | ||
12 | mkdir out | ||
13 | docker 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)" | ||
14 | rm -rf ../subprojects/solver-linux-aarch64/src/main/resources/z3java-linux-aarch64/* | ||
15 | cp ./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 | |||
5 | file.encoding=UTF-8 | ||
6 | group=tools.refinery.z3 | ||
7 | org.gradle.configuration-cache=false | ||
8 | org.gradle.parallel=true | ||
9 | version=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] | ||
6 | junit = "5.10.1" | ||
7 | |||
8 | [libraries] | ||
9 | asm = { group = "org.ow2.asm", name = "asm", version = "9.6" } | ||
10 | jna = { group = "net.java.dev.jna", name = "jna", version = "5.13.0" } | ||
11 | junit-api = { group = "org.junit.jupiter", name = "junit-jupiter-api", version.ref = "junit" } | ||
12 | junit-engine = { group = "org.junit.jupiter", name = "junit-jupiter-engine", version.ref = "junit" } | ||
13 | |||
14 | [plugins] | ||
15 | versions = { 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 | |||
7 | rootProject.name = "refinery-z3" | ||
8 | |||
9 | include( | ||
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 | |||
18 | for (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 | |||
7 | plugins { | ||
8 | id("tools.refinery.z3.gradle.java-library") | ||
9 | } | ||
10 | |||
11 | val classifier = "z3-${version}-arm64-osx-11.0" | ||
12 | val library = "z3java-darwin-aarch64" | ||
13 | |||
14 | dependencies { | ||
15 | z3("Z3Prover:z3:${version}:${classifier}@zip") | ||
16 | } | ||
17 | |||
18 | val 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 | |||
34 | sourceSets.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 | |||
7 | plugins { | ||
8 | id("tools.refinery.z3.gradle.java-library") | ||
9 | } | ||
10 | |||
11 | val classifier = "z3-${version}-x64-osx-10.16" | ||
12 | val library = "z3java-darwin-x86-64" | ||
13 | |||
14 | dependencies { | ||
15 | z3("Z3Prover:z3:${version}:${classifier}@zip") | ||
16 | } | ||
17 | |||
18 | val 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 | |||
34 | sourceSets.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 | |||
7 | plugins { | ||
8 | id("tools.refinery.z3.gradle.java-library") | ||
9 | } | ||
10 | |||
11 | tasks.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 @@ | |||
1 | Copyright (c) Microsoft Corporations | ||
2 | |||
3 | SPDX-License-Identifier: MIT | ||
4 | |||
5 | This file was created from the Z3 4.12.2 release sources using the | ||
6 | z3/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 @@ | |||
1 | Copyright (c) Microsoft Corporations | ||
2 | |||
3 | SPDX-License-Identifier: MIT | ||
4 | |||
5 | This file was created from the Z3 4.12.2 release sources using the | ||
6 | z3/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 | |||
7 | plugins { | ||
8 | id("tools.refinery.z3.gradle.java-library") | ||
9 | } | ||
10 | |||
11 | val classifier = "z3-${version}-x64-glibc-2.31" | ||
12 | val library = "z3java-linux-x86-64" | ||
13 | |||
14 | dependencies { | ||
15 | z3("Z3Prover:z3:${version}:${classifier}@zip") | ||
16 | } | ||
17 | |||
18 | val 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 | |||
34 | sourceSets.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 | |||
7 | plugins { | ||
8 | id("tools.refinery.z3.gradle.java-library") | ||
9 | } | ||
10 | |||
11 | val classifier = "z3-${version}-x64-win" | ||
12 | val library = "z3java-win32-x86-64" | ||
13 | |||
14 | dependencies { | ||
15 | z3("Z3Prover:z3:${version}:${classifier}@zip") | ||
16 | } | ||
17 | |||
18 | val 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 | |||
36 | sourceSets.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 | |||
7 | import tools.refinery.z3.gradle.ClassFilePatcher | ||
8 | |||
9 | plugins { | ||
10 | id("tools.refinery.z3.gradle.java-library") | ||
11 | } | ||
12 | |||
13 | val classifier = "z3-${version}-x64-glibc-2.31" | ||
14 | val extractedClassesDir = layout.buildDirectory.dir("z3-extracted") | ||
15 | val extractedSourcesDir = layout.buildDirectory.dir("z3-sources") | ||
16 | |||
17 | java { | ||
18 | withJavadocJar() | ||
19 | withSourcesJar() | ||
20 | } | ||
21 | |||
22 | val z3Source: Configuration by configurations.creating { | ||
23 | isCanBeConsumed = false | ||
24 | isCanBeResolved = true | ||
25 | } | ||
26 | |||
27 | val 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 | |||
49 | val 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 | |||
65 | tasks.jar { | ||
66 | // Add class files to our jar manually. | ||
67 | from(extractZ3Jar) | ||
68 | } | ||
69 | |||
70 | tasks.test { | ||
71 | useJUnitPlatform() | ||
72 | } | ||
73 | |||
74 | tasks.named<Jar>("sourcesJar") { | ||
75 | from(extractZ3Source) | ||
76 | } | ||
77 | |||
78 | tasks.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 | |||
93 | dependencies { | ||
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 | */ | ||
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 | } | ||
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 | */ | ||
6 | package tools.refinery.z3; | ||
7 | |||
8 | import com.microsoft.z3.*; | ||
9 | import org.junit.jupiter.api.Test; | ||
10 | |||
11 | import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; | ||
12 | import static org.junit.jupiter.api.Assertions.assertEquals; | ||
13 | |||
14 | class 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 | } | ||