如何将 Manifest 部分与 Gradle 和 shadowJar 合并
How to merge Manifest sections with Gradle and shadowJar
我需要什么
我们用 Gradle 和 shadowJar. Some of the libraries we use, utilize individual sections in Jar Manifests 打包我们的产品,特别是 Implementation-Title 和
实施版本。这些有时会显示在我们产品的(输出)中,所以我希望它们能够在 shawdowJar-Process 中存活下来。
例子
lib1.jar/META-INF/MANIFEST.MF
Manifest-Version: 1.0
...
Name: org/some/lib
...
Implementation-Title: someLib
Implementation-Version: 2.3
...
lib2.jar/META-INF/MANIFEST.MF
Manifest-Version: 1.0
...
Name: org/some/other/lib
...
Implementation-Title: someOtherLib
Implementation-Version: 5.7-RC
...
=>
产品.jar/META-INF/MANIFEST.MF
Manifest-Version: 1.0
...
Name: org/some/lib
...
Implementation-Title: someLib
Implementation-Version: 2.3
...
Name: org/some/other/lib
...
Implementation-Title: someOtherLib
Implementation-Version: 5.7-RC
...
我发现了什么
- 使用 shadowJar manipulate the resulting Manifest 相当容易:
project.shadowJar {
manifest {
attributes(["Implementation-Title" : "someLib"], "org/some/lib")
attributes(["Implementation-Title" : "someOtherLib"], "org/some/other/lib")
}
}
静态生成我想要的内容。
- shadowJar 可以给我提供a list of dependencies。但是,当我像这样遍历 FileCollection 时
project.shadowJar {
manifest {
for (dependency in includedDependencies) {
// read in jar file and set attributes
}
}
}
Gradle 不高兴:“依赖项配置‘:project:products::compile’包含在依赖项解析中后,无法更改依赖项。”
- 当我定义一个新任务时
def dependencies = [];
project.tasks.register('resolveDependencies') {
doFirst {
gradleProject.configurations.compile.resolvedConfiguration.resolvedArtifacts.each {
dependencies.add(it.file)
}
}
}
project.tasks['shadowJar'].dependsOn(project.tasks['resolveDependencies']);
project.shadowJar {
manifest {
// dependencies will be empty when this code is called
for (dependency in dependencies) {
// read in jar file and set attributes
}
}
}
依赖关系没有及时解决
我想知道的
如何在不扰乱 Gradle 的情况下访问依赖项?或者,还有另一种方法可以将命名的各个部分与 shadowJar 合并吗?
根据 https://github.com/johnrengelman/shadow/issues/369 应该使用 shadowJar 的 Transformer 接口。
所以来了:
import com.github.jengelman.gradle.plugins.shadow.transformers.Transformer;
import com.github.jengelman.gradle.plugins.shadow.transformers.TransformerContext;
import java.io.ByteArrayOutputStream;
import java.util.jar.Attributes;
import java.util.jar.Manifest;
import java.util.Map.Entry;
import shadow.org.apache.tools.zip.ZipOutputStream;
import shadow.org.apache.tools.zip.ZipEntry;
import shadow.org.codehaus.plexus.util.IOUtil;
import org.gradle.api.file.FileTreeElement;
import static java.nio.charset.StandardCharsets.*
import static java.util.jar.JarFile.*;
/**ManifestVersionMergeTransformer appends all version information sections from manifest files to the resulting manifest file.
* @author Robert Lichtenberger
*/
public class ManifestMergeTransformer implements Transformer {
String includePackages; // regular expression that must match a given package
String excludePackages; // regular expression that must not match a given package
private Manifest manifest;
@Override
public boolean canTransformResource(FileTreeElement element) {
MANIFEST_NAME.equalsIgnoreCase(element.relativePath.pathString);
}
@Override
public void transform(TransformerContext context) {
if (manifest == null) {
manifest = new Manifest(context.is);
} else {
Manifest toMerge = new Manifest(context.is);
for (Entry<String, Attributes> entry : toMerge.getEntries().entrySet()) {
if (mustInclude(entry.getKey())) {
manifest.getEntries().put(entry.getKey(), entry.getValue());
}
}
}
IOUtil.close(context.is);
}
private boolean mustInclude(String packageName) {
return (includePackages == null || packageName.matches(includePackages)) && (excludePackages == null || !packageName.matches(excludePackages));
}
@Override
public boolean hasTransformedResource() {
return true;
}
@Override
public void modifyOutputStream(ZipOutputStream os, boolean preserveFileTimestamps) {
ZipEntry entry = new ZipEntry(MANIFEST_NAME);
entry.time = TransformerContext.getEntryTimestamp(preserveFileTimestamps, entry.time);
os.putNextEntry(entry);
if (manifest != null) {
ByteArrayOutputStream manifestContents = new ByteArrayOutputStream();
manifest.write(manifestContents);
os.write(manifestContents.toByteArray());
}
}
}
我需要什么
我们用 Gradle 和 shadowJar. Some of the libraries we use, utilize individual sections in Jar Manifests 打包我们的产品,特别是 Implementation-Title 和 实施版本。这些有时会显示在我们产品的(输出)中,所以我希望它们能够在 shawdowJar-Process 中存活下来。
例子
lib1.jar/META-INF/MANIFEST.MF
Manifest-Version: 1.0
...
Name: org/some/lib
...
Implementation-Title: someLib
Implementation-Version: 2.3
...
lib2.jar/META-INF/MANIFEST.MF
Manifest-Version: 1.0
...
Name: org/some/other/lib
...
Implementation-Title: someOtherLib
Implementation-Version: 5.7-RC
...
=> 产品.jar/META-INF/MANIFEST.MF
Manifest-Version: 1.0
...
Name: org/some/lib
...
Implementation-Title: someLib
Implementation-Version: 2.3
...
Name: org/some/other/lib
...
Implementation-Title: someOtherLib
Implementation-Version: 5.7-RC
...
我发现了什么
- 使用 shadowJar manipulate the resulting Manifest 相当容易:
project.shadowJar {
manifest {
attributes(["Implementation-Title" : "someLib"], "org/some/lib")
attributes(["Implementation-Title" : "someOtherLib"], "org/some/other/lib")
}
}
静态生成我想要的内容。
- shadowJar 可以给我提供a list of dependencies。但是,当我像这样遍历 FileCollection 时
project.shadowJar {
manifest {
for (dependency in includedDependencies) {
// read in jar file and set attributes
}
}
}
Gradle 不高兴:“依赖项配置‘:project:products:
- 当我定义一个新任务时
def dependencies = [];
project.tasks.register('resolveDependencies') {
doFirst {
gradleProject.configurations.compile.resolvedConfiguration.resolvedArtifacts.each {
dependencies.add(it.file)
}
}
}
project.tasks['shadowJar'].dependsOn(project.tasks['resolveDependencies']);
project.shadowJar {
manifest {
// dependencies will be empty when this code is called
for (dependency in dependencies) {
// read in jar file and set attributes
}
}
}
依赖关系没有及时解决
我想知道的
如何在不扰乱 Gradle 的情况下访问依赖项?或者,还有另一种方法可以将命名的各个部分与 shadowJar 合并吗?
根据 https://github.com/johnrengelman/shadow/issues/369 应该使用 shadowJar 的 Transformer 接口。
所以来了:
import com.github.jengelman.gradle.plugins.shadow.transformers.Transformer;
import com.github.jengelman.gradle.plugins.shadow.transformers.TransformerContext;
import java.io.ByteArrayOutputStream;
import java.util.jar.Attributes;
import java.util.jar.Manifest;
import java.util.Map.Entry;
import shadow.org.apache.tools.zip.ZipOutputStream;
import shadow.org.apache.tools.zip.ZipEntry;
import shadow.org.codehaus.plexus.util.IOUtil;
import org.gradle.api.file.FileTreeElement;
import static java.nio.charset.StandardCharsets.*
import static java.util.jar.JarFile.*;
/**ManifestVersionMergeTransformer appends all version information sections from manifest files to the resulting manifest file.
* @author Robert Lichtenberger
*/
public class ManifestMergeTransformer implements Transformer {
String includePackages; // regular expression that must match a given package
String excludePackages; // regular expression that must not match a given package
private Manifest manifest;
@Override
public boolean canTransformResource(FileTreeElement element) {
MANIFEST_NAME.equalsIgnoreCase(element.relativePath.pathString);
}
@Override
public void transform(TransformerContext context) {
if (manifest == null) {
manifest = new Manifest(context.is);
} else {
Manifest toMerge = new Manifest(context.is);
for (Entry<String, Attributes> entry : toMerge.getEntries().entrySet()) {
if (mustInclude(entry.getKey())) {
manifest.getEntries().put(entry.getKey(), entry.getValue());
}
}
}
IOUtil.close(context.is);
}
private boolean mustInclude(String packageName) {
return (includePackages == null || packageName.matches(includePackages)) && (excludePackages == null || !packageName.matches(excludePackages));
}
@Override
public boolean hasTransformedResource() {
return true;
}
@Override
public void modifyOutputStream(ZipOutputStream os, boolean preserveFileTimestamps) {
ZipEntry entry = new ZipEntry(MANIFEST_NAME);
entry.time = TransformerContext.getEntryTimestamp(preserveFileTimestamps, entry.time);
os.putNextEntry(entry);
if (manifest != null) {
ByteArrayOutputStream manifestContents = new ByteArrayOutputStream();
manifest.write(manifestContents);
os.write(manifestContents.toByteArray());
}
}
}