diff --git a/build.gradle b/build.gradle index 3928cb7ee1dc..56afa5e4a9a1 100644 --- a/build.gradle +++ b/build.gradle @@ -118,12 +118,19 @@ switch (JavaVersion.current()) { break; } -task setLocalRepo(type:Exec) { - commandLine 'git', 'worktree', 'list' - standardOutput = new ByteArrayOutputStream() +task setLocalRepo(type: Exec) { + def outputFile = layout.buildDirectory.file("setLocalRepo-output.txt") + outputs.file(outputFile) + + commandLine = [ + 'bash', + '-c', + "git worktree list > ${outputFile.get().asFile.absolutePath}" + ] + doLast { - String worktreeList = standardOutput.toString() - localRepo = worktreeList.substring(0, worktreeList.indexOf(' ')) + '/.git' + String worktreeList = outputFile.get().asFile.text + ext.localRepo = worktreeList.substring(0, worktreeList.indexOf(' ')) + '/.git' } } @@ -146,7 +153,7 @@ spotless { // > Could not create task ':checker-qual:spotlessJava'. // > File signature can only be created for existing regular files, given: // .../checker-framework/checker-qual/build/libs/checker-qual-3.25.1-SNAPSHOT.jar - predeclareDepsFromBuildscript() + predeclareDeps() } spotlessPredeclare { @@ -1004,31 +1011,40 @@ subprojects { withSourcesJar() } + // Capture project references early. + def projectVersion = project.version + def projectName = project.name.replaceAll('-', '.') + // Things in this block reference definitions in the subproject that do not exist, // until the project is evaluated. afterEvaluate { // Adds manifest to all Jar files tasks.withType(Jar) { includeEmptyDirs = false - if (archiveFileName.get().startsWith('checker-qual') || archiveFileName.get().startsWith('checker-util')) { - metaInf { - from './LICENSE.txt' - } - } else { - metaInf { - from "${rootDir}/LICENSE.txt" + + archiveFileName.map { fileName -> + if (fileName.startsWith('checker-qual') || fileName.startsWith('checker-util')) { + metaInf { + from './LICENSE.txt' + } + } else { + metaInf { + from "${rootDir}/LICENSE.txt" + } } } manifest { - attributes('Implementation-Version': "${project.version}") + attributes('Implementation-Version': "${projectVersion}") attributes('Implementation-URL': 'https://eisop.github.io/') - if (! archiveFileName.get().endsWith('source.jar') && ! archiveFileName.get().startsWith('checker-qual')) { - attributes('Automatic-Module-Name': 'org.checkerframework.' + project.name.replaceAll('-', '.')) - } - if (archiveFileName.get().startsWith('checker-qual') || archiveFileName.get().startsWith('checker-util')) { - attributes('Bundle-License': 'MIT') - } else { - attributes('Bundle-License': '(GPL-2.0-only WITH Classpath-exception-2.0)') + archiveFileName.map { fileName -> + if (! fileName.endsWith('source.jar') && ! fileName.startsWith('checker-qual')) { + attributes('Automatic-Module-Name': 'org.checkerframework.' + projectName) + } + if (fileName.startsWith('checker-qual') || fileName.startsWith('checker-util')) { + attributes('Bundle-License': 'MIT') + } else { + attributes('Bundle-License': '(GPL-2.0-only WITH Classpath-exception-2.0)') + } } } } diff --git a/checker-qual-android/build.gradle b/checker-qual-android/build.gradle index 1f1b4b58c169..22972f2fbc8a 100644 --- a/checker-qual-android/build.gradle +++ b/checker-qual-android/build.gradle @@ -1,12 +1,14 @@ evaluationDependsOn(':checker-qual') +tasks.register('deletePreviousSources') { + // Delete the directory in case a previously copied file should no longer be in checker-qual. + delete file(layout.buildDirectory.dir("generated/sources/main/java")) +} + task copySources(type: Copy) { description = 'Copy checker-qual source to checker-qual-android' + dependsOn 'deletePreviousSources' includeEmptyDirs = false - doFirst { - // Delete the directory in case a previously copied file should no longer be in checker-qual - delete file(layout.buildDirectory.dir("generated/sources/main/java")) - } from files(project(':checker-qual').sourceSets.main.java) include '**/*.java' exclude '**/SignednessUtilExtra.java' diff --git a/checker-qual/build.gradle b/checker-qual/build.gradle index ba026310f1cd..527aaefaf1b9 100644 --- a/checker-qual/build.gradle +++ b/checker-qual/build.gradle @@ -39,6 +39,10 @@ jar { manifest { attributes('Export-Package': '*') } + // Work around http://github.com/bndtools/bnd/issues/6346 + bundle { + properties.put("project.group", provider({project.group})) + } } apply from: rootProject.file('gradle-mvn-push.gradle') diff --git a/checker/build.gradle b/checker/build.gradle index 0d6ddc8d42f6..54932a093544 100644 --- a/checker/build.gradle +++ b/checker/build.gradle @@ -133,45 +133,43 @@ jar { // This task differs from the `assemble` task in that it does not build Javadoc. // It is useful for those who only want to run `javac`. // checker.jar is copied to checker/dist/ when it is built by the shadowJar task. -task assembleForJavac(dependsOn: shadowJar, group: 'Build') { +task assembleForJavac(group: 'Build') { description = 'Builds or downloads jars required by CheckerMain and puts them in checker/dist/.' + dependsOn 'copyCheckerQualJar' + dependsOn 'copyCheckerUtilJar' + dependsOn 'copyJavacJar' +} + +tasks.register('copyCheckerQualJar', Copy) { + dependsOn ':checker-qual:jar' dependsOn project(':checker-qual').tasks.jar - def checkerQualJarFile = file(project(':checker-qual').tasks.getByName('jar').archiveFile) - def checkerUtilJarFile = file(project(':checker-util').tasks.getByName('jar').archiveFile) - doLast { - if (!checkerQualJarFile.exists()) { - throw new GradleException('File not found: ' + checkerQualJarFile) - } - copy { - from checkerQualJarFile - into "${projectDir}/dist" - rename { String fileName -> - // remove version number on checker-qual.jar - fileName.replace(fileName, 'checker-qual.jar') - } - } + from project(':checker-qual').tasks.jar.archiveFile + into "${projectDir}/dist" + rename { String fileName -> + // remove version number on checker-qual.jar + fileName.replace(fileName, 'checker-qual.jar') + } +} - if (!checkerUtilJarFile.exists()) { - throw new GradleException('File not found: ' + checkerUtilJarFile) - } - copy { - from checkerUtilJarFile - into "${projectDir}/dist" - rename { String fileName -> - // remove version number on checker-util.jar - fileName.replace(fileName, 'checker-util.jar') - } - } +tasks.register('copyCheckerUtilJar', Copy) { + dependsOn 'shadowJar' + dependsOn project(':checker-util').tasks.jar - copy { - // This is required to *run* the Checker Framework on JDK 8. - from configurations.javacJar - into "${projectDir}/dist" - rename { String fileName -> - fileName.replace(fileName, 'javac.jar') - } - } + from project(':checker-util').tasks.jar.archiveFile + into "${projectDir}/dist" + rename { String fileName -> + // remove version number on checker-util.jar + fileName.replace(fileName, 'checker-util.jar') + } +} + +tasks.register('copyJavacJar', Copy) { + // This is required to *run* the Checker Framework on JDK 8. + from configurations.javacJar + into "${projectDir}/dist" + rename { String fileName -> + fileName.replace(fileName, 'javac.jar') } } @@ -234,17 +232,22 @@ shadowJar { // To see what files are incorporated into the shadow jar file: // doFirst { println sourceSets.main.runtimeClasspath.asPath } archiveClassifier = 'all' - doLast{ - copy { - from archiveFile.get() - into file("${projectDir}/dist") - rename 'checker.*', 'checker.jar' - } - } minimize() } +shadowJar.finalizedBy('copyCheckerJar') + +tasks.register('copyCheckerJar', Copy) { + dependsOn ':checker:shadowJar' + + // This used `from archiveFile.get()` when it was part of shadowJar. + // Look at e.g. copyCheckerQualJar whether there is a better way to do this. + from file("${projectDir}/build/libs/checker-${project.version}-all.jar") + into file("${projectDir}/dist") + rename 'checker.*', 'checker.jar' +} + artifacts { // Don't add this here or else the Javadoc and the sources jar is built during the assemble task. // archives allJavadocJar @@ -257,8 +260,12 @@ artifacts { clean { def injected = project.objects.newInstance(InjectedExecOps) + def bldDir = buildDir + def distDir = "${projectDir}/dist" + def cmdlineDir = file('tests/command-line') + def nullnessextraDir = file('tests/nullness-extra') - delete("${projectDir}/dist") + delete(distDir) delete('tests/calledmethods-delomboked') delete('tests/ainfer-testchecker/annotated') delete('tests/ainfer-testchecker/inference-output') @@ -271,20 +278,18 @@ clean { delete('tests/build') doLast { injected.execOps.exec { - workingDir = file('tests/command-line') + workingDir = cmdlineDir commandLine 'make', 'clean' } injected.execOps.exec { - workingDir = file('tests/nullness-extra') + workingDir = nullnessextraDir commandLine 'make', 'clean' } - } -} -clean.doLast { - while (buildDir.exists()) { - sleep(10000) // wait 10 seconds - buildDir.deleteDir() + while(bldDir.exists()) { + sleep(10000) // wait 10 seconds + bldDir.deleteDir() + } } } @@ -1090,7 +1095,7 @@ final checkerPom(publication) { publishing { publications { checker(MavenPublication) { - project.shadow.component it + from components.shadow checkerPom it artifact checkerJar artifact allSourcesJar diff --git a/docs/build.gradle b/docs/build.gradle index ecb64aac695e..a5d2858d7ddc 100644 --- a/docs/build.gradle +++ b/docs/build.gradle @@ -6,11 +6,12 @@ interface InjectedExecOps { clean { def injected = project.objects.newInstance(InjectedExecOps) + def examples = file('examples') delete('api/') doLast { injected.execOps.exec { - workingDir = file('examples') + workingDir = examples commandLine 'make', 'clean' } } diff --git a/docs/examples/errorprone/gradle.properties b/docs/examples/errorprone/gradle.properties new file mode 120000 index 000000000000..591a8b3f8db6 --- /dev/null +++ b/docs/examples/errorprone/gradle.properties @@ -0,0 +1 @@ +../../../gradle.properties \ No newline at end of file diff --git a/docs/examples/jspecify/gradle.properties b/docs/examples/jspecify/gradle.properties new file mode 120000 index 000000000000..591a8b3f8db6 --- /dev/null +++ b/docs/examples/jspecify/gradle.properties @@ -0,0 +1 @@ +../../../gradle.properties \ No newline at end of file diff --git a/docs/examples/lombok/gradle.properties b/docs/examples/lombok/gradle.properties new file mode 120000 index 000000000000..591a8b3f8db6 --- /dev/null +++ b/docs/examples/lombok/gradle.properties @@ -0,0 +1 @@ +../../../gradle.properties \ No newline at end of file diff --git a/docs/examples/nullaway/gradle.properties b/docs/examples/nullaway/gradle.properties new file mode 120000 index 000000000000..591a8b3f8db6 --- /dev/null +++ b/docs/examples/nullaway/gradle.properties @@ -0,0 +1 @@ +../../../gradle.properties \ No newline at end of file diff --git a/framework/build.gradle b/framework/build.gradle index f6327d3c32d4..fb1d9817d50f 100644 --- a/framework/build.gradle +++ b/framework/build.gradle @@ -97,18 +97,52 @@ tasks.register('cloneAnnotatedJdk', CloneTask) { outputs.upToDateWhen { false } } -task copyAndMinimizeAnnotatedJdkFiles(type: JavaExec, dependsOn: cloneAnnotatedJdk, group: 'Build') { +tasks.register('copyAnnotatedJdkFiles', Copy) { + dependsOn ':framework:cloneAnnotatedJdk' + + def inputDir = "${annotatedJdkHome}/src" + def outputDir = "${buildDir}/generated/resources/annotated-jdk/" + + inputs.dir file(inputDir) + outputs.dir file(outputDir) + + FileTree tree = fileTree(dir: inputDir) + String absolutejdkHome = file(annotatedJdkHome).absolutePath + int jdkDirStringSize = absolutejdkHome.size() + + NavigableSet annotatedForFiles = new TreeSet<>(); + // Populate `annotatedForFiles`. + tree.visit { FileVisitDetails fvd -> + if (!fvd.file.isDirectory() && fvd.file.name.matches('.*\\.java') + && !fvd.file.path.contains('org/checkerframework')) { + fvd.getFile().readLines().any { line -> + if (line.contains('@AnnotatedFor') || line.contains('org.checkerframework') || line.contains('org.jspecify')) { + annotatedForFiles.add(fvd.file.absolutePath) + return true; + } + } + } + } + + from(annotatedJdkHome) + into(outputDir) + for (String filename : annotatedForFiles) { + include filename.substring(jdkDirStringSize) + } +} + +task copyAndMinimizeAnnotatedJdkFiles(type: JavaExec, dependsOn: copyAnnotatedJdkFiles, group: 'Build') { + def outputDir = "${buildDir}/generated/resources/annotated-jdk/" + + description = "Copy annotated JDK files to ${outputDir}. Removes private and package-private methods, method bodies, comments, etc. from the annotated JDK" + dependsOn ':framework:compileStubifierJava' // we need the next two dependencies because we run JavaStubifier using this project's runtimeClasspath, // which refers to the jars for these other projects dependsOn ':javacutil:jar' dependsOn ':dataflow:jar' - def inputDir = "${annotatedJdkHome}/src" - def outputDir = "${buildDir}/generated/resources/annotated-jdk/" - - description = "Copy annotated JDK files to ${outputDir}. Removes private and package-private methods, method bodies, comments, etc. from the annotated JDK" - inputs.dir file(inputDir) + inputs.dir file(outputDir) outputs.dir file(outputDir) classpath = sourceSets.stubifier.runtimeClasspath @@ -117,32 +151,6 @@ task copyAndMinimizeAnnotatedJdkFiles(type: JavaExec, dependsOn: cloneAnnotatedJ mainClass = 'org.checkerframework.framework.stubifier.JavaStubifier' args outputDir - - doFirst { - FileTree tree = fileTree(dir: inputDir) - NavigableSet annotatedForFiles = new TreeSet<>(); - // Populate `annotatedForFiles`. - tree.visit { FileVisitDetails fvd -> - if (!fvd.file.isDirectory() && fvd.file.name.matches('.*\\.java') - && !fvd.file.path.contains('org/checkerframework')) { - fvd.getFile().readLines().any { line -> - if (line.contains('@AnnotatedFor') || line.contains('org.checkerframework') || line.contains('org.jspecify')) { - annotatedForFiles.add(fvd.file.absolutePath) - return true; - } - } - } - } - String absolutejdkHome = file(annotatedJdkHome).absolutePath - int jdkDirStringSize = absolutejdkHome.size() - copy { - from(annotatedJdkHome) - into(outputDir) - for (String filename : annotatedForFiles) { - include filename.substring(jdkDirStringSize) - } - } - } } sourcesJar.dependsOn(copyAndMinimizeAnnotatedJdkFiles) diff --git a/gradle-mvn-push.gradle b/gradle-mvn-push.gradle index c011c59dc233..6b335b10571c 100644 --- a/gradle-mvn-push.gradle +++ b/gradle-mvn-push.gradle @@ -2,10 +2,13 @@ apply plugin: 'maven-publish' apply plugin: 'signing' final isSnapshot = version.contains('SNAPSHOT') -// https://github.com/johnrengelman/shadow/issues/586#issuecomment-708375599 -components.java.withVariantsFromConfiguration(configurations.shadowRuntimeElements) { - skip() -} +/* TODO: this work-around isn't working anymore. Is it still needed? + // https://github.com/johnrengelman/shadow/issues/586#issuecomment-708375599 + components.java.withVariantsFromConfiguration(configurations.shadowRuntimeElements) { + skip() + } + */ + publishing { repositories { maven { @@ -22,7 +25,6 @@ publishing { } signing { - // Use external gpg cmd. This makes it easy to use gpg-agent, // to avoid being prompted for a password once per artifact. useGpgCmd() diff --git a/gradle.properties b/gradle.properties index b854190ae91c..a7dc7b9fb887 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,3 +4,4 @@ org.gradle.parallel=true org.gradle.jvmargs=-Xmx2g -Dfile.encoding=UTF-8 org.gradle.caching=true +org.gradle.configuration-cache=true diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 1b33c55baabb..8bdaf60c75ab 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index aaaabb3cb9fe..37f78a6af837 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.4-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.3.1-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index 23d15a936707..ef07e0162b18 100755 --- a/gradlew +++ b/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License.