Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Maven plugin for dumping dependencies (cross-repo navigation) #735

Merged
merged 20 commits into from
Aug 9, 2024
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 49 additions & 16 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,26 +17,15 @@ jobs:
java: [8, 11, 17, 21]
steps:
- uses: actions/checkout@v4

- uses: actions/setup-java@v4
with:
distribution: "temurin"
cache: "sbt"
java-version: ${{ matrix.java }}
- name: Main project tests
run: sbt test

benchmarks-test:
runs-on: ubuntu-latest
name: Benchmark tests
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
distribution: "temurin"
cache: "sbt"
java-version: 17
- name: Run sample benchmarks
run: sbt 'bench/Jmh/run -i 1 -f1 -t1 -foe true'
- name: Main project tests
run: sbt test

docker_test:
runs-on: ${{ matrix.os }}
Expand Down Expand Up @@ -94,8 +83,8 @@ jobs:
steps:
- uses: actions/checkout@v2
- run: yarn global add @bazel/bazelisk
- run: sbt cli/pack
- run: echo "$PWD/scip-java/target/pack/bin" >> $GITHUB_PATH
- run: sbt build
- run: echo "$PWD/out/bin" >> $GITHUB_PATH
- name: Auto-index scip-java codebase
run: |
scip-java index --build-tool=bazel --bazel-scip-java-binary=$(which scip-java)
Expand All @@ -116,4 +105,48 @@ jobs:
distribution: "temurin"
java-version: 17
cache: "sbt"

- run: sbt checkAll

- name: Run sample benchmarks
run: sbt 'bench/Jmh/run -i 1 -f1 -t1 -foe true'


maven:
runs-on: ubuntu-latest
name: Maven tests
strategy:
fail-fast: false
matrix:
java: [8, 11, 17, 21]
steps:
- uses: actions/checkout@v4

- uses: actions/setup-java@v4
with:
distribution: "temurin"
cache: "sbt"
java-version: ${{ matrix.java }}


- run: |
sbt build publishM2 publishLocal dumpScipJavaVersion
echo "SCIP_JAVA_VERSION=$(cat VERSION)" >> $GITHUB_ENV
echo "SCIP_JAVA_CLI=$PWD/out/bin/scip-java" >> $GITHUB_ENV

- run: |
mvn clean verify -DskipTests -Dscip-java.version=$SCIP_JAVA_VERSION sourcegraph:sourcegraphDependencies
working-directory: examples/maven-example

- run: $SCIP_JAVA_CLI index-semanticdb target/semanticdb-targetroot
working-directory: examples/maven-example

- run: |
set -e
grep org.hamcrest target/semanticdb-targetroot/dependencies.txt
grep $PWD/src/main/java target/semanticdb-targetroot/dependencies.txt
working-directory: examples/maven-example

- run: du -h index.scip
working-directory: examples/maven-example

41 changes: 41 additions & 0 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,7 @@ lazy val scipProto = project
lazy val scip = project
.in(file("scip-semanticdb"))
.settings(
publishMavenStyle := true,
moduleName := "scip-semanticdb",
javaToolchainVersion := "8",
javaOnlySettings,
Expand All @@ -214,6 +215,36 @@ lazy val scip = project
)
.dependsOn(semanticdb, scipProto)

lazy val mavenPlugin = project
.in(file("maven-plugin"))
.settings(
moduleName := "maven-plugin",
javaToolchainVersion := "8",
javaOnlySettings,
libraryDependencies ++=
Seq(
"org.apache.maven" % "maven-plugin-api" % "3.6.3",
"org.apache.maven.plugin-tools" % "maven-plugin-annotations" % "3.6.4" %
Provided,
"org.apache.maven" % "maven-project" % "2.2.1"
),
Compile / resourceGenerators +=
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maven has a special packaging mode for plugins, which seems to be nothing more than a post-processing step that introspects the plugin's pom.xml + all the Mojo files, then writes the special plugin.xml file.

https://maven.apache.org/guides/plugin/guide-java-plugin-development.html

Setting up a Maven build just for that seems to be counterproductive, so we just write the plugin.xml by hand using a template. It's obviously not great, but at this point I don't see a better solution, and we don't expect this plugin to change very frequently.

plugin.xml will be force-kept in sync by tests, because Maven derives a lot of information from it, so if it's broken - things won't work.

For local development this allows you to do mavenPlugin/publishM2 and use that in maven builds.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense. I love sbt

Def.task {
val dir = (Compile / managedResourceDirectories).value.head /
"META-INF" / "maven"
IO.createDirectory(dir)
val file = dir / "plugin.xml"
val template = IO.read(
(Compile / resourceDirectory).value / "META-INF" / "maven" /
"plugin.template.xml"
)

IO.write(file, template.replace("@VERSION@", version.value))

Seq(file)
}
)

lazy val cli = project
.in(file("scip-java"))
.settings(
Expand Down Expand Up @@ -602,3 +633,13 @@ dumpScipJavaVersion := {

IO.write((ThisBuild / baseDirectory).value / "VERSION", versionValue)
}

lazy val build = taskKey[Unit](
"Build `scip-java` CLI and place it in the out/bin/scip-java. "
)

build := {
val source = (cli / pack).value
val destination = (ThisBuild / baseDirectory).value / "out"
IO.copyDirectory(source, destination)
}
101 changes: 101 additions & 0 deletions examples/maven-example/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
<?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>com.sourcegraph</groupId>
<artifactId>example</artifactId>
<version>${revision}</version>

<name>example</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<revision>1.0.0-SNAPSHOT</revision>
</properties>

<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.sourcegraph</groupId>
<artifactId>semanticdb-javac</artifactId>
<version>${scip-java.version}</version>
</dependency>
</dependencies>

<build>
<pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
<plugins>
<!-- clean lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#clean_Lifecycle -->
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<!-- default lifecycle, jar packaging: see https://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging -->
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<compilerArgs>
<arg>-Xplugin:semanticdb -sourceroot:${session.executionRootDirectory} -targetroot:${session.executionRootDirectory}/target/semanticdb-targetroot</arg>
</compilerArgs>
</configuration>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
</plugin>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
</plugin>
<!-- site lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#site_Lifecycle -->
<plugin>
<artifactId>maven-site-plugin</artifactId>
<version>3.7.1</version>
</plugin>
<plugin>
<artifactId>maven-project-info-reports-plugin</artifactId>
<version>3.0.0</version>
</plugin>
<plugin>
<groupId>com.sourcegraph</groupId>
<artifactId>maven-plugin</artifactId>
<version>${scip-java.version}</version>
<executions>
<execution>
<goals>
<goal>sourcegraphDependencies</goal>
</goals>
</execution>
</executions>
<!-- <configuration> -->
<!-- <scope>test</scope> -->
<!-- </configuration> -->
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
13 changes: 13 additions & 0 deletions examples/maven-example/src/main/java/App.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package test;

/**
* Hello world!
*
*/
public class App
{
public static void main( String[] args )
{
System.out.println( "Hello World!" );
}
}
20 changes: 20 additions & 0 deletions examples/maven-example/src/test/java/AppTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package test;

import static org.junit.Assert.assertTrue;

import org.junit.Test;

/**
* Unit test for simple App.
*/
public class AppTest
{
/**
* Rigorous Test :-)
*/
@Test
public void shouldAnswerWithTrue()
{
assertTrue( true );
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package com.sourcegraph.maven;

import org.apache.maven.artifact.Artifact;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.plugins.annotations.ResolutionScope;
import org.apache.maven.project.MavenProject;

import java.io.BufferedWriter;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.Set;

@Mojo(
name = "sourcegraphDependencies",
defaultPhase = LifecyclePhase.COMPILE,
requiresDependencyResolution = ResolutionScope.COMPILE,
requiresProject = true)
public class DependencyWriterMojo extends AbstractMojo {
@Parameter(defaultValue = "${project}", required = true, readonly = true)
MavenProject project;

@Parameter(
property = "semanticdb.targetRoot",
defaultValue = "${session.executionRootDirectory}/target/semanticdb-targetroot")
private String targetRoot;

public void execute() throws MojoExecutionException, MojoFailureException {
Set artifacts = project.getArtifacts();
StringBuilder builder = new StringBuilder();

String groupID = project.getGroupId();
String artifactID = project.getArtifactId();
String version = project.getVersion();
List sourceRoots = project.getCompileSourceRoots();

if (groupID == null || artifactID == null) {
getLog()
.warn(
"Failed to extract groupID and artifactID from the project.\n"
+ "This will not prevent a SCIP index from being created, but the symbols \n"
+ "extracted from this project won't be available for cross-repository navigation,\n"
+ "as this project doesn't define any Maven coordinates by which it can be referred back to.\n"
+ "See here for more details: https://sourcegraph.github.io/scip-java/docs/manual-configuration.html#step-5-optional-enable-cross-repository-navigation\n");
} else {
for (Object root : sourceRoots) {
if (root instanceof String) {
String rootString = (String) root;
builder.append(
String.format("%s\t%s\t%s\t%s\n", groupID, artifactID, version, rootString));
}
}
}

for (Object dep : artifacts) {
if (dep instanceof Artifact) {
Artifact artifact = (Artifact) dep;
if (artifact.getFile() != null) {
builder.append(
String.format(
"%s\t%s\t%s\t%s\n",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TSV ftw!

artifact.getGroupId(),
artifact.getArtifactId(),
artifact.getVersion(),
artifact.getFile()));
} else {
getLog()
.warn(
"Dependency "
+ summariseArtifact(artifact)
+ " does not have a resolved file, so it won't be added to the dependencies.txt");
}
}
}

Path dependenciesFile = Paths.get(targetRoot).resolve("dependencies.txt");

try {
Files.createDirectories(dependenciesFile.getParent());
try (BufferedWriter writer = Files.newBufferedWriter(dependenciesFile)) {
writer.write(builder.toString());
}
} catch (IOException e) {
throw new MojoFailureException(
"Failed to write dependencies to file " + dependenciesFile.toString(), e);
}

getLog().info("Dependencies were written to " + dependenciesFile.toAbsolutePath().toString());
}

private String summariseArtifact(Artifact artifact) {
return String.format(
"%s:%s:%s", artifact.getGroupId(), artifact.getArtifactId(), artifact.getVersion());
}
}
Loading
Loading