diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 01195f9..be2df0e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,6 +17,6 @@ jobs: - name: Set up JDK uses: actions/setup-java@v1 with: - java-version: 13 + java-version: 14 - name: Build with Gradle run: ./gradlew build diff --git a/.gitignore b/.gitignore index c8427e1..fd0c699 100644 --- a/.gitignore +++ b/.gitignore @@ -3,9 +3,13 @@ .gradle build/ +target/ .idea/ out/ +*.iml +*.ipr +*.iws .project diff --git a/README.md b/README.md index 8b39e95..99ab2c5 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Jabel - use Javac 12+ syntax when targeting Java 8 +# Jabel - use modern Java 9-14 syntax when targeting Java 8 > Because life is too short to wait for your users to upgrade their Java! @@ -13,7 +13,7 @@ But, since most of features after Java 8 did not require a change in the bytecod ## How Jabel works -Although Jabel is an annotation processor, it does not run any processing, +Although Jabel is a javac compiler plugin, it does not run any processing, but instruments the java compiler classes and makes it treat some new Java 9+ languages features as they were supported in Java 8. @@ -32,7 +32,7 @@ require the same target as the JVM because they get released altogether. As was previously described, Jabel makes the compiler think that certain features were developed for Java 8, and removes the checks that otherwise will report them as invalid for the target. -It is important to understand that it will use the same code as for Java 12 and won't change +It is important to understand that it will use the same desugaring code as for Java 9+ but won't change the result's classfile version, because the compilation phase will be done with Java 8 target. ## How to use @@ -66,7 +66,7 @@ Jabel has to be added as an annotation processor to your maven-compiler-plugin: org.apache.maven.plugins maven-compiler-plugin - 13 + 14 --enable-preview @@ -77,29 +77,33 @@ Jabel has to be added as an annotation processor to your maven-compiler-plugin: - - - - org.apache.maven.plugins - maven-compiler-plugin - 3.8.1 - - - 8 - - - com.github.bsideup.jabel - jabel-javac-plugin - 0.2.0 - - - - com.github.bsideup.jabel.JabelJavacProcessor - - - - - + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + + 8 + 14 + 14 + + -Xplugin:jabel + + + + + + + + + com.github.bsideup.jabel + jabel-javac-plugin + 0.3.0 + provided + + ``` Compile your project and verify that Jabel is installed and successfully reports: @@ -129,19 +133,26 @@ repositories { Then, add Jabel as any other annotation processor: ```groovy dependencies { - annotationProcessor 'com.github.bsideup.jabel:jabel-javac-plugin:0.2.0' + annotationProcessor 'com.github.bsideup.jabel:jabel-javac-plugin:0.3.0' } ``` Now, even if you set source/target/release to 8, the compiler will let you use some new language features. The full list of features will be printed during the compilation. ```groovy -sourceCompatibility = 12 // for the IDE support +sourceCompatibility = 14 // for the IDE support -compileJava { +tasks.withType(JavaCompile).all { options.compilerArgs = [ - "--release", "8" // Avoid using Java 12 APIs + "--release", "8", // Avoid using Java 9+ APIs + '--enable-preview', ] + + doFirst { + options.compilerArgs = options.compilerArgs.findAll { + it != '--enable-preview' + } + } } ``` diff --git a/example/build.gradle b/example/build.gradle index 852b738..b42698e 100644 --- a/example/build.gradle +++ b/example/build.gradle @@ -2,19 +2,20 @@ plugins { id "java" } -sourceCompatibility = 13 +sourceCompatibility = 14 targetCompatibility = 8 -compileJava { +tasks.withType(JavaCompile).all { options.compilerArgs = [ "--release", "8", + '--enable-preview', ] -} -compileTestJava { - options.compilerArgs = [ - "--release", "8", - ] + doFirst { + options.compilerArgs = options.compilerArgs.findAll { + it != '--enable-preview' + } + } } test { diff --git a/example/src/main/java/com/example/JabelExample.java b/example/src/main/java/com/example/JabelExample.java index 1b7862a..3c648a0 100644 --- a/example/src/main/java/com/example/JabelExample.java +++ b/example/src/main/java/com/example/JabelExample.java @@ -15,7 +15,10 @@ public String run(String[] args) { // https://openjdk.java.net/jeps/325 var result = switch (args.length) { case 1 -> { - yield "one"; + yield """ + one... + yet pretty long! + """; } case 2, 3 -> "two or three"; default -> new JabelExample().new Inner().innerPublic(); @@ -33,8 +36,14 @@ private String outerPrivate(List... args) { @Override public String call() { // Var in lambda parameter - Function function = (var prefix) -> { - return prefix + Integer.toString(0); + Function function = (var prefix) -> { + // Pattern Matching in instanceof + // https://openjdk.java.net/jeps/305 + if (prefix instanceof String s) { + return s + Integer.toString(0); + } else { + throw new IllegalArgumentException("Expected string!"); + } }; // Test indy strings return function.apply("idk "); diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 5c2d1cf..490fda8 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 4a6ebce..bb8b2fc 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.2.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.5.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index b0d6d0a..2fe81a7 100755 --- a/gradlew +++ b/gradlew @@ -7,7 +7,7 @@ # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, @@ -125,8 +125,8 @@ if $darwin; then GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" fi -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` JAVACMD=`cygpath --unix "$JAVACMD"` @@ -154,19 +154,19 @@ if $cygwin ; then else eval `echo args$i`="\"$arg\"" fi - i=$((i+1)) + i=`expr $i + 1` done case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; esac fi @@ -175,14 +175,9 @@ save () { for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done echo " " } -APP_ARGS=$(save "$@") +APP_ARGS=`save "$@"` # Collect all arguments for the java command, following the shell quoting and substitution rules eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" -# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong -if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then - cd "$(dirname "$0")" -fi - exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index 9991c50..62bd9b9 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -5,7 +5,7 @@ @rem you may not use this file except in compliance with the License. @rem You may obtain a copy of the License at @rem -@rem http://www.apache.org/licenses/LICENSE-2.0 +@rem https://www.apache.org/licenses/LICENSE-2.0 @rem @rem Unless required by applicable law or agreed to in writing, software @rem distributed under the License is distributed on an "AS IS" BASIS, @@ -29,6 +29,9 @@ if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" diff --git a/jabel-javac-plugin/build.gradle b/jabel-javac-plugin/build.gradle index 6328421..47fa235 100644 --- a/jabel-javac-plugin/build.gradle +++ b/jabel-javac-plugin/build.gradle @@ -6,6 +6,6 @@ plugins { sourceCompatibility = targetCompatibility = 8 dependencies { - compile 'net.bytebuddy:byte-buddy:1.10.8' - compile 'net.bytebuddy:byte-buddy-agent:1.10.8' + compile 'net.bytebuddy:byte-buddy:1.10.14' + compile 'net.bytebuddy:byte-buddy-agent:1.10.14' } \ No newline at end of file diff --git a/jabel-javac-plugin/src/main/java/com/github/bsideup/jabel/JabelJavacProcessor.java b/jabel-javac-plugin/src/main/java/com/github/bsideup/jabel/JabelCompilerPlugin.java similarity index 70% rename from jabel-javac-plugin/src/main/java/com/github/bsideup/jabel/JabelJavacProcessor.java rename to jabel-javac-plugin/src/main/java/com/github/bsideup/jabel/JabelCompilerPlugin.java index 904801c..90e73f3 100644 --- a/jabel-javac-plugin/src/main/java/com/github/bsideup/jabel/JabelJavacProcessor.java +++ b/jabel-javac-plugin/src/main/java/com/github/bsideup/jabel/JabelCompilerPlugin.java @@ -1,5 +1,7 @@ package com.github.bsideup.jabel; +import com.sun.source.util.JavacTask; +import com.sun.source.util.Plugin; import com.sun.tools.javac.code.Source; import com.sun.tools.javac.parser.JavaTokenizer; import com.sun.tools.javac.parser.JavacParser; @@ -8,15 +10,6 @@ import net.bytebuddy.asm.Advice; import net.bytebuddy.dynamic.loading.ClassReloadingStrategy; -import javax.annotation.processing.Completion; -import javax.annotation.processing.ProcessingEnvironment; -import javax.annotation.processing.Processor; -import javax.annotation.processing.RoundEnvironment; -import javax.lang.model.SourceVersion; -import javax.lang.model.element.AnnotationMirror; -import javax.lang.model.element.Element; -import javax.lang.model.element.ExecutableElement; -import javax.lang.model.element.TypeElement; import java.lang.reflect.Field; import java.util.Arrays; import java.util.Objects; @@ -24,13 +17,12 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import static java.util.Collections.emptySet; import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.takesArguments; -public class JabelJavacProcessor implements Processor { +public class JabelCompilerPlugin implements Plugin { - private static final Set ENABLED_FEATURES = Stream + static final Set ENABLED_FEATURES = Stream .of( "PRIVATE_SAFE_VARARGS", @@ -60,7 +52,8 @@ public class JabelJavacProcessor implements Processor { .filter(Objects::nonNull) .collect(Collectors.toSet()); - static { + @Override + public void init(JavacTask task, String... args) { ByteBuddyAgent.install(); ByteBuddy byteBuddy = new ByteBuddy(); @@ -89,15 +82,7 @@ public class JabelJavacProcessor implements Processor { } catch (Exception e) { throw new RuntimeException(e); } - } - @Override - public SourceVersion getSupportedSourceVersion() { - return SourceVersion.RELEASE_8; - } - - @Override - public void init(ProcessingEnvironment processingEnv) { System.out.println( ENABLED_FEATURES.stream() .map(Enum::name) @@ -110,22 +95,12 @@ public void init(ProcessingEnvironment processingEnv) { } @Override - public Set getSupportedOptions() { - return emptySet(); + public String getName() { + return "jabel"; } - @Override - public Set getSupportedAnnotationTypes() { - return emptySet(); - } - - @Override - public boolean process(Set annotations, RoundEnvironment roundEnv) { - return false; - } - - @Override - public Iterable getCompletions(Element element, AnnotationMirror annotation, ExecutableElement member, String userText) { - return emptySet(); + // Make it auto start on Java 14+ + public boolean autoStart() { + return true; } } diff --git a/jabel-javac-plugin/src/main/resources/META-INF/services/com.sun.source.util.Plugin b/jabel-javac-plugin/src/main/resources/META-INF/services/com.sun.source.util.Plugin new file mode 100644 index 0000000..f9bfc26 --- /dev/null +++ b/jabel-javac-plugin/src/main/resources/META-INF/services/com.sun.source.util.Plugin @@ -0,0 +1 @@ +com.github.bsideup.jabel.JabelCompilerPlugin \ No newline at end of file diff --git a/jabel-javac-plugin/src/main/resources/META-INF/services/javax.annotation.processing.Processor b/jabel-javac-plugin/src/main/resources/META-INF/services/javax.annotation.processing.Processor deleted file mode 100644 index 5339e62..0000000 --- a/jabel-javac-plugin/src/main/resources/META-INF/services/javax.annotation.processing.Processor +++ /dev/null @@ -1 +0,0 @@ -com.github.bsideup.jabel.JabelJavacProcessor \ No newline at end of file