Skip to content

Commit

Permalink
Add quiet mode and gradle script for checking transitive deps.
Browse files Browse the repository at this point in the history
  • Loading branch information
LexManos committed Mar 18, 2024
1 parent 5b33e42 commit 19bd85b
Show file tree
Hide file tree
Showing 3 changed files with 133 additions and 13 deletions.
103 changes: 102 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ dependencies {
implementation(libs.jopt.simple)
implementation(libs.gson)
implementation(libs.guava)
compileOnly(libs.nulls)
implementation(libs.nulls)

testImplementation(libs.junit.api)
testRuntimeOnly(libs.bundles.junit.runtime)
Expand Down Expand Up @@ -65,6 +65,107 @@ tasks.named('assemble').configure {
dependsOn 'shadowJar'
}

def getArtifacts(coord) {
def udep = project.dependencies.create(coord)
def cfg = project.configurations.detachedConfiguration(udep)
cfg.setTransitive(true)
def files = cfg.resolve()
def ret = [
main: null,
libs: [:]
]

cfg.resolvedConfiguration.resolvedArtifacts.each {
def art = [
group: it.moduleVersion.id.group,
name: it.moduleVersion.id.name,
version: it.moduleVersion.id.version,
classifier: it.classifier,
extension: it.extension,
file: it.file
]

def desc = "${art.group}:${art.name}:${art.version}"
if (art.classifier != null)
desc += ":${art.classifier}"
if (art.extension != 'jar')
desc += "@${art.extension}"

if (coord == desc) {
ret.main = art.file
} else {
ret.libs["${art.group}:${art.name}"] = [
version: art.version,
name: desc,
file: art.file
]
}
}

return ret
}

tasks.register('testLibrary').configure {
dependsOn 'shadowJar'
doFirst {
def libs =
//['cpw.mods:securejarhandler:2.1.10', 'net.minecraftforge:securemodules:2.2.2']
['net.minecraftforge:coremods:5.0.1', 'net.minecraftforge:coremods:5.1.2']
//['cpw.mods:modlauncher:9.0.24', 'net.minecraftforge:modlauncher:10.1.1']
def base = getArtifacts(libs[0])
def input = getArtifacts(libs[1])

def cmd = [
tasks.named('shadowJar').get().archiveFile.get().asFile.absolutePath,
'--quiet',
'--api'
]
base.libs.each { ga, lib ->
cmd += ['--base-lib', lib.file.absolutePath]
}
input.libs.each { ga, lib ->
cmd += ['--input-lib', lib.file.absolutePath]
}

logger.lifecycle(libs[0] + ' -> ' + libs[1])
javaexec {
ignoreExitValue = true
main = '-jar'
args = cmd + [
'--base-jar', base.main.absolutePath,
'--input-jar', input.main.absolutePath
]
}

base.libs.each { ga, lib ->
def ilib = input.libs[ga]
if (ilib == null) {
logger.lifecycle('')
logger.lifecycle("$ga: ${lib.version} -> removed")
} else if (ilib.version != lib.version) {
logger.lifecycle('')
logger.lifecycle("$ga: ${lib.version} -> ${ilib.version}")
javaexec {
ignoreExitValue = true
main = '-jar'
args = cmd + [
'--base-jar', lib.file.absolutePath,
'--input-jar', ilib.file.absolutePath
]
}
}
}
input.libs.each { ga, lib ->
if (base.libs[ga] == null) {
logger.lifecycle('')
logger.lifecycle("$ga: missing -> ${lib.version}")
}
}

logger.lifecycle('')
}
}

tasks.withType(JavaCompile).configureEach {
options.encoding = 'UTF-8' // Use the UTF-8 charset for Java compilation
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,20 @@

import java.io.File;
import java.util.List;
import java.util.function.Consumer;

public class ConsoleTool {
public static void main(String[] args) {
try {
OptionParser parser = new OptionParser();
OptionSpec<Void> quietO = parser.accepts("quiet", "Disabels some debug logging");
OptionSpec<Void> apiO = parser.accepts("api", "Enables the API compatibility checking mode");
OptionSpec<Void> binaryO = parser.accepts("binary", "Enables the binary compatibility checking mode. This option will override the API compatibility flag. Defaults to true.");
OptionSpec<File> baseJarO = parser.accepts("base-jar", "Base JAR file that will be matched against for compatibility").withRequiredArg().ofType(File.class).required();
OptionSpec<File> inputJarO = parser.accepts("input-jar", "JAR file to validate against the base JAR").withRequiredArg().ofType(File.class).required();
OptionSpec<File> libO = parser.acceptsAll(ImmutableList.of("lib", "library"), "Libraries that the base JAR and input JAR both use").withRequiredArg().ofType(File.class);
OptionSpec<File> baseLibO = parser.acceptsAll(ImmutableList.of("base-lib", "base-library"), "Libraries that only the base JAR uses").withRequiredArg().ofType(File.class);
OptionSpec<File> concreteLibO = parser.acceptsAll(ImmutableList.of("concrete-lib", "concrete-library"), "Libraries that only the input JAR uses").withRequiredArg().ofType(File.class);
OptionSpec<File> inputLibO = parser.acceptsAll(ImmutableList.of("input-lib", "input-libary", "concrete-lib", "concrete-library"), "Libraries that only the input JAR uses").withRequiredArg().ofType(File.class);
OptionSpec<AnnotationCheckMode> annotationCheckModeO = parser.acceptsAll(ImmutableList.of("annotation-check-mode", "ann-mode"), "What mode to use for checking annotations")
.withRequiredArg().withValuesConvertedBy(new EnumConverter<AnnotationCheckMode>(AnnotationCheckMode.class) {});
OptionSpec<String> internalAnnotationO = parser.acceptsAll(ImmutableList.of("internal-annotation", "internal-ann"), "The fully resolved classname of an allowed internal API annotation")
Expand All @@ -51,15 +53,17 @@ public static void main(String[] args) {
File inputJar = options.valueOf(inputJarO);
List<File> commonLibs = options.valuesOf(libO);
List<File> baseLibs = options.valuesOf(baseLibO);
List<File> concreteLibs = options.valuesOf(concreteLibO);
List<File> concreteLibs = options.valuesOf(inputLibO);
boolean checkBinary = !options.has(apiO) || options.has(binaryO);
AnnotationCheckMode annotationCheckMode = options.valueOf(annotationCheckModeO);
List<String> internalAnnotations = options.valuesOf(internalAnnotationO);
InternalAnnotationCheckMode internalAnnotationCheckMode = options.valueOf(internalAnnotationCheckModeO);

Consumer<String> dbg = options.has(quietO) ? s -> {} : System.out::println;

// TODO allow logging to a file
JarCompatibilityChecker checker = new JarCompatibilityChecker(baseJar, inputJar, checkBinary, annotationCheckMode, internalAnnotations, internalAnnotationCheckMode,
commonLibs, baseLibs, concreteLibs, System.out::println, System.err::println);
commonLibs, baseLibs, concreteLibs, System.out::println, System.err::println, dbg);

int incompatibilities = checker.check();
// Clamp to a max of 125 to prevent conflicting with special meaning exit codes - https://tldp.org/LDP/abs/html/exitcodes.html
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import net.minecraftforge.jarcompatibilitychecker.core.Incompatibility;
import net.minecraftforge.jarcompatibilitychecker.core.InternalAnnotationCheckMode;
import net.minecraftforge.jarcompatibilitychecker.data.ClassInfo;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;

import java.io.File;
Expand All @@ -34,6 +35,7 @@ public class JarCompatibilityChecker {
private final List<File> concreteLibs;
private final Consumer<String> stdLogger;
private final Consumer<String> errLogger;
private final Consumer<String> dbgLogger;

/**
* Constructs a new JarCompatibilityChecker.
Expand Down Expand Up @@ -70,6 +72,14 @@ public JarCompatibilityChecker(File baseJar, File inputJar, boolean checkBinary,
*/
public JarCompatibilityChecker(File baseJar, File inputJar, boolean checkBinary, @Nullable AnnotationCheckMode annotationCheckMode, List<String> internalAnnotations,
InternalAnnotationCheckMode internalAnnotationCheckMode, List<File> commonLibs, List<File> baseLibs, List<File> concreteLibs, Consumer<String> stdLogger, Consumer<String> errLogger) {
this(baseJar, inputJar, checkBinary, annotationCheckMode, InternalAnnotationCheckMode.DEFAULT_INTERNAL_ANNOTATIONS, InternalAnnotationCheckMode.DEFAULT_MODE,
commonLibs, baseLibs, concreteLibs, stdLogger, errLogger, stdLogger);

}

@ApiStatus.Internal
JarCompatibilityChecker(File baseJar, File inputJar, boolean checkBinary, @Nullable AnnotationCheckMode annotationCheckMode, List<String> internalAnnotations,
InternalAnnotationCheckMode internalAnnotationCheckMode, List<File> commonLibs, List<File> baseLibs, List<File> concreteLibs, Consumer<String> stdLogger, Consumer<String> errLogger, Consumer<String> dbgLogger) {
this.baseJar = baseJar;
this.inputJar = inputJar;
this.checkBinary = checkBinary;
Expand All @@ -84,6 +94,7 @@ public JarCompatibilityChecker(File baseJar, File inputJar, boolean checkBinary,
this.concreteLibs = concreteLibs;
this.stdLogger = stdLogger;
this.errLogger = errLogger;
this.dbgLogger = dbgLogger;
}

private void log(String message) {
Expand All @@ -94,27 +105,31 @@ private void logError(String message) {
this.errLogger.accept(message);
}

private void logDebug(String message) {
this.dbgLogger.accept(message);
}

/**
* Loads the base jar and input jar and compares them for compatibility based on the current mode, API or binary.
* Any incompatibilities will be logged to the error logger.
*
* @return the number of incompatibilities detected based on the current mode
*/
public int check() throws IOException {
log("Compatibility mode: " + (this.checkBinary ? "Binary" : "API"));
log("Annotation check mode: " + (this.annotationCheckMode == null ? "NONE" : this.annotationCheckMode));
log("Internal API annotation check mode: " + this.internalAnnotationCheckMode);
log("Internal API annotations: " + this.internalAnnotations);
log("Base JAR: " + this.baseJar.getAbsolutePath());
log("Input JAR: " + this.inputJar.getAbsolutePath());
logDebug("Compatibility mode: " + (this.checkBinary ? "Binary" : "API"));
logDebug("Annotation check mode: " + (this.annotationCheckMode == null ? "NONE" : this.annotationCheckMode));
logDebug("Internal API annotation check mode: " + this.internalAnnotationCheckMode);
logDebug("Internal API annotations: " + this.internalAnnotations);
logDebug("Base JAR: " + this.baseJar.getAbsolutePath());
logDebug("Input JAR: " + this.inputJar.getAbsolutePath());
for (File baseLib : this.baseLibs) {
log("Base Library: " + baseLib.getAbsolutePath());
logDebug("Base Library: " + baseLib.getAbsolutePath());
}
for (File concreteLib : this.concreteLibs) {
log("Concrete Library: " + concreteLib.getAbsolutePath());
logDebug("Concrete Library: " + concreteLib.getAbsolutePath());
}
for (File commonLib : this.commonLibs) {
log("Common Library: " + commonLib.getAbsolutePath());
logDebug("Common Library: " + commonLib.getAbsolutePath());
}

List<File> baseFiles = new ArrayList<>(this.baseLibs);
Expand Down

0 comments on commit 19bd85b

Please sign in to comment.