diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index b820c93..6069fcf 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -9,10 +9,13 @@ jobs: strategy: matrix: - java: [ '11', '17', '21' ] + java: [ '8', '11', '17', '21' ] steps: - uses: actions/checkout@v4 + with: + fetch-depth: 1 + show-progress: false - name: Set up JDK uses: actions/setup-java@v4 with: @@ -27,4 +30,5 @@ jobs: - name: ./gradlew requireJavadoc run: ./gradlew requireJavadoc - name: ./gradlew spotlessCheck + if: ${{ matrix.java != '8' }} run: ./gradlew spotlessCheck diff --git a/.gitignore b/.gitignore index 10f5124..25b327a 100644 --- a/.gitignore +++ b/.gitignore @@ -3,13 +3,22 @@ build *.class +# BlueJ files +*.ctxt + # Mobile Tools for Java (J2ME) .mtj.tmp/ # Package Files # -# *.jar # Used by gradle +*.jar # Need to commit gradle-wrapper.jar file *.war *.ear +# .jar file not to ignore +!gradle/wrapper/gradle-wrapper.jar # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* + +# IDE files +.idea +.vscode diff --git a/.setup-CF.sh b/.setup-CF.sh index 74aea4e..a9b3ae4 100755 --- a/.setup-CF.sh +++ b/.setup-CF.sh @@ -9,12 +9,12 @@ export SHELLOPTS export JAVA_HOME=${JAVA_HOME:-$(dirname $(dirname $(readlink -f $(which javac))))} -git -C /tmp clone --depth 1 -q https://github.com/plume-lib/plume-scripts.git +git -C /tmp clone --depth 1 -q https://github.com/eisop-plume-lib/git-scripts.git export CHECKERFRAMEWORK="${CHECKERFRAMEWORK:-$(pwd -P)/../checker-framework}" ## Build Checker Framework -/tmp/plume-scripts/git-clone-related eisop checker-framework ${CHECKERFRAMEWORK} +/tmp/git-scripts/git-clone-related eisop checker-framework ${CHECKERFRAMEWORK} (cd $CHECKERFRAMEWORK && ./gradlew assembleForJavac) diff --git a/README.md b/README.md index 99079e8..0d11f4b 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ See the [documentation](http://plumelib.org/multi-version-control/api/org/plumel ``` cd SOME_DIRECTORY -git clone https://github.com/plume-lib/multi-version-control +git clone https://github.com/eisop-plume-lib/multi-version-control cd multi-version-control ./gradlew assemble ``` @@ -23,4 +23,4 @@ alias mvc='java -ea -cp SOME_DIRECTORY/multi-version-control/build/libs/multi-ve ## Other scripts for managing multiple git clones and branches -See the scripts in [manage-git-branches](https://github.com/plume-lib/manage-git-branches). +See the scripts in [manage-git-branches](https://github.com/eisop-plume-lib/manage-git-branches). diff --git a/build.gradle b/build.gradle index 3b6dba1..324a32b 100644 --- a/build.gradle +++ b/build.gradle @@ -1,3 +1,14 @@ +buildscript { + dependencies { + if (JavaVersion.current() >= JavaVersion.VERSION_11) { + // Code formatting; defines targets "spotlessApply" and "spotlessCheck". + // https://github.com/diffplug/spotless/tags ; see tags starting "gradle/" + // Only works on JDK 11+. + classpath 'com.diffplug.spotless:spotless-plugin-gradle:6.25.0' + } + } +} + plugins { id 'java' id 'application' @@ -5,15 +16,14 @@ plugins { // To create a fat jar build/libs/...-all.jar, run: ./gradlew shadowJar id 'com.github.johnrengelman.shadow' version '8.1.1' - // Code formatting; defines targets "spotlessApply" and "spotlessCheck" - // Requires JDK 11 or higher; the plugin crashes under JDK 8. - id 'com.diffplug.spotless' version '6.25.0' - // Error Prone linter - id('net.ltgt.errorprone') version '3.1.0' + id('net.ltgt.errorprone') version '4.0.1' // Checker Framework pluggable type-checking - id 'org.checkerframework' version '0.6.38' + id 'org.checkerframework' version '0.6.40' + + // To show task list as a tree, run: ./gradlew taskTree + id 'com.dorongold.task-tree' version '4.0.0' } repositories { @@ -21,77 +31,108 @@ repositories { maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' } } +ext.errorproneVersion = '2.28.0' + ext { + isJava11orHigher = JavaVersion.current() >= JavaVersion.VERSION_11 isJava21orHigher = JavaVersion.current() >= JavaVersion.VERSION_21 } dependencies { - implementation 'org.apache.commons:commons-exec:1.3' + implementation 'org.apache.commons:commons-exec:1.4.0' implementation 'org.ini4j:ini4j:0.5.4' implementation 'org.plumelib:options:2.0.3' - implementation 'org.plumelib:plume-util:1.9.0' + implementation 'org.plumelib:plume-util:1.9.3' implementation 'org.tmatesoft.svnkit:svnkit:1.10.11' + + compileOnly "com.google.errorprone:error_prone_annotations:${errorproneVersion}" + errorprone("com.google.errorprone:error_prone_core:${errorproneVersion}") } application { mainClass = 'org.plumelib.multiversioncontrol.MultiVersionControl' } -sourceCompatibility = 11 -targetCompatibility = 11 +java { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 +} -spotless { - format 'misc', { - // define the files to apply `misc` to - target '*.md', '.gitignore' +if (isJava11orHigher) { + apply plugin: 'com.diffplug.spotless' + spotless { + format 'misc', { + // define the files to apply `misc` to + target '*.md', '.gitignore' - // define the steps to apply to those files - trimTrailingWhitespace() - indentWithSpaces(2) - endWithNewline() - } - java { - targetExclude('**/WeakIdentityHashMap.java') - googleJavaFormat() - formatAnnotations() - } - groovyGradle { - target '**/*.gradle' - greclipse() // which formatter Spotless should use to format .gradle files. - indentWithSpaces(2) - trimTrailingWhitespace() - // endWithNewline() // Don't want to end empty files with a newline + // define the steps to apply to those files + trimTrailingWhitespace() + indentWithSpaces(2) + endWithNewline() + } + java { + targetExclude('**/WeakIdentityHashMap.java') + googleJavaFormat() + formatAnnotations() + } + groovyGradle { + target '**/*.gradle' + greclipse() // which formatter Spotless should use to format .gradle files. + indentWithSpaces(2) + trimTrailingWhitespace() + // endWithNewline() // Don't want to end empty files with a newline + } } } /// Error Prone linter -dependencies { - errorprone('com.google.errorprone:error_prone_core:2.26.1') -} tasks.withType(JavaCompile).configureEach { // "-processing" avoids javac warning "No processor claimed any of these annotations". - // "-options" is because starting in JDK 20, javac warns about using -source 8. + // "-Xlint:-options" is because of JDK 21 warning "source value 8 is obsolete..." options.compilerArgs << '-Xlint:all,-processing,-options' << '-Werror' if (isJava21orHigher) { options.compilerArgs << '-Xlint:-this-escape,-options' } + options.errorprone.enabled = isJava11orHigher options.errorprone { + disable('ExtendsObject') // Incorrect when using the Checker Framework disable('ReferenceEquality') // Use Interning Checker instead. } options.forkOptions.jvmArgs += '-Xmx2g' - options.release = 11 } /// Checker Framework pluggable type-checking apply plugin: 'org.checkerframework' +if (true) { + // Use the released version of the EISOP Checker Framework. + ext.checkerFrameworkVersion = '3.42.0-eisop3' +} else { + // To use a snapshot version of the EISOP Checker Framework. + // TODO: Change the above test to false to use a snapshot. + ext.checkerFrameworkVersion = '3.42.0-eisop4-SNAPSHOT' + configurations.all { + resolutionStrategy.cacheChangingModulesFor 0, 'minutes' + } +} + dependencies { - compileOnly 'io.github.eisop:checker-qual:3.34.0-eisop1' - testCompileOnly 'io.github.eisop:checker-qual:3.34.0-eisop1' - checkerFramework 'io.github.eisop:checker:3.34.0-eisop1' + compileOnly "io.github.eisop:checker-qual:${checkerFrameworkVersion}" + testCompileOnly "io.github.eisop:checker-qual:${checkerFrameworkVersion}" + checkerFramework "io.github.eisop:checker:${checkerFrameworkVersion}" +} + +// To use a locally-built Checker Framework, run gradle with "-PcfLocal". +if (project.hasProperty('cfLocal')) { + def cfHome = String.valueOf(System.getenv('CHECKERFRAMEWORK')) + dependencies { + compileOnly files(cfHome + '/checker/dist/checker-qual.jar') + testCompileOnly files(cfHome + '/checker/dist/checker-qual.jar') + checkerFramework files(cfHome + '/checker/dist/checker.jar') + } } checkerFramework { @@ -110,46 +151,26 @@ checkerFramework { 'org.checkerframework.common.initializedfields.InitializedFieldsChecker', ] extraJavacArgs = [ - '-Werror', - // '-Aversion', - // '-verbose', + // No "'-Werror'" because of JDK 21 warning "source value 8 is obsolete..." + // '-Werror', '-AcheckPurityAnnotations', '-ArequirePrefixInWarningSuppressions', '-AwarnRedundantAnnotations', '-AwarnUnneededSuppressions', '-AnoJreVersionCheck', + '-Aversion', ] } -// To use a snapshot version of the Checker Framework. -if (false) { - // TODO: change the above test to false when CF is released - ext.checkerFrameworkVersion = '3.42.0-eisop3' - dependencies { - compileOnly "io.github.eisop:checker-qual:${checkerFrameworkVersion}" - testCompileOnly "io.github.eisop:checker-qual:${checkerFrameworkVersion}" - checkerFramework "io.github.eisop:checker:${checkerFrameworkVersion}" - } - configurations.all { - resolutionStrategy.cacheChangingModulesFor 0, 'minutes' - } -} -// To use a locally-built Checker Framework, run gradle with "-PcfLocal". -if (project.hasProperty('cfLocal')) { - def cfHome = String.valueOf(System.getenv('CHECKERFRAMEWORK')) - dependencies { - compileOnly files(cfHome + '/checker/dist/checker-qual.jar') - testCompileOnly files(cfHome + '/checker/dist/checker-qual.jar') - checkerFramework files(cfHome + '/checker/dist/checker.jar') - } -} /// Javadoc // Turn Javadoc warnings into errors. javadoc { - options.addStringOption('Xwerror', '-Xdoclint:all') + // No "'-Werror'" because of JDK 21 warning "source value 8 is obsolete..." + // options.addStringOption('Xwerror', '-Xdoclint:all') + options.addStringOption('Xdoclint:all', '-quiet') options.addStringOption('private', '-quiet') - options.addStringOption('source', '11') + options.addStringOption('source', '8') doLast { ant.replaceregexp(match:"@import url\\('resources/fonts/dejavu.css'\\);\\s*", replace:'', flags:'g', byline:true) { @@ -164,12 +185,19 @@ task javadocWeb(type: Javadoc) { source = sourceSets.main.allJava destinationDir = file("/cse/web/research/plumelib/${project.name}/api") classpath = project.sourceSets.main.compileClasspath - options.addStringOption('source', '11') + options.addStringOption('source', '8') doLast { ant.replaceregexp(match:"@import url\\('resources/fonts/dejavu.css'\\);\\s*", replace:'', flags:'g', byline:true) { fileset(dir: destinationDir) } + // Set permissions + project.exec { + commandLine('chgrp', '-R', 'plse_www', "/cse/web/research/plumelib/${project.name}/api") + } + project.exec { + commandLine('chmod', '-R', 'g+w', "/cse/web/research/plumelib/${project.name}/api") + } } } @@ -180,6 +208,7 @@ dependencies { requireJavadoc 'org.plumelib:require-javadoc:1.0.9' } task requireJavadoc(type: JavaExec) { + group = 'Documentation' description = 'Ensures that Javadoc documentation exists.' mainClass = 'org.plumelib.javadoc.RequireJavadoc' classpath = configurations.requireJavadoc diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index a80b22c..a441313 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.6-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index 1aa94a4..b740cf1 100755 --- a/gradlew +++ b/gradlew @@ -55,7 +55,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. diff --git a/settings.gradle b/settings.gradle index ac3c0c8..fb2a72f 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,8 +1,8 @@ -buildscript { - if (JavaVersion.current() == JavaVersion.VERSION_1_8) { - throw new Error("Use Java 11 or later.") - } -} +// buildscript { +// if (JavaVersion.current() == JavaVersion.VERSION_1_8) { +// throw new Error("Use Java 11 or later.") +// } +// } // Project name is read-only in build scripts, and defaults to directory name. rootProject.name = 'multi-version-control' diff --git a/src/main/java/org/plumelib/multiversioncontrol/MultiVersionControl.java b/src/main/java/org/plumelib/multiversioncontrol/MultiVersionControl.java index 2c7cf7d..8baf95e 100644 --- a/src/main/java/org/plumelib/multiversioncontrol/MultiVersionControl.java +++ b/src/main/java/org/plumelib/multiversioncontrol/MultiVersionControl.java @@ -9,7 +9,9 @@ import java.io.IOException; import java.io.InputStream; import java.io.UncheckedIOException; +import java.io.UnsupportedEncodingException; import java.nio.file.Files; +import java.time.Duration; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; @@ -1105,7 +1107,8 @@ static void addCheckoutCvs(File cvsDir, File parentDir, Set checkouts) // apparently it wasn't a version control directory return; } - String pathInRepo = FilesPlume.readFile(repositoryFile).trim(); + + String pathInRepo = FilesPlume.readString(repositoryFile.toPath()).trim(); @NonNull File repoFileRoot = new File(pathInRepo); while (repoFileRoot.getParentFile() != null) { repoFileRoot = repoFileRoot.getParentFile(); @@ -1126,7 +1129,7 @@ static void addCheckoutCvs(File cvsDir, File parentDir, Set checkouts) pathInRepoAtCheckout = dirRelative.getName(); } - String repoRoot = FilesPlume.readFile(rootFile).trim(); + String repoRoot = FilesPlume.readString(rootFile.toPath()).trim(); checkouts.add(new Checkout(RepoType.CVS, dirRelative, repoRoot, pathInRepoAtCheckout)); } @@ -1529,7 +1532,17 @@ public void process(Set checkouts) { case GIT: // "--" is to prevent the directory name from being interpreted as a command-line // option, if it starts with a hyphen. - pb.command(gitExecutable, "clone", "--", c.repository, dirbase); + // "--filter=blob:none" makes cloning fast and reduces disk space. It makes a + // subsequent `git blame` command slower, since it has retrieve information from the + // remote repository. It makes pulling from the cloned repository impossible. + pb.command( + gitExecutable, + "clone", + "--recursive", + // "--filter=blob:none", + "--", + c.repository, + dirbase); addArgs(pb, gitArg); break; case HG: @@ -1963,12 +1976,13 @@ int perform_command(ProcessBuilder pb, List replacers, boolean showNor String[] argArray = Arrays.copyOfRange(args, 1, args.length); cmdLine.addArguments(argArray); DefaultExecuteResultHandler resultHandler = new DefaultExecuteResultHandler(); - DefaultExecutor executor = new DefaultExecutor(); @SuppressWarnings("nullness") // defaults to non-null and was never reset @NonNull File defaultDirectory = pb.directory(); - executor.setWorkingDirectory(defaultDirectory); + DefaultExecutor executor = + DefaultExecutor.builder().setWorkingDirectory(defaultDirectory).get(); - ExecuteWatchdog watchdog = new ExecuteWatchdog(timeout * 1000L); + ExecuteWatchdog watchdog = + ExecuteWatchdog.builder().setTimeout(Duration.ofSeconds(timeout)).get(); executor.setWatchdog(watchdog); final ByteArrayOutputStream outStream = new ByteArrayOutputStream(); @@ -2013,9 +2027,9 @@ int perform_command(ProcessBuilder pb, List replacers, boolean showNor // Filter then print the output. String output; try { - String tmpOutput = outStream.toString(UTF_8); + String tmpOutput = outStream.toString(UTF_8.toString()); output = tmpOutput; - } catch (RuntimeException e) { + } catch (RuntimeException | UnsupportedEncodingException e) { throw new Error("Exception getting process standard output"); }