From b573b8c498ea12cd2b84edb45be3036d9853d03e Mon Sep 17 00:00:00 2001 From: Tamas Cservenak Date: Tue, 28 May 2024 13:09:29 +0200 Subject: [PATCH] [MSHADE-478] Extra JARs feature (#228) Ability to add "extra JARs and Artifacts" and still enjoy the full benefits of relocation, resource transformation, etc. --- https://issues.apache.org/jira/browse/MSHADE-478 --- .../extrajar-missing-file/invoker.properties | 18 ++++ src/it/projects/extrajar-missing-file/pom.xml | 71 +++++++++++++ src/it/projects/extrajar/pom.xml | 79 ++++++++++++++ src/it/projects/extrajar/verify.bsh | 39 +++++++ .../maven/plugins/shade/mojo/ShadeMojo.java | 100 +++++++++++++++--- 5 files changed, 291 insertions(+), 16 deletions(-) create mode 100644 src/it/projects/extrajar-missing-file/invoker.properties create mode 100644 src/it/projects/extrajar-missing-file/pom.xml create mode 100644 src/it/projects/extrajar/pom.xml create mode 100644 src/it/projects/extrajar/verify.bsh diff --git a/src/it/projects/extrajar-missing-file/invoker.properties b/src/it/projects/extrajar-missing-file/invoker.properties new file mode 100644 index 00000000..f2a7dfb4 --- /dev/null +++ b/src/it/projects/extrajar-missing-file/invoker.properties @@ -0,0 +1,18 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); 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 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +invoker.buildResult = failure diff --git a/src/it/projects/extrajar-missing-file/pom.xml b/src/it/projects/extrajar-missing-file/pom.xml new file mode 100644 index 00000000..58e944e0 --- /dev/null +++ b/src/it/projects/extrajar-missing-file/pom.xml @@ -0,0 +1,71 @@ + + + + + + 4.0.0 + + org.apache.maven.its.shade.extrajar + test + 1.0 + jar + + extrajar + + Test that asserts failure if specified extra jar is not a file. + + + + 1.7.36 + + + + + org.slf4j + slf4j-api + ${slf4j.version} + + + + + + + org.apache.maven.plugins + maven-shade-plugin + @project.version@ + + + attach-shade + package + + shade + + + true + + ${project.build.directory}/no-such-file.jar + + + + + + + + diff --git a/src/it/projects/extrajar/pom.xml b/src/it/projects/extrajar/pom.xml new file mode 100644 index 00000000..67b43599 --- /dev/null +++ b/src/it/projects/extrajar/pom.xml @@ -0,0 +1,79 @@ + + + + + + 4.0.0 + + org.apache.maven.its.shade.extrajar + test + 1.0 + jar + + extrajar + + Test that asserts addition of extra-jars. + + + + 1.7.36 + + + + + org.slf4j + slf4j-api + ${slf4j.version} + + + + + + + org.apache.maven.plugins + maven-shade-plugin + @project.version@ + + + attach-shade + package + + shade + + + true + + org.slf4j:slf4j-simple:${slf4j.version} + + + + *:* + + META-INF/MANIFEST.MF + + + + + + + + + + diff --git a/src/it/projects/extrajar/verify.bsh b/src/it/projects/extrajar/verify.bsh new file mode 100644 index 00000000..b7d1db37 --- /dev/null +++ b/src/it/projects/extrajar/verify.bsh @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); 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 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import java.io.*; +import java.util.jar.*; + +String[] wanted = +{ + "org/slf4j/Logger.class", + "org/slf4j/impl/SimpleLogger.class" +}; + +JarFile jarFile = new JarFile( new File( basedir, "target/test-1.0-shaded.jar" ) ); + +for ( String path : wanted ) +{ + if ( jarFile.getEntry( path ) == null ) + { + throw new IllegalStateException( "wanted path is missing: " + path ); + } +} + +jarFile.close(); diff --git a/src/main/java/org/apache/maven/plugins/shade/mojo/ShadeMojo.java b/src/main/java/org/apache/maven/plugins/shade/mojo/ShadeMojo.java index 48a39d99..5d171b38 100644 --- a/src/main/java/org/apache/maven/plugins/shade/mojo/ShadeMojo.java +++ b/src/main/java/org/apache/maven/plugins/shade/mojo/ShadeMojo.java @@ -77,7 +77,6 @@ import org.eclipse.aether.graph.DependencyNode; import org.eclipse.aether.resolution.ArtifactRequest; import org.eclipse.aether.resolution.ArtifactResolutionException; -import org.eclipse.aether.resolution.ArtifactResult; import static org.apache.maven.plugins.shade.resource.UseDependencyReducedPom.createPomReplaceTransformers; @@ -393,6 +392,37 @@ public class ShadeMojo extends AbstractMojo { @Parameter(defaultValue = "false") private boolean skip; + /** + * Extra JAR files to infuse into shaded result. Accepts list of files that must exists. If any of specified + * files does not exist (or is not a file), Mojo will fail. + *

+ * Extra JARs will be processed in same way as main JAR (if any) is: applied relocation, resource transformers + * but not filtering. + *

+ * Note: this feature should be used lightly, is not meant as ability to replace dependency hull! It is more + * just a feature to be able to slightly "differentiate" shaded JAR from main only. + * + * @since 3.6.0 + */ + @Parameter + private List extraJars; + + /** + * Extra Artifacts to infuse into shaded result. Accepts list of GAVs in form of + * {@code :[:[:]]:} that will be resolved. If any of them + * cannot be resolved, Mojo will fail. + *

+ * The artifacts will be resolved (not transitively), and will be processed in same way as dependency JARs + * are (if any): applied relocation, resource transformers and filtering. + *

+ * Note: this feature should be used lightly, is not meant as ability to replace dependency hull! It is more + * just a feature to be able to slightly "differentiate" shaded JAR from main only. + * + * @since 3.6.0 + */ + @Parameter + private List extraArtifacts; + @Inject private MavenProjectHelper projectHelper; @@ -444,6 +474,17 @@ public void execute() throws MojoExecutionException { artifacts.add(project.getArtifact().getFile()); + if (extraJars != null && !extraJars.isEmpty()) { + for (File extraJar : extraJars) { + if (!Files.isRegularFile(extraJar.toPath())) { + throw new MojoExecutionException( + "Failed to create shaded artifact: parameter extraJars contains path " + extraJar + + " that is not a file (does not exist or is not a file)"); + } + artifacts.add(extraJar); + } + } + if (createSourcesJar) { File file = shadedSourcesArtifactFile(); if (file.isFile()) { @@ -680,7 +721,8 @@ private void processArtifactSelectors( Set sourceArtifacts, Set testArtifacts, Set testSourceArtifacts, - ArtifactSelector artifactSelector) { + ArtifactSelector artifactSelector) + throws MojoExecutionException { List excludedArtifacts = new ArrayList<>(); List pomArtifacts = new ArrayList<>(); @@ -688,7 +730,31 @@ private void processArtifactSelectors( List emptyTestArtifacts = new ArrayList<>(); List emptyTestSourceArtifacts = new ArrayList<>(); - for (Artifact artifact : project.getArtifacts()) { + ArrayList processedArtifacts = new ArrayList<>(); + if (extraArtifacts != null && !extraArtifacts.isEmpty()) { + processedArtifacts.addAll(extraArtifacts.stream() + .map(org.eclipse.aether.artifact.DefaultArtifact::new) + .map(RepositoryUtils::toArtifact) + .collect(Collectors.toList())); + + for (Artifact artifact : processedArtifacts) { + try { + org.eclipse.aether.artifact.Artifact resolved = + resolveArtifact(RepositoryUtils.toArtifact(artifact)); + if (resolved.getFile() != null) { + artifact.setFile(resolved.getFile()); + } + } catch (ArtifactResolutionException e) { + throw new MojoExecutionException( + "Failed to create shaded artifact: parameter extraArtifacts contains artifact " + + artifact.getId() + " that is not resolvable", + e); + } + } + } + processedArtifacts.addAll(project.getArtifacts()); + + for (Artifact artifact : processedArtifacts) { if (!artifactSelector.isSelected(artifact)) { excludedArtifacts.add(artifact.getId()); @@ -802,7 +868,7 @@ private void copyFiles(File source, File target) throws IOException { } private File resolveArtifactForClassifier(Artifact artifact, String classifier) { - org.eclipse.aether.artifact.Artifact coordinate = RepositoryUtils.toArtifact(new DefaultArtifact( + Artifact toResolve = new DefaultArtifact( artifact.getGroupId(), artifact.getArtifactId(), artifact.getVersionRange() == null @@ -812,24 +878,26 @@ private File resolveArtifactForClassifier(Artifact artifact, String classifier) artifact.getType(), classifier, artifact.getArtifactHandler(), - artifact.isOptional())); - - ArtifactRequest request = new ArtifactRequest( - coordinate, RepositoryUtils.toRepos(project.getRemoteArtifactRepositories()), "shade"); - - Artifact resolvedArtifact; + artifact.isOptional()); try { - ArtifactResult result = repositorySystem.resolveArtifact(session.getRepositorySession(), request); - resolvedArtifact = RepositoryUtils.toArtifact(result.getArtifact()); + org.eclipse.aether.artifact.Artifact resolved = resolveArtifact(RepositoryUtils.toArtifact(toResolve)); + if (resolved.getFile() != null) { + return resolved.getFile(); + } + return null; } catch (ArtifactResolutionException e) { getLog().warn("Could not get " + classifier + " for " + artifact); return null; } + } - if (resolvedArtifact.isResolved()) { - return resolvedArtifact.getFile(); - } - return null; + private org.eclipse.aether.artifact.Artifact resolveArtifact(org.eclipse.aether.artifact.Artifact artifact) + throws ArtifactResolutionException { + return repositorySystem + .resolveArtifact( + session.getRepositorySession(), + new ArtifactRequest(artifact, project.getRemoteProjectRepositories(), "shade")) + .getArtifact(); } private List getRelocators() {