Skip to content

Use an EISOP CF build #155

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 29 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
73a3c08
Use EISOP CF sources (#118)
wmdietl Apr 10, 2024
644b705
Document that detail messages should not be filtered out (#146)
wmdietl Feb 6, 2024
70ca5bb
Use standard error format (#154)
wmdietl Feb 8, 2024
570d80e
Handle both `https` and `git@` clones (#158)
wmdietl Feb 8, 2024
c8d5933
Use the new `TypeInformationPresenter` to output more type informatio…
wmdietl Apr 10, 2024
11a2980
Map `type.invalid.super.wildcard` as an expected error (#166)
wmdietl Feb 27, 2024
76b33c9
Use the new `TypeUseLocation` default locations (#165)
wmdietl Apr 11, 2024
0c36cdf
Test fixes (#173)
wmdietl Apr 17, 2024
68e9072
Ensure root is set in both worlds. (#174)
wmdietl Apr 17, 2024
2d53314
Fix lower bound of type variables and unspecified subtypes. (#175)
wmdietl Apr 22, 2024
31279fb
Ensure override compatibility with type variables (#171)
wmdietl Apr 22, 2024
5462021
Stop recognizing annotations from the old package. (#181)
cpovirk Jun 30, 2024
9e4d14d
Merge.
cpovirk Jul 9, 2024
fffce55
Update to gradle 8.9 (#187)
wmdietl Jul 24, 2024
39764fb
Use `DetailedTestDiagnostic` from EISOP instead of `DetailMessage` (#…
wmdietl Jul 25, 2024
e1069a2
Introduce a `NullSpecTypeValidator` to ensure unspecified type variab…
wmdietl Jul 25, 2024
36855b8
Use EISOP release and include jspecify/jdk in jar (#188)
wmdietl Jul 25, 2024
af9dfbc
Make a local release of the reference checker (#189)
wmdietl Jul 25, 2024
a005f4e
Merge branch 'main' of github.com:/jspecify/jspecify-reference-checke…
wmdietl Jul 26, 2024
89e055f
Use same version
wmdietl Jul 26, 2024
746e34b
Build a local `eisop/checker-framework` for CI (#196)
wmdietl Jul 26, 2024
3c80e50
Report all errors as [nullness]. (#194)
cpovirk Jul 26, 2024
fc545a8
Fix capture conversion in lenient mode. (#197)
cpovirk Aug 14, 2024
eea8585
Use the new `NoContractsFromMethod` (#201)
wmdietl Aug 23, 2024
27d2334
Update to gradle 8.10 (#203)
wmdietl Aug 29, 2024
36b365f
Use EISOP CF release again (#216)
wmdietl Dec 26, 2024
9cc6b0e
Update to Gradle 8.12 (#215)
wmdietl Dec 26, 2024
dd98a4e
Add subdirectories to dependabot (#217)
wmdietl Jan 7, 2025
77a63c3
Update versions (#218)
wmdietl Jan 7, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,13 @@ updates:
directory: "/"
schedule:
interval: "daily"

- package-ecosystem: "gradle"
directory: "/conformance-test-framework"
schedule:
interval: "daily"

- package-ecosystem: "gradle"
directory: "/usage-demo"
schedule:
interval: "daily"
17 changes: 14 additions & 3 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ jobs:
with:
repository: jspecify/jspecify
path: jspecify
# Clone the EISOP CF if necessary:
# - name: Check out eisop/checker-framework
# uses: actions/checkout@v4
# with:
# repository: eisop/checker-framework
# path: checker-framework
- name: Set up Java
uses: actions/setup-java@v4
with:
Expand All @@ -28,17 +34,22 @@ jobs:
uses: gradle/gradle-build-action@v3
- name: Build and Test
run: ./gradlew build conformanceTests demoTest --include-build ../jspecify
# If a cloned EISOP CF is needed, use the following:
# run: ./gradlew build conformanceTests demoTest --include-build ../jspecify --include-build ../checker-framework
env:
SHALLOW: 1
- name: Check out jspecify/samples-google-prototype
JSPECIFY_CONFORMANCE_TEST_MODE: details
- name: Check out jspecify/samples-google-prototype-eisop
if: always()
run: |
git fetch --depth=1 origin samples-google-prototype
git checkout samples-google-prototype
git fetch --depth=1 origin samples-google-prototype-eisop
git checkout samples-google-prototype-eisop
working-directory: jspecify
- name: Run Samples Tests
if: always()
run: ./gradlew jspecifySamplesTest --include-build ../jspecify
# If a cloned EISOP CF is needed, use the following:
# run: ./gradlew jspecifySamplesTest --include-build ../jspecify --include-build ../checker-framework

publish-snapshot:
name: Publish Conformance Test Framework Snapshot
Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,21 @@ But that's its *only* job. Notably, it is:

## Relationship to Checker Framework and EISOP

The [EISOP project](https://eisop.github.io/) maintains a fork of [Checker Framework](https://checkerframework.org/), and JSpecify conformance is one of its primary goals.
The [EISOP project](https://eisop.github.io/) maintains a fork of the [Checker Framework](https://checkerframework.org/), and JSpecify conformance is one of its goals.

This tool happens to be built on top of another fork of these ([here](https://github.com/jspecify/checker-framework)). However, please view this relationship as **implementation detail** only. Building a reference checker from scratch would simply have been too difficult, so we needed to base it on some existing tool. The choice of which tool was made purely for expediency and is **subject to change**.
This tool is built on top of the [EISOP Checker Framework](https://github.com/eisop/checker-framework). However, please view this relationship as **implementation detail** only. Building a reference checker from scratch would simply have been too difficult, so we needed to base it on some existing tool. The choice of which tool was made purely for expediency and is **subject to change**.

## Usage

Building and running this tool requires building code from several other repositories, but these instructions will take care of that automatically.
Building and running this tool depends on code from several other repositories, but these instructions will take care of that automatically.

These instructions might require workarounds or fail outright. Please file an issue if you have any trouble!

### Prework

Ideally set `JAVA_HOME` to a JDK 11 or JDK 17 installation.

Make sure you have Apache Maven installed and in your PATH, or the Gradle build will fail:
Make sure you have Apache Maven installed and in your PATH, or the `demo` script will fail:

```sh
mvn
Expand Down
171 changes: 150 additions & 21 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,26 @@ plugins {

id 'com.diffplug.spotless' version '6.25.0'
id 'io.github.gradle-nexus.publish-plugin' version '1.3.0'
id 'net.ltgt.errorprone' version '3.0.1'
id 'net.ltgt.errorprone' version '4.1.0'
// To show task list as a tree, run: ./gradlew <taskname> taskTree
id 'com.dorongold.task-tree' version '4.0.0'
}

// Nexus Publish plugin requires a group/version at the root project.
group = 'org.jspecify.reference'
version = '0.0.0-SNAPSHOT'

sourceSets {
main {
resources {
// Minimized jspecify/jdk
srcDirs += [
"${buildDir}/generated/resources"
]
}
}
}

repositories {
mavenLocal()
maven {
Expand All @@ -35,10 +48,14 @@ nexusPublishing {
}

ext {
checkerFramework = gradle.includedBuild("checker-framework")
// null if not included with `--include-build path/to/checker-framework`
checkerFramework = gradle.includedBuilds.find { it.name == 'checker-framework' }

// null if not included with `--include-build path/to/jspecify`
jspecify = gradle.includedBuilds.find { it.name == 'jspecify' }

// Location of the jspecify/jdk clone, relative to this directory
jspecifyJdkHome = '../jdk'
}

configurations {
Expand All @@ -61,8 +78,10 @@ java {
dependencies {
implementation libs.checkerFramework.checker
implementation libs.checkerFramework.checker.qual
implementation libs.checkerFramework.framework
implementation libs.checkerFramework.javacutil
// Eventually, we would want to only depend on `framework` and
// `javacutil` artifacts instead of the entire `checker`.
// implementation libs.checkerFramework.framework
// implementation libs.checkerFramework.javacutil

implementation libs.jspecify

Expand All @@ -78,21 +97,31 @@ dependencies {
errorprone libs.errorProne.core
}

// Assemble checker-framework when assembling the reference checker.
assemble.dependsOn(checkerFramework.task(":assemble"))
// If built with `--include-build path/to/checker-framework` then
// assemble checker-framework when assembling the reference checker.
if (checkerFramework != null) {
compileJava.dependsOn(checkerFramework.task(":checker:assembleForJavac"))
}

// If built with `--include-build path/to/jspecify` then
// assemble jspecify when assembling the reference checker.
if (jspecify != null) {
assemble.dependsOn(jspecify.task(':assemble'))
}

// Enable exec/javaexec
interface InjectedExecOps {
@Inject
ExecOperations getExecOps()
}

tasks.withType(JavaCompile).configureEach {
options.compilerArgs.add("-Xlint:all")
// ErrorProne makes suppressing these easier
options.compilerArgs.add("-Xlint:-fallthrough")

options.errorprone.disable("BadImport")
options.errorprone.disable("VoidUsed")

options.compilerArgs.addAll(
[
Expand All @@ -111,6 +140,56 @@ tasks.withType(JavaCompile).configureEach {
.collect { "--add-exports=jdk.compiler/com.sun.tools.javac.$it=ALL-UNNAMED" })
}

tasks.register('includeJSpecifyJDK') {
group = 'Build'
dependsOn 'compileJava'

def srcDir = "${jspecifyJdkHome}/src"
// This directory needs to be stored at the top-level of the resulting .jar file.
// org.checkerframework.framework.stub.AnnotationFileElementTypes will then load
// the JDK classes from here instead of from checker.jar.
def dstDir = "${buildDir}/generated/resources/annotated-jdk/src/"

inputs.dir file(srcDir)
outputs.dir file(dstDir)

def injected = project.objects.newInstance(InjectedExecOps)

doLast {
FileTree srcTree = fileTree(dir: srcDir)
NavigableSet<String> specFiles = new TreeSet<>();
srcTree.visit { FileVisitDetails fvd ->
if (!fvd.file.isDirectory() && fvd.file.name.matches('.*\\.java')) {
fvd.getFile().readLines().any { line ->
if (line.contains('org.jspecify')) {
specFiles.add(fvd.file.absolutePath)
return true;
}
}
}
}
String absoluteSrcDir = file(srcDir).absolutePath
int srcPrefixSize = absoluteSrcDir.size()
copy {
from(srcDir)
into(dstDir)
for (String specFile : specFiles) {
include specFile.substring(srcPrefixSize)
}
}
injected.execOps.javaexec {
classpath = sourceSets.main.runtimeClasspath
standardOutput = System.out
errorOutput = System.err

mainClass = 'org.checkerframework.framework.stub.JavaStubifier'
args dstDir
}
}
}

processResources.dependsOn(includeJSpecifyJDK)

tasks.withType(Test).configureEach {
if (!JavaVersion.current().java9Compatible) {
jvmArgs "-Xbootclasspath/p:${configurations.errorproneJavac.asPath}"
Expand All @@ -132,14 +211,16 @@ tasks.withType(Test).configureEach {
showStackTraces = false
showStandardStreams = true
events "failed"
exceptionFormat "full"
exceptionFormat = "full"
}
}

test {
include '**/NullSpecTest$Minimal.class'

inputs.files("${rootDir}/tests/minimal")

include '**/NullSpecTest$Regression.class'
inputs.files("${rootDir}/tests/regression")
}

tasks.register('jspecifySamplesTest', Test) {
Expand Down Expand Up @@ -217,24 +298,28 @@ tasks.register('demoTest', Exec) {
See https://github.com/jspecify/jspecify-reference-checker/issues/81
*/

def cfQualJar =
checkerFramework.projectDir.toPath()
.resolve("checker-qual/build/libs/checker-qual-${libs.versions.checkerFramework.get()}.jar")

if (!cfQualJar.toFile().exists()) {
mkdir(cfQualJar.parent)
exec {
executable 'jar'
args = [
'cf',
cfQualJar,
buildFile.path // Use this build script file!
]
if (checkerFramework != null) {
def cfQualJar =
checkerFramework.projectDir.toPath()
.resolve("checker-qual/build/libs/checker-qual-${libs.versions.checkerFramework.get()}.jar")
def injected = project.objects.newInstance(InjectedExecOps)

if (!cfQualJar.toFile().exists()) {
mkdir(cfQualJar.parent)
injected.execOps.exec {
executable 'jar'
args = [
'cf',
cfQualJar,
buildFile.path // Use this build script file!
]
}
}
}

spotless {
java {
target '**/*.java'
googleJavaFormat()
formatAnnotations()
}
Expand All @@ -260,3 +345,47 @@ eclipse.classpath {
}
}
}

publishing {
publications {
jspecifyReferenceChecker(MavenPublication) {
pom {
groupId = 'org.jspecify.reference'
artifactId = 'checker'
version = project.version
name = 'JSpecify Reference Checker'
description = 'The JSpecify Reference Checker'
url = 'http://jspecify.org/'
from components.java
licenses {
license {
name = 'The Apache License, Version 2.0'
url = 'http://www.apache.org/licenses/LICENSE-2.0.txt'
}
}
scm {
connection = 'scm:git:[email protected]:jspecify/jspecify-reference-checker.git'
developerConnection = 'scm:git:[email protected]:jspecify/jspecify-reference-checker.git'
url = 'https://github.com/jspecify/jspecify-reference-checker'
}
developers {
developer {
id = 'cpovirk'
name = 'Chris Povirk'
email = '[email protected]'
}
developer {
id = 'netdpb'
name = 'David P. Baker'
email = '[email protected]'
}
developer {
id = 'wmdietl'
name = 'Werner M. Dietl'
email = '[email protected]'
}
}
}
}
}
}
4 changes: 2 additions & 2 deletions conformance-test-framework/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ plugins {
id 'java-library'
}

group 'org.jspecify.conformance'
version '0.0.0-SNAPSHOT'
group = 'org.jspecify.conformance'
version = '0.0.0-SNAPSHOT'

repositories {
mavenCentral()
Expand Down
18 changes: 17 additions & 1 deletion demo
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,20 @@ if [ ! -e "${jspecify}" ]; then
-DoutputDirectory="$(dirname "${jspecify}")"
fi
fi

checkerFrameworkDir="${dir}/../checker-framework/"
checkerFrameworkJar="${checkerFrameworkDir}/checker/dist/checker.jar"
if [ ! -e "${checkerFrameworkJar}" ]; then
cfVersion="3.42.0-eisop4"
checkerFrameworkJar="${dir}/build/checker-${cfVersion}-all.jar"
if [ ! -e "${checkerFrameworkJar}" ]; then
echo "Downloading $(basename "${checkerFrameworkJar}") from Maven central"
mvn -q org.apache.maven.plugins:maven-dependency-plugin:3.7.1:copy \
-Dartifact="io.github.eisop:checker:${cfVersion}:jar:all" \
-DoutputDirectory="$(dirname "${checkerFrameworkJar}")"
fi
fi

jspecify_reference_checker="${dir}/build/libs/jspecify-reference-checker-0.0.0-SNAPSHOT.jar"
if [ ! -e "${jspecify_reference_checker}" ]; then
echo "Assembling jspecify-reference-checker"
Expand All @@ -25,9 +39,11 @@ ourclasspath="${jspecify}:${jspecify_reference_checker}"

export CLASSPATH="${ourclasspath}:$CLASSPATH"

$dir/../checker-framework/checker/bin/javac \
java -jar "${checkerFrameworkJar}" \
-processorpath "${ourclasspath}" \
-processor com.google.jspecify.nullness.NullSpecChecker \
-checkerQualJar "${checkerFrameworkJar}" \
-checkerUtilJar "${checkerFrameworkJar}" \
-AcheckImpl \
-AassumePure \
-AsuppressWarnings=contracts.conditional.postcondition.false.methodref,contracts.conditional.postcondition.false.override,contracts.conditional.postcondition.true.methodref,contracts.conditional.postcondition.true.override,purity.methodref,purity.overriding,type.anno.before.decl.anno,type.anno.before.modifier \
Expand Down
Loading
Loading