这篇简短的博客文章提供了另一个Groovy脚本,该脚本提供了两个JAR(或WAR或EAR)文件及其MANIFEST.MF文件的简单区别。 它代表了我之前 写过的JAR比较脚本 , Rick 添加的Groovy的CliBuilder以允许关闭某些输出数据,我的MANIFEST.MF比较脚本以及使用命令行标志的能力的组合。使脚本能够输出其他清单比较数据。
jarDiff2.groovy
#!/usr/bin/env groovy
/**
* jarDiff2.groovy
*
* jarDiff2.groovy -htsc <first_jar_file> <second_jar_file>
*
* Script that compares two JAR files, reporting basic characteristics of each
* along with differences between the two JARs. This script is based on the
* original jarrDiff.groovy script with enhancements provided by Rick and with
* enhancements for seeing more detailed differences between two Manifest files.
*
* Note this script can be used on any files using the Java ARchive format,
* including WAR and EAR files.
*/
if (args.length < 2)
{
println "\nUSAGE: jarDiff2.groovy -htsc <first_jar_file> <second_jar_file>\n"
System.exit(-1)
}
TOTAL_WIDTH = 180
COLUMN_WIDTH = TOTAL_WIDTH / 2 - 3
ROW_SEPARATOR = "-".multiply(TOTAL_WIDTH)
import java.util.jar.Attributes
import java.util.jar.JarFile
// Set up the CLI options
//
def cli = new CliBuilder( usage: 'jarDiff.groovy -h -tsc ')
cli.with
{
h longOpt: 'help', 'usage information'
t longOpt: 'ignoreTime', args: 0, required: false, type: Boolean, 'Ignore time differences'
s longOpt: 'ignoreSize', args: 0, required: false, type: Boolean, 'Ignore size differences'
c longOpt: 'ignoreCrc', args: 0, required: false, type: Boolean, 'Ignore CRC differences'
m longOpt: 'displayManifestDetails', args: 0, required: false, type: Boolean, 'Display Manifest differences details'
}
def opt = cli.parse(args)
if (!opt) return
if (opt.h)
{
cli.usage()
System.exit(-1)
}
def ignoreTime = opt.t
def ignoreSize = opt.s
def ignoreCrc = opt.c
def displayManifestDiffDetails = opt.m
if (opt.arguments().size < 2)
{
println "Two JAR files required\n"
cli.usage()
System.exit(-1)
}
def file1Name = opt.arguments()[0]
def jar1File = new JarFile(file1Name)
def jar1 = extractJarContents(jar1File)
def file2Name = opt.arguments()[1]
def jar2File = new JarFile(file2Name)
def jar2 = extractJarContents(jar2File)
def entriesInJar1ButNotInJar2 = jar1.keySet() - jar2.keySet()
def entriesInJar2ButNotInJar1 = jar2.keySet() - jar1.keySet()
println ROW_SEPARATOR
println "| ${file1Name.center(COLUMN_WIDTH)} |${file2Name.center(COLUMN_WIDTH)} |"
print "| ${(Integer.toString(jar1File.size()) + " bytes").center(COLUMN_WIDTH)} |"
println "${(Integer.toString(jar2File.size()) + " bytes").center(COLUMN_WIDTH)} |"
println ROW_SEPARATOR
if (jar1File.manifest != jar2File.manifest)
{
if (displayManifestDiffDetails)
{
displayDetailedManifestFilesDifferences(jar1File.manifest.mainAttributes, jar2File.manifest.mainAttributes)
}
else
{
def manifestPreStr = "# Manifest Entries: "
def manifest1Str = manifestPreStr + Integer.toString(jar1File.manifest.mainAttributes.size())
print "| ${manifest1Str.center(COLUMN_WIDTH)} |"
def manifest2Str = manifestPreStr + Integer.toString(jar2File.manifest.mainAttributes.size())
println "${manifest2Str.center(COLUMN_WIDTH)} |"
println ROW_SEPARATOR
}
}
entriesInJar1ButNotInJar2.each
{ entry1 ->
print "| ${entry1.center(COLUMN_WIDTH)} |"
println "${" ".center(entry1.size() > COLUMN_WIDTH ? 2 * COLUMN_WIDTH - entry1.size() : COLUMN_WIDTH)} |"
println ROW_SEPARATOR
}
entriesInJar2ButNotInJar1.each
{ entry2 ->
print "| ${" ".center(entry2.size() > COLUMN_WIDTH ? 2 * COLUMN_WIDTH - entry2.size() : COLUMN_WIDTH)}"
println "| ${entry2.center(COLUMN_WIDTH)} |"
println ROW_SEPARATOR
}
jar1.each
{ key, value ->
if (!entriesInJar1ButNotInJar2.contains(key))
{
def jar2Entry = jar2.get(key)
if (value != jar2Entry)
{
println "| ${key.center(COLUMN_WIDTH)} |${jar2Entry.name.center(COLUMN_WIDTH)} |"
if (value.crc != jar2Entry.crc)
{
def crc1Str = "CRC: ${value.crc}"
def crc2Str = "CRC: ${jar2Entry.crc}"
print "| ${crc1Str.center(COLUMN_WIDTH)} |"
println "${crc2Str.center(COLUMN_WIDTH)} |"
}
if (value.size != jar2Entry.size)
{
def size1Str = "${value.size} bytes"
def size2Str = "${jar2Entry.size} bytes"
print "| ${size1Str.center(COLUMN_WIDTH)} |"
println "${size2Str.center(COLUMN_WIDTH)} |"
}
if (value.time != jar2Entry.time)
{
boolean crcDiff = (!ignoreCrc && value.crc != jar2Entry.crc)
boolean sizeDiff = (!ignoreSize && value.size != jar2Entry.size)
boolean timeDiff = (!ignoreTime && value.time != jar2Entry.time)
if(crcDiff || sizeDiff || timeDiff)
{
println "| ${key.center(COLUMN_WIDTH)} |${jar2Entry.name.center(COLUMN_WIDTH)} |"
if (crcDiff)
{
def crc1Str = "CRC: ${value.crc}"
def crc2Str = "CRC: ${jar2Entry.crc}"
print "| ${crc1Str.center(COLUMN_WIDTH)} |"
println "${crc2Str.center(COLUMN_WIDTH)} |"
}
if (sizeDiff)
{
def size1Str = "${value.size} bytes"
def size2Str = "${jar2Entry.size} bytes"
print "| ${size1Str.center(COLUMN_WIDTH)} |"
println "${size2Str.center(COLUMN_WIDTH)} |"
}
if (timeDiff)
{
def time1Str = "${new Date(value.time)}"
def time2Str = "${new Date(jar2Entry.time)}"
print "| ${time1Str.center(COLUMN_WIDTH)} |"
println "${time2Str.center(COLUMN_WIDTH)} |"
}
println ROW_SEPARATOR
}
}
}
}
}
/**
* Provide mapping of JAR entry names to characteristics about that JAR entry
* for the JAR indicated by the provided JAR file name.
*
* @param jarFile JAR file from which to extract contents.
* @return JAR entries and thir characteristics.
*/
def TreeMap<String, JarCharacteristics> extractJarContents(JarFile jarFile)
{
def jarContents = new TreeMap<String, JarCharacteristics>()
entries = jarFile.entries()
entries.each
{ entry->
jarContents.put(entry.name, new JarCharacteristics(entry.name, entry.crc, entry.size, entry.time));
}
return jarContents
}
/**
* Add more detailed Manifest differences to output report.
*
* @param manifest1Attrs Main attributes of first JAR file's Manifest
* @param manifest2Attrs Main attributes of second JAR file's Manifest.
*/
def displayDetailedManifestFilesDifferences(
Attributes manifest1Attrs, Attributes manifest2Attrs)
{
def attrsIn1ButNot2 = manifest1Attrs.keySet() - manifest2Attrs.keySet()
def attrsIn2ButNot1 = manifest2Attrs.keySet() - manifest1Attrs.keySet()
attrsIn1ButNot2.each
{
def attr1onlyStr = "${it}=${manifest1Attrs.get(it)}"
print "| ${attr1onlyStr.center(COLUMN_WIDTH)} |"
println "${" ".center(attr1onlyStr.size() > COLUMN_WIDTH ? 2 * COLUMN_WIDTH - attr1onlyStr.size() : COLUMN_WIDTH)} |"
}
println ROW_SEPARATOR
attrsIn2ButNot1.each
{
def attr2onlyStr = "${it}=${manifest2Attrs.get(it)}"
print "| ${" ".center(attr2onlyStr.size() > COLUMN_WIDTH ? 2 * COLUMN_WIDTH - attr2onlyStr.size() : COLUMN_WIDTH)}|"
println " ${attr2onlyStr.center(COLUMN_WIDTH)} |"
}
println ROW_SEPARATOR
manifest1Attrs.each
{
def key = it.key
if (it.value != manifest2Attrs.get(key) && !attrsIn1ButNot2.contains(it.key))
{
def attr1Str = "${key}=${manifest1Attrs.get(key)}"
print "| ${attr1Str.center(COLUMN_WIDTH)}"
def attr2Str = "${key}=${manifest2Attrs.get(key)}"
println "| ${attr2Str.center(COLUMN_WIDTH)} |"
}
}
println ROW_SEPARATOR
}
与该脚本的第一个版本一样,该脚本依赖于非常简单的JarCharacteristics.groovy
类。 在Groovy 1.8或更高版本中,此类很简单(Groovy的早期版本需要实现一些其他代码,因为它们没有@Canonical ):
JarCharacteristics.groovy
@groovy.transform.Canonical
class JarCharacteristics
{
String name
long crc
long size
long time
}
这篇文章中显示的JAR差异脚本版本使用命令行参数来指定何时不显示由于CRC,大小或修改日期而引起的JAR条目差异。 指定该标志时,还会显示一个附加标志,以显示更详细的清单文件差异。 默认值是显示基于CRC,大小和修改日期的常规JAR条目差异,但不显示清单文件中的详细差异。 这些标志可用于禁用CRC,大小或修改日期的差异,或启用更详细的清单文件差异输出。
翻译自: https://www.javacodegeeks.com/2013/07/enhanced-groovy-based-jarmanifest-diff-tool.html