davidB / scala-maven-plugin

The scala-maven-plugin (previously maven-scala-plugin) is used for compiling/testing/running/documenting scala code in maven.
https://davidb.github.io/scala-maven-plugin/
The Unlicense
554 stars 150 forks source link

Preview compiler features in a mixed Java/Scala Project #727

Closed aghasemi closed 9 months ago

aghasemi commented 9 months ago

Hi,

I have a project with both Java and Scala sources, and I want to use a preview feature of Java 21 (String templates) in the Java code. However, building the project with mvn compile results in:

 error: StringTemplate is a preview API and is disabled by default.
import static java.lang.StringTemplate.STR;
                       ^
  (use --enable-preview to enable preview APIs)

I have set --enable-preview in the maven compiler plugin, but the maven scala compiler does not have such a feature, does it? Is there anything I can do to have my Java code compiled, or preview features are unusable intentionally? By the way, Java 21 works fine with the plugin otherwise, and other new features of Java 21 such as pattern matching and virtual threads work as intended.

Many thanks

slandelle commented 9 months ago

Hi,

See https://github.com/davidB/scala-maven-plugin/blob/master/src/main/java/scala_maven/ScalaMojoSupport.java#L131

aghasemi commented 9 months ago

Many many thanks. That solved the issue I mentioned, but there is still one issue:

When I actually use Java 21's String templates, e.g. by running System.out.println(STR."=\{2+2}");, I get the following error at the beginning of the compile step

invalid escape character

, which makes sense from the point of view of previous Java versions, but not Java 21. Moreover, java.lang.StringTemplate.STR is correctly imported and I can freely use it as long I don't have \{ in my String which of course defeats the purpose of templates. Is there any solution for this one?

slandelle commented 9 months ago

Please provide a reproducer as described here: https://stackoverflow.com/help/minimal-reproducible-example.

aghasemi commented 9 months ago

pom.xml:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>io.aghasemi</groupId>
  <artifactId>mvntest</artifactId>
  <version>1.0-SNAPSHOT</version>

  <properties>
    <maven.compiler.source>21</maven.compiler.source>
    <maven.compiler.target>21</maven.compiler.target>
    <encoding>UTF-8</encoding>
    <scala.version>2.13.12</scala.version>
    <scala.compat.version>2.13</scala.compat.version>
  </properties>

  <dependencies>

    <!-- https://mvnrepository.com/artifact/org.scalatest/scalatest -->
    <dependency>
      <groupId>org.scalatest</groupId>
      <artifactId>scalatest_${scala.compat.version}</artifactId>
      <version>3.2.17</version>
      <scope>test</scope>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.scalatestplus/junit-4-13 -->
    <dependency>
      <groupId>org.scalatestplus</groupId>
      <artifactId>junit-4-13_${scala.compat.version}</artifactId>
      <version>3.2.17.0</version>
      <scope>test</scope>
    </dependency>

    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.13.1</version>
      <scope>test</scope>
    </dependency>

  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>net.alchim31.maven</groupId>
        <artifactId>scala-maven-plugin</artifactId>
        <version>4.8.1</version>
        <executions>
          <execution>
            <id>scala-compile-first</id>
            <phase>process-resources</phase>
            <goals>
              <goal>add-source</goal>
              <goal>compile</goal>
            </goals>
          </execution>
          <execution>
            <id>scala-test-compile</id>
            <phase>process-test-resources</phase>
            <goals>
              <goal>testCompile</goal>
            </goals>
          </execution>
        </executions>
        <configuration>
          <javacArgs>--enable-preview</javacArgs>
          <jvmArgs>
            <jvmArg>--enable-preview</jvmArg>
          </jvmArgs>
          <source>${maven.compiler.source}</source>
          <target>${maven.compiler.target}</target>

        </configuration>
      </plugin>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.11.0</version>
        <executions>
          <execution>
            <phase>compile</phase>
            <goals>
              <goal>compile</goal>
            </goals>
          </execution>
        </executions>
        <configuration>
          <source>${maven.compiler.source}</source>
          <target>${maven.compiler.target}</target>
          <enablePreview>true</enablePreview>
        </configuration>
      </plugin>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-surefire-plugin</artifactId>
        <version>2.21.0</version>
        <configuration>
          <!-- Tests will be run with scalatest-maven-plugin instead -->
          <skipTests>true</skipTests>
          <argLine>--enable-preview</argLine>
        </configuration>
      </plugin>
      <plugin>
        <groupId>org.scalatest</groupId>
        <artifactId>scalatest-maven-plugin</artifactId>
        <version>2.0.0</version>
        <configuration>
          <reportsDirectory>${project.build.directory}/surefire-reports</reportsDirectory>
          <junitxml>.</junitxml>
          <filereports>TestSuiteReport.txt</filereports>
          <!-- Comma separated list of JUnit test class names to execute -->
          <jUnitClasses>io.aghasemi.Tests</jUnitClasses>
          <argLine>
            --enable-preview
          </argLine>
        </configuration>
        <executions>
          <execution>
            <id>test</id>
            <goals>
              <goal>test</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-assembly-plugin</artifactId>
        <version>3.6.0</version>

        <configuration>
          <descriptorRefs>
            <descriptorRef>jar-with-dependencies</descriptorRef>
          </descriptorRefs>
        </configuration>

        <executions>
          <execution>
            <id>make-assembly</id>
            <phase>package</phase>
            <goals>
              <goal>single</goal>
            </goals>
          </execution>
        </executions>

      </plugin>
    </plugins>
  </build>
</project>

and then io.aghasmi.Tests.java:

package io.aghasemi;

import static java.lang.StringTemplate.STR;

public class Tests {

    public static void main(String[] args) {
         System.out.println(STR."=\{2+2}");

        return;
    }
}

Temurin JDk 21 and Maven 3.9.4.

slandelle commented 9 months ago

Could you please:

aghasemi commented 9 months ago

Could you please:

  • trim down your pom.xml to what's really necessary
  • push a maven project in a repo on GitHub

Here it is: https://github.com/aghasemi/mavan-scala-java21-test

Thanks

slandelle commented 9 months ago

I have the exact same error with sbt. It seems the Scala compiler doesn't support String templates yet (and I expect supporting an experimental Java feature to be very low priority).

Nothing we can do on our side. scala-maven-plugin will support automatically it once scalac supports it.

aghasemi commented 9 months ago

I see. Thanks. Quite a bit of a shame since the Scala compiler is actually OK with the preview features, except for this extension to escape characters it seems. Would not happen if \ was not chosen to use in templated strings :)

slandelle commented 9 months ago

2 different things: being able to pass the javac option to enable features in preview and specifically being able to parse this new Java code. Might be related to the version of asm currently shipped.

Anyway, nothing we can do here. I recommend that you open an issue directly against Scala.

aghasemi commented 9 months ago

Thanks. I will. Just out of curiosity, why should the Scala compiler parse a Java source file?

slandelle commented 9 months ago

So it can figure out the types and methods in Java code and compile the Scala code that might import them.

But it just parses Java sources, it doesn't compile them in bytecode, javac is used for that. That's why you only need a JRE to compile Scala code, but you need a full JDK to compile mixed projects with Java code.

aghasemi commented 9 months ago

Of course! Many thanks.