javamavenjava-native-interfacemaven-central

maven dylib resource put in sources jar file, not "compiled" jar file


When I build my package with mvn package and install locally with mvn install, it includes the resources I specify in the compiled jar.

Files that get installed:

Archive:  ~/.m2/repository/cam/narzt/getargv/Getargv/0.1-SNAPSHOT/Getargv-0.1-SNAPSHOT.jar
  Length      Date    Time    Name
---------  ---------- -----   ----
        0  03-03-2023 11:25   META-INF/
       81  03-03-2023 11:25   META-INF/MANIFEST.MF
        0  03-03-2023 11:18   cam/
        0  03-03-2023 11:18   cam/narzt/
        0  03-03-2023 11:18   cam/narzt/getargv/
        0  03-03-2023 11:25   META-INF/maven/
        0  03-03-2023 11:25   META-INF/maven/cam.narzt.getargv/
        0  03-03-2023 11:25   META-INF/maven/cam.narzt.getargv/Getargv/
     1542  03-03-2023 11:18   cam/narzt/getargv/Main.class
     3596  03-03-2023 11:18   cam/narzt/getargv/NativeLoader.class
     4350  03-03-2023 11:18   cam/narzt/getargv/Getargv.class
    34152  03-03-2023 11:25   libcam_narzt_getargv_Getargv.dylib
    10309  03-03-2023 11:18   META-INF/maven/cam.narzt.getargv/Getargv/pom.xml
       66  03-03-2023 11:19   META-INF/maven/cam.narzt.getargv/Getargv/pom.properties
---------                     -------
    54096                     14 files

But when I release my package to maven central mvn release:clean release:prepare && mvn release:perform, it puts the resources in the sources jar instead, how can i indicate that they should go in the compiled jar?

pom.xml:

<?xml version="1.0" encoding="UTF-8"?>

<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/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>cam.narzt.getargv</groupId>
  <artifactId>Getargv</artifactId>
  <version>0.2-SNAPSHOT</version>

  <name>${project.artifactId}</name>
  <url>https://getargv.narzt.cam</url>
  <description>JNI bindings for libgetargv</description>
  <inceptionYear>2023</inceptionYear>

  <organization>
    <name>Getargv</name>
    <url>https://github.com/getargv</url>
  </organization>

  <developers>
    <developer>
      <id>CamJN</id>
      <name>Camden Narzt</name>
      <email>getargv@narzt.cam</email>
      <url>https://getargv.narzt.cam</url>
      <organization>Getargv</organization>
      <organizationUrl>https://https://github.com/getargv</organizationUrl>
      <roles>
        <role>developer</role>
      </roles>
      <timezone>America/Edmonton</timezone>
      <properties>
        <picUrl>https://avatars.githubusercontent.com/u/6243207</picUrl>
      </properties>
    </developer>
  </developers>

  <issueManagement>
    <system>Github</system>
    <url>https://github.com/getargv/getargv.java/issues/</url>
  </issueManagement>

  <ciManagement>
    <system>Github</system>
    <url>https://github.com/getargv/getargv.java/actions</url>
  </ciManagement>

  <scm>
    <connection>scm:git:https://github.com/getargv/getargv.java.git</connection>
    <developerConnection>scm:git:ssh://github.com/getargv/getargv.java.git</developerConnection>
    <tag>HEAD</tag>
    <url>https://github.com/getargv/getargv.java</url>
  </scm>

  <distributionManagement>
    <snapshotRepository>
      <id>ossrh</id>
      <url>https://s01.oss.sonatype.org/content/repositories/snapshots</url>
    </snapshotRepository>
    <repository>
      <id>ossrh</id>
      <url>https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/</url>
    </repository>
  </distributionManagement>

  <licenses>
    <license>
      <name>BSD-3-Clause</name>
      <url>https://spdx.org/licenses/BSD-3-Clause.html</url>
      <distribution>repo</distribution>
      <comments>Allows: Commercial use, Distribution, Modification, and Private use; License and copyright notice required</comments>
    </license>
  </licenses>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>11</maven.compiler.source>
    <maven.compiler.target>$(java.specification.version)</maven.compiler.target>
    <maven.compiler.release>${java.specification.version}</maven.compiler.release>
    <exec.mainClass>${project.groupId}.Main</exec.mainClass>
    <fqn_underscores>cam_narzt_getargv_${project.artifactId}</fqn_underscores>
    <fqn_slashes>cam/narzt/getargv/${project.artifactId}</fqn_slashes>
    <gpg_keyname>E45D816B</gpg_keyname>
    <javah_cli_args>-h ${project.build.directory}/native/include -d ${java.io.tmpdir} --source-path ${project.build.sourceDirectory} ${project.build.sourceDirectory}/${fqn_slashes}.java</javah_cli_args>
  </properties>

  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>org.junit</groupId>
        <artifactId>junit-bom</artifactId>
        <version>5.9.2</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
    </dependencies>
  </dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>org.junit.jupiter</groupId>
      <artifactId>junit-jupiter</artifactId>
      <version>5.9.2</version>
      <scope>test</scope>
    </dependency>
  </dependencies>

  <build>
    <resources>
      <resource>
        <directory>${project.build.directory}/native/lib</directory>
        <includes>
          <include>**</include>
        </includes>
      </resource>
    </resources>

    <plugins>
      <plugin>
        <groupId>org.sonatype.plugins</groupId>
        <artifactId>nexus-staging-maven-plugin</artifactId>
        <version>1.6.13</version>
        <extensions>true</extensions>
        <configuration>
          <serverId>ossrh</serverId>
          <nexusUrl>https://s01.oss.sonatype.org/</nexusUrl>
          <autoReleaseAfterClose>true</autoReleaseAfterClose>
        </configuration>
      </plugin>
      <plugin>
        <artifactId>maven-clean-plugin</artifactId>
        <version>3.2.0</version>
      </plugin>
      <plugin>
        <artifactId>maven-resources-plugin</artifactId>
        <version>3.3.0</version>
      </plugin>
      <plugin>
        <artifactId>maven-source-plugin</artifactId>
        <version>3.2.1</version>
        <executions>
          <execution>
            <id>attach-sources</id>
            <goals>
              <goal>jar-no-fork</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
      <plugin>
        <artifactId>maven-javadoc-plugin</artifactId>
        <version>3.5.0</version>
        <executions>
          <execution>
            <id>attach-javadocs</id>
            <goals>
              <goal>jar</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
      <plugin>
        <artifactId>maven-gpg-plugin</artifactId>
        <version>3.0.1</version>
        <executions>
          <execution>
            <id>sign-artifacts</id>
            <phase>verify</phase>
            <goals>
              <goal>sign</goal>
            </goals>
          </execution>
        </executions>
        <configuration>
          <keyname>${gpg_keyname}</keyname>
        </configuration>
      </plugin>
      <plugin>
        <artifactId>maven-antrun-plugin</artifactId>
        <version>3.1.0</version>
        <configuration>
          <target>
            <delete dir="${project.build.directory}/native/lib" />
            <mkdir dir="${project.build.directory}/native/lib" />
          </target>
        </configuration>
        <executions>
          <execution>
            <id>createLibDir</id>
            <phase>process-resources</phase>
            <goals>
              <goal>run</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
      <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>native-maven-plugin</artifactId>
        <version>1.0-alpha-11</version>
        <extensions>true</extensions>
        <configuration>
          <jdkIncludePath>${java.home}/include</jdkIncludePath>
          <sources>
            <source>
              <directory>${basedir}/src/main/native</directory>
              <fileNames>
                <fileName>${fqn_underscores}.c</fileName>
              </fileNames>
            </source>
            <source>
              <directory>${project.build.directory}/native/include</directory>
            </source>
          </sources>

          <compilerProvider>generic</compilerProvider>
          <compilerExecutable>clang</compilerExecutable>
          <compilerStartOptions>
            <compilerStartOption>-fPIC</compilerStartOption>
          </compilerStartOptions>
          <compilerMiddleOptions>
            <compilerMiddleOption>-g</compilerMiddleOption>
          </compilerMiddleOptions>
          <compilerEndOptions>
            <compilerEndOption>-I</compilerEndOption>
            <compilerEndOption>${java.home}/include</compilerEndOption>
            <compilerEndOption>-I</compilerEndOption>
            <compilerEndOption>${java.home}/include/darwin</compilerEndOption>
          </compilerEndOptions>

          <linkerExecutable>clang</linkerExecutable>
          <linkerStartOptions>
            <linkerStartOption>-dynamiclib</linkerStartOption>
          </linkerStartOptions>
          <linkerMiddleOptions>
            <linkerMiddleOption>-g</linkerMiddleOption>
          </linkerMiddleOptions>
          <linkerEndOptions>
            <linkerEndOption>-lgetargv</linkerEndOption>
          </linkerEndOptions>
          <linkerOutputDirectory>${project.build.directory}/native/lib</linkerOutputDirectory>
          <linkerFinalName>lib${fqn_underscores}</linkerFinalName>
          <linkerFinalNameExt>dylib</linkerFinalNameExt>
        </configuration>
        <executions>
          <execution>
            <id>dylib</id>
            <phase>compile</phase>
            <goals>
              <goal>initialize</goal>
              <goal>compile</goal>
              <goal>link</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
      <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>exec-maven-plugin</artifactId>
        <version>3.1.0</version>
        <extensions>true</extensions>
        <configuration>
          <executable>javac</executable>
          <commandlineArgs>${javah_cli_args}</commandlineArgs>
          <useMavenLogger>true</useMavenLogger>
        </configuration>
        <executions>
          <execution>
            <id>generate-native-header</id>
            <phase>generate-sources</phase>
            <goals>
              <goal>exec</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
      <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.10.1</version>
      </plugin>
      <plugin>
        <artifactId>maven-surefire-plugin</artifactId>
        <version>3.0.0-M9</version>
        <configuration>
          <argLine>-Djava.library.path=${project.build.directory}/native/lib</argLine>
          <excludes>
            <exclude>**/*TestHelper.java</exclude>
          </excludes>
        </configuration>
      </plugin>
      <plugin>
        <artifactId>maven-jar-plugin</artifactId>
        <version>3.3.0</version>
      </plugin>
      <plugin>
        <artifactId>maven-install-plugin</artifactId>
        <version>3.1.0</version>
      </plugin>
      <plugin>
        <artifactId>maven-deploy-plugin</artifactId>
        <version>3.1.0</version>
      </plugin>
      <plugin>
        <artifactId>maven-site-plugin</artifactId>
        <version>4.0.0-M5</version>
      </plugin>
      <plugin>
        <artifactId>maven-project-info-reports-plugin</artifactId>
        <version>3.4.2</version>
      </plugin>
    </plugins>
  </build>
</project>

Solution

  • It looks like the intention is to build a native library into the ${project.build.directory}/native/lib directory during the compile phase, and then have the compiled library added into the final JAR during the package phase. The reason this doesn't work is that there is an additional step that is being missed. During the package phase, the Maven JAR Plugin packages files from the ${project.build.outputDirectory} (by default). An earlier process-resources phase copies project resources to this directory from the sources listed in the project/build/resources section of the POM. The process-resources phase runs before the compile phase in the Maven default lifecycle. That means that the dynamic library is compiled into the ${project.build.directory}/native/lib directory after that directory has been copied to ${project.build.outputDirectory}.

    If it seems to work when running mvn package, this is most likely because an existing compiled library already exists in this directory when the process-resources phase runs. If you run mvn clean package instead, I think you'll find that the library is missing from the JAR.

    The reason that running mvn release:clean release:prepare && mvn release:perform does not pick up the existing compiled library is that the Maven Release Plugin creates a fresh clone of the project from source control into a new directory when performing a release, and uses a separate build output directory. This is specifically to avoid the possibility of releasing files that only exist by coincidence on the release machine. So it's working as intended.

    The reason that it does include the library in the sources JAR is that the source:jar-no-fork goal binds to the package lifecycle phase, which runs after compile. Because it is intended to package source code, it adds files from the original sources and resources directories (including the additional ${project.build.directory}/native/lib resources directory) rather than the build output directory.

    The intended use of the Native Maven Plugin is to treat each native library as a separate module, but in this case, the native code is in the same project as the Java code. One possible solution, without restructuring the entire project, would be to bind the native-maven-plugin executions to an earlier lifecycle phase, such as generate-resources:

        <executions>
          <execution>
            <id>dylib</id>
            <phase>generate-resources</phase>
            <goals>
              <goal>initialize</goal>
              <goal>compile</goal>
              <goal>link</goal>
            </goals>
          </execution>
        </executions>
    

    An alternative option may be to configure the linkerOutputDirectory parameter of the Native Maven Plugin to output directly into ${project.build.outputDirectory}, and remove uses of ${project.build.directory}/native/lib. This could be a better option if you prefer not to include the library in the sources JAR.