diff --git a/.circleci/config.yml b/.circleci/config.yml index 23a81428a..c6888a954 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -5,12 +5,12 @@ version: 2.1 jobs: compile: - docker: [{ image: 'cimg/openjdk:15.0.2-node' }] + docker: [{ image: 'cimg/openjdk:17.0.1-node' }] resource_class: large environment: CIRCLE_TEST_REPORTS: /home/circleci/junit CIRCLE_ARTIFACTS: /home/circleci/artifacts - GRADLE_OPTS: -Dorg.gradle.jvmargs='-Xmx2g' -Dorg.gradle.workers.max=2 + GRADLE_OPTS: -Dorg.gradle.workers.max=2 -Dorg.gradle.jvmargs='-Xmx2g --add-exports jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED' _JAVA_OPTIONS: -XX:ActiveProcessorCount=4 -XX:MaxRAM=8g -XX:ErrorFile=/home/circleci/artifacts/hs_err_pid%p.log -XX:HeapDumpPath=/home/circleci/artifacts steps: - checkout @@ -54,12 +54,12 @@ jobs: paths: [ project, .gradle/init.gradle ] check: - docker: [{ image: 'cimg/openjdk:15.0.2-node' }] + docker: [{ image: 'cimg/openjdk:17.0.1-node' }] resource_class: medium environment: CIRCLE_TEST_REPORTS: /home/circleci/junit CIRCLE_ARTIFACTS: /home/circleci/artifacts - GRADLE_OPTS: -Dorg.gradle.jvmargs='-Xmx2g' -Dorg.gradle.workers.max=1 + GRADLE_OPTS: -Dorg.gradle.workers.max=1 -Dorg.gradle.jvmargs='-Xmx2g --add-exports jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED' _JAVA_OPTIONS: -XX:ActiveProcessorCount=2 -XX:MaxRAM=4g -XX:ErrorFile=/home/circleci/artifacts/hs_err_pid%p.log -XX:HeapDumpPath=/home/circleci/artifacts steps: - attach_workspace: { at: /home/circleci } @@ -76,12 +76,12 @@ jobs: - store_artifacts: { path: ~/artifacts } unit-test: - docker: [{ image: 'cimg/openjdk:15.0.2-node' }] + docker: [{ image: 'cimg/openjdk:17.0.1-node' }] resource_class: large environment: CIRCLE_TEST_REPORTS: /home/circleci/junit CIRCLE_ARTIFACTS: /home/circleci/artifacts - GRADLE_OPTS: -Dorg.gradle.jvmargs='-Xmx2g' -Dorg.gradle.workers.max=2 + GRADLE_OPTS: -Dorg.gradle.workers.max=2 -Dorg.gradle.jvmargs='-Xmx2g --add-exports jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED' _JAVA_OPTIONS: -XX:ActiveProcessorCount=4 -XX:MaxRAM=8g -XX:ErrorFile=/home/circleci/artifacts/hs_err_pid%p.log -XX:HeapDumpPath=/home/circleci/artifacts steps: - attach_workspace: { at: /home/circleci } @@ -103,7 +103,7 @@ jobs: environment: CIRCLE_TEST_REPORTS: /home/circleci/junit CIRCLE_ARTIFACTS: /home/circleci/artifacts - GRADLE_OPTS: -Dorg.gradle.jvmargs='-Xmx2g' -Dorg.gradle.workers.max=2 + GRADLE_OPTS: -Dorg.gradle.workers.max=2 -Dorg.gradle.jvmargs='-Xmx2g --add-exports jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED' _JAVA_OPTIONS: -XX:ActiveProcessorCount=4 -XX:MaxRAM=8g -XX:ErrorFile=/home/circleci/artifacts/hs_err_pid%p.log -XX:HeapDumpPath=/home/circleci/artifacts steps: - checkout @@ -119,13 +119,42 @@ jobs: - store_test_results: { path: ~/junit } - store_artifacts: { path: ~/artifacts } + unit-test-15: + docker: [{ image: 'cimg/openjdk:11.0.10-node' }] + resource_class: large + environment: + CIRCLE_TEST_REPORTS: /home/circleci/junit + CIRCLE_ARTIFACTS: /home/circleci/artifacts + GRADLE_OPTS: -Dorg.gradle.workers.max=2 -Dorg.gradle.jvmargs='-Xmx2g --add-exports jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED' + _JAVA_OPTIONS: -XX:ActiveProcessorCount=4 -XX:MaxRAM=8g -XX:ErrorFile=/home/circleci/artifacts/hs_err_pid%p.log -XX:HeapDumpPath=/home/circleci/artifacts + JAVA_HOME: /opt/java15 + steps: + - checkout + - run: + name: Install Java + command: | + sudo mkdir -p /opt/java && cd /opt/java && sudo chown -R circleci:circleci . + curl https://cdn.azul.com/zulu/bin/zulu15.36.13-ca-jdk15.0.5-linux_x64.tar.gz | tar -xzf - -C /opt/java + sudo ln -s /opt/java/zulu*/ /opt/java15 + - restore_cache: { key: 'gradle-wrapper-v2-{{ checksum "gradle/wrapper/gradle-wrapper.properties" }}' } + - restore_cache: { key: 'unit-test-15-gradle-cache-v2-{{ checksum "versions.props" }}-{{ checksum "build.gradle" }}' } + - run: ./gradlew --parallel --stacktrace --continue test -Pcom.palantir.baseline-error-prone.disable -Porg.gradle.java.installations.fromEnv=JAVA_8_HOME,JAVA_11_HOME,JAVA_15_HOME,JAVA_17_HOME,JAVA_HOME + - save_cache: + key: 'unit-test-15-gradle-cache-v2-{{ checksum "versions.props" }}-{{ checksum "build.gradle" }}' + paths: [ ~/.gradle/caches ] + - run: + command: mkdir -p ~/junit && find . -type f -regex ".*/build/.*TEST.*xml" -exec cp --parents {} ~/junit/ \; + when: always + - store_test_results: { path: ~/junit } + - store_artifacts: { path: ~/artifacts } + trial-publish: - docker: [{ image: 'cimg/openjdk:15.0.2-node' }] + docker: [{ image: 'cimg/openjdk:17.0.1-node' }] resource_class: medium environment: CIRCLE_TEST_REPORTS: /home/circleci/junit CIRCLE_ARTIFACTS: /home/circleci/artifacts - GRADLE_OPTS: -Dorg.gradle.jvmargs='-Xmx2g' -Dorg.gradle.workers.max=1 + GRADLE_OPTS: -Dorg.gradle.workers.max=1 -Dorg.gradle.jvmargs='-Xmx2g --add-exports jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED' _JAVA_OPTIONS: -XX:ActiveProcessorCount=2 -XX:MaxRAM=4g -XX:ErrorFile=/home/circleci/artifacts/hs_err_pid%p.log -XX:HeapDumpPath=/home/circleci/artifacts steps: - attach_workspace: { at: /home/circleci } @@ -142,12 +171,12 @@ jobs: - store_artifacts: { path: ~/artifacts } publish: - docker: [{ image: 'cimg/openjdk:15.0.2-node' }] + docker: [{ image: 'cimg/openjdk:17.0.1-node' }] resource_class: medium environment: CIRCLE_TEST_REPORTS: /home/circleci/junit CIRCLE_ARTIFACTS: /home/circleci/artifacts - GRADLE_OPTS: -Dorg.gradle.jvmargs='-Xmx2g' -Dorg.gradle.workers.max=1 + GRADLE_OPTS: -Dorg.gradle.workers.max=1 -Dorg.gradle.jvmargs='-Xmx2g --add-exports jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED' _JAVA_OPTIONS: -XX:ActiveProcessorCount=2 -XX:MaxRAM=4g -XX:ErrorFile=/home/circleci/artifacts/hs_err_pid%p.log -XX:HeapDumpPath=/home/circleci/artifacts steps: - attach_workspace: { at: /home/circleci } @@ -179,6 +208,9 @@ workflows: - unit-test-11: filters: { tags: { only: /.*/ } } + - unit-test-15: + filters: { tags: { only: /.*/ } } + - check: requires: [ compile ] filters: { tags: { only: /.*/ } } @@ -188,5 +220,5 @@ workflows: filters: { branches: { ignore: develop } } - publish: - requires: [ unit-test, unit-test-11, check, trial-publish ] + requires: [ unit-test, unit-test-11, unit-test-15, check, trial-publish ] filters: { tags: { only: /.*/ }, branches: { only: develop } } diff --git a/.circleci/template.sh b/.circleci/template.sh index 133f0613e..b3f867435 100644 --- a/.circleci/template.sh +++ b/.circleci/template.sh @@ -1,4 +1,5 @@ #!/usr/bin/env bash export CIRCLECI_TEMPLATE=java-library-oss -export JDK=15 +export JDK=17 export UNIT_TEST_11=true +export UNIT_TEST_15=true diff --git a/gradle-palantir-java-format/src/test/groovy/com/palantir/javaformat/gradle/PalantirJavaFormatPluginTest.groovy b/gradle-palantir-java-format/src/test/groovy/com/palantir/javaformat/gradle/PalantirJavaFormatPluginTest.groovy index 8205fa92c..c6d87b187 100644 --- a/gradle-palantir-java-format/src/test/groovy/com/palantir/javaformat/gradle/PalantirJavaFormatPluginTest.groovy +++ b/gradle-palantir-java-format/src/test/groovy/com/palantir/javaformat/gradle/PalantirJavaFormatPluginTest.groovy @@ -35,6 +35,15 @@ class PalantirJavaFormatPluginTest extends IntegrationTestKitSpec { } apply plugin: 'idea' """.stripIndent() + + // Add jvm args to allow spotless and formatter gradle plugins to run with Java 16+ + file('gradle.properties') << """ + org.gradle.jvmargs=--add-exports jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED \ + --add-exports jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED \ + --add-exports jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED \ + --add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED \ + --add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED + """.stripIndent() } def 'formatDiff updates only lines changed in git diff'() { diff --git a/gradle-palantir-java-format/src/test/groovy/com/palantir/javaformat/gradle/PalantirJavaFormatSpotlessPluginTest.groovy b/gradle-palantir-java-format/src/test/groovy/com/palantir/javaformat/gradle/PalantirJavaFormatSpotlessPluginTest.groovy index 13a228dfa..bd1c42f06 100644 --- a/gradle-palantir-java-format/src/test/groovy/com/palantir/javaformat/gradle/PalantirJavaFormatSpotlessPluginTest.groovy +++ b/gradle-palantir-java-format/src/test/groovy/com/palantir/javaformat/gradle/PalantirJavaFormatSpotlessPluginTest.groovy @@ -33,6 +33,15 @@ class PalantirJavaFormatSpotlessPluginTest extends IntegrationTestKitSpec { palantirJavaFormat files(file("${CLASSPATH_FILE}").text.split(':')) } """.stripIndent() + + // Add jvm args to allow spotless and formatter gradle plugins to run with Java 16+ + file('gradle.properties') << """ + org.gradle.jvmargs=--add-exports jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED \ + --add-exports jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED \ + --add-exports jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED \ + --add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED \ + --add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED + """.stripIndent() } def "formats with spotless when spotless is applied"() { diff --git a/gradle.properties b/gradle.properties index 4f996f1a0..122fced72 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,2 +1,8 @@ org.gradle.parallel=true org.gradle.caching=true +# Add jvm args to allow spotless and formatter gradle plugins to run with Java 16+ +org.gradle.jvmargs=--add-exports jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED \ + --add-exports jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED \ + --add-exports jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED \ + --add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED \ + --add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED diff --git a/palantir-java-format/build.gradle b/palantir-java-format/build.gradle index 447bda3c4..5e0a2cbe3 100644 --- a/palantir-java-format/build.gradle +++ b/palantir-java-format/build.gradle @@ -43,22 +43,28 @@ def exports = [ 'jdk.compiler/com.sun.tools.javac.api' ] +def jvmArgList = exports.stream().map(new Function() { + @Override + String apply(String value) { + return "--add-exports=${value}=ALL-UNNAMED" + } +}).collect(Collectors.toList()) + tasks.withType(JavaCompile).configureEach { options.errorprone.disable 'StrictUnusedVariable' // Allow access to internal javac apis - options.compilerArgs += exports.stream().map(new Function() { - @Override - String apply(String value) { - return "--add-exports=${value}=ALL-UNNAMED" - } - }).collect(Collectors.toList()) + options.compilerArgs += jvmArgList if (JavaVersion.current() < JavaVersion.VERSION_14) { excludes = ['**/Java14InputAstVisitor.java'] } } +tasks.withType(Test).configureEach { + jvmArgs = jvmArgList +} + tasks.withType(Javadoc).configureEach { // Allow access to internal javac apis options.optionFiles << file('../gradle/javadoc.options') diff --git a/palantir-java-format/src/main/java/com/palantir/javaformat/java/JavaInputAstVisitor.java b/palantir-java-format/src/main/java/com/palantir/javaformat/java/JavaInputAstVisitor.java index bb9519eb9..c1fc139cc 100644 --- a/palantir-java-format/src/main/java/com/palantir/javaformat/java/JavaInputAstVisitor.java +++ b/palantir-java-format/src/main/java/com/palantir/javaformat/java/JavaInputAstVisitor.java @@ -384,11 +384,14 @@ public Void visitCompilationUnit(CompilationUnitTree node, Void unused) { first = false; dropEmptyDeclarations(); } + handleModule(first, node); // set a partial format marker at EOF to make sure we can format the entire file markForPartialFormat(); return null; } + protected void handleModule(boolean first, CompilationUnitTree node) {} + /** Skips over extra semi-colons at the top-level, or in a class member declaration lists. */ protected void dropEmptyDeclarations() { if (builder.peekToken().equals(Optional.of(";"))) { diff --git a/palantir-java-format/src/main/java/com/palantir/javaformat/java/java14/Java14InputAstVisitor.java b/palantir-java-format/src/main/java/com/palantir/javaformat/java/java14/Java14InputAstVisitor.java index 91bf3500a..6b6e5cbe9 100644 --- a/palantir-java-format/src/main/java/com/palantir/javaformat/java/java14/Java14InputAstVisitor.java +++ b/palantir-java-format/src/main/java/com/palantir/javaformat/java/java14/Java14InputAstVisitor.java @@ -24,14 +24,17 @@ import com.google.common.collect.ImmutableList; import com.palantir.javaformat.Op; import com.palantir.javaformat.OpsBuilder; +import com.palantir.javaformat.OpsBuilder.BlankLineWanted; import com.palantir.javaformat.java.JavaInputAstVisitor; import com.sun.source.tree.BindingPatternTree; import com.sun.source.tree.BlockTree; import com.sun.source.tree.CaseTree; import com.sun.source.tree.ClassTree; +import com.sun.source.tree.CompilationUnitTree; import com.sun.source.tree.ExpressionTree; import com.sun.source.tree.InstanceOfTree; import com.sun.source.tree.LambdaExpressionTree; +import com.sun.source.tree.ModuleTree; import com.sun.source.tree.SwitchExpressionTree; import com.sun.source.tree.Tree; import com.sun.source.tree.Tree.Kind; @@ -53,6 +56,24 @@ public Java14InputAstVisitor(OpsBuilder builder, int indentMultiplier) { super(builder, indentMultiplier); } + @Override + protected void handleModule(boolean first, CompilationUnitTree node) { + try { + ModuleTree module = (ModuleTree) + CompilationUnitTree.class.getMethod("getModule").invoke(node); + if (module != null) { + if (!first) { + builder.blankLineWanted(BlankLineWanted.YES); + } + markForPartialFormat(); + visitModule(module, null); + builder.forcedBreak(); + } + } catch (ReflectiveOperationException e) { + // Java < 17, see https://bugs.openjdk.java.net/browse/JDK-8255464 + } + } + @Override public Void visitBindingPattern(BindingPatternTree node, Void unused) { sync(node); diff --git a/palantir-java-format/src/test/java/com/palantir/javaformat/java/MainTest.java b/palantir-java-format/src/test/java/com/palantir/javaformat/java/MainTest.java index dae2bad3b..f97717ca3 100644 --- a/palantir-java-format/src/test/java/com/palantir/javaformat/java/MainTest.java +++ b/palantir-java-format/src/test/java/com/palantir/javaformat/java/MainTest.java @@ -43,6 +43,13 @@ @Execution(ExecutionMode.CONCURRENT) public class MainTest { + private static final ImmutableList ADD_EXPORTS = ImmutableList.of( + "--add-exports", "jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED", + "--add-exports", "jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED", + "--add-exports", "jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED", + "--add-exports", "jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED", + "--add-exports", "jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED"); + @TempDir public Path testFolder; @@ -103,16 +110,7 @@ public void preserveOriginalFile() throws Exception { @Test public void testMain() throws Exception { - Process process = new ProcessBuilder(ImmutableList.of( - Paths.get(System.getProperty("java.home")) - .resolve("bin/java") - .toString(), - "-cp", - System.getProperty("java.class.path"), - Main.class.getName())) - .redirectError(Redirect.PIPE) - .redirectOutput(Redirect.PIPE) - .start(); + Process process = formatterMain().start(); process.waitFor(); String err = new String(ByteStreams.toByteArray(process.getErrorStream()), UTF_8); assertThat(err).contains("Usage: palantir-java-format"); @@ -418,20 +416,7 @@ public void keepGoingWhenFilesDontExist() throws Exception { public void exitIfChangedStdin() throws Exception { Path path = Files.createFile(testFolder.resolve("Test.java")); Files.write(path, "class Test {\n}\n".getBytes(UTF_8)); - Process process = new ProcessBuilder(ImmutableList.of( - Paths.get(System.getProperty("java.home")) - .resolve("bin/java") - .toString(), - "-cp", - System.getProperty("java.class.path"), - Main.class.getName(), - "-n", - "--set-exit-if-changed", - "-")) - .redirectInput(path.toFile()) - .redirectError(Redirect.PIPE) - .redirectOutput(Redirect.PIPE) - .start(); + Process process = formatterMain("-").redirectInput(path.toFile()).start(); process.waitFor(); String err = new String(ByteStreams.toByteArray(process.getErrorStream()), UTF_8); String out = new String(ByteStreams.toByteArray(process.getInputStream()), UTF_8); @@ -443,19 +428,7 @@ public void exitIfChangedStdin() throws Exception { public void exitIfChangedFiles() throws Exception { Path path = Files.createFile(testFolder.resolve("Test.java")); Files.write(path, "class Test {\n}\n".getBytes(UTF_8)); - Process process = new ProcessBuilder(ImmutableList.of( - Paths.get(System.getProperty("java.home")) - .resolve("bin/java") - .toString(), - "-cp", - System.getProperty("java.class.path"), - Main.class.getName(), - "-n", - "--set-exit-if-changed", - path.toAbsolutePath().toString())) - .redirectError(Redirect.PIPE) - .redirectOutput(Redirect.PIPE) - .start(); + Process process = formatterMain(path.toAbsolutePath().toString()).start(); process.waitFor(); String err = new String(ByteStreams.toByteArray(process.getErrorStream()), UTF_8); String out = new String(ByteStreams.toByteArray(process.getInputStream()), UTF_8); @@ -544,4 +517,20 @@ public void noReflowLongStrings() throws Exception { assertThat(main.format("--skip-reflowing-long-strings", "-")).isEqualTo(0); assertThat(out.toString()).isEqualTo(joiner.join(expected)); } + + private static ProcessBuilder formatterMain(String... args) { + return new ProcessBuilder(ImmutableList.builder() + .add(Paths.get(System.getProperty("java.home")) + .resolve("bin/java") + .toString()) + .addAll(ADD_EXPORTS) + .add("-cp", System.getProperty("java.class.path")) + .add(Main.class.getName()) + .add("-n") + .add("--set-exit-if-changed") + .add(args) + .build()) + .redirectError(Redirect.PIPE) + .redirectOutput(Redirect.PIPE); + } }