Skip to content

Commit 35848c4

Browse files
committed
Fail build on missing or empty config
To enforce the usage of AccessTransformers when requested, the build will now fail if the requested AccessTransformers config file is not defined, cannot be found, or is empty. This validation will happen after project evaluation rather than at artifact transformation, because exceptions thrown by artifact transforms are consumed and don't fail the build. Also, added `AccessTransformerContainer#setLogLevel` to allow for viewing the output stream of AccessTransformers using a different log level.
1 parent cb5b18c commit 35848c4

File tree

9 files changed

+200
-78
lines changed

9 files changed

+200
-78
lines changed

.github/workflows/publish.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ on:
55
branches: [ 'master' ]
66
paths-ignore:
77
- 'at-gradle/**'
8+
- 'at-gradle-demo/**'
89
- '.github/workflows/**'
910
- 'docs/**'
1011
- 'README.md'

at-gradle-demo/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ plugins {
66
java.toolchain.languageVersion = JavaLanguageVersion.of(17)
77

88
accessTransformers.register {
9+
logLevel = LogLevel.LIFECYCLE
910
config = project.file('accesstransformer.cfg')
1011
}
1112

at-gradle-demo/settings.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ pluginManagement {
1111

1212
plugins {
1313
id 'org.gradle.toolchains.foojay-resolver-convention' version '1.0.0'
14-
id 'net.minecraftforge.gradleutils' version '3.2.5'
14+
id 'net.minecraftforge.gradleutils' version '3.2.6'
1515
}
1616

1717
rootProject.name = 'at-gradle-demo'

at-gradle/settings.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ plugins {
44
// NOTE: We need to load this into the classpath before GradleUtils for the service to load correctly
55
id 'io.freefair.javadoc-links' version '8.14' apply false // https://plugins.gradle.org/plugin/io.freefair.javadoc-links
66

7-
id 'net.minecraftforge.gradleutils' version '3.2.5' // https://plugins.gradle.org/plugin/net.minecraftforge.gradleutils
7+
id 'net.minecraftforge.gradleutils' version '3.2.6' // https://plugins.gradle.org/plugin/net.minecraftforge.gradleutils
88
}
99

1010
rootProject.name = 'at-gradle'
@@ -35,7 +35,7 @@ dependencyResolutionManagement {
3535
library 'gradle', 'name.remal.gradle-api', 'gradle-api' versionRef 'gradle'
3636

3737
// GradleUtils Shared Base
38-
library 'gradleutils-shared', 'net.minecraftforge', 'gradleutils-shared' version '3.2.5'
38+
library 'gradleutils-shared', 'net.minecraftforge', 'gradleutils-shared' version '3.2.6'
3939

4040
// Utils
4141
library 'utils-hash', 'net.minecraftforge', 'hash-utils' version '0.1.9'

at-gradle/src/main/java/net/minecraftforge/accesstransformers/gradle/AccessTransformersContainer.java

Lines changed: 44 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import org.gradle.api.attributes.Attribute;
1717
import org.gradle.api.file.FileCollection;
1818
import org.gradle.api.file.RegularFile;
19+
import org.gradle.api.logging.LogLevel;
1920
import org.gradle.api.provider.Provider;
2021
import org.gradle.api.provider.ProviderConvertible;
2122
import org.gradle.jvm.toolchain.JavaLauncher;
@@ -152,6 +153,49 @@ default Provider<?> dep(ProviderConvertible<?> dependencyNotation) {
152153
/// When initially registering an AccessTransformers container, the consumer must define key information regarding
153154
/// how AccessTransformers will be used. This interface is used to define that information.
154155
sealed interface Options permits AccessTransformersContainerInternal.Options {
156+
/// Sets the AccessTransformer configuration to use.
157+
///
158+
/// If the given provider does not provide a [file][File] or [regular file][RegularFile], the result will be
159+
/// resolved using [org.gradle.api.Project#file(Object)], similarly to [#setConfig(Object)].
160+
///
161+
/// @param configFile The configuration file to use
162+
void setConfig(Provider<?> configFile);
163+
164+
/// Sets the AccessTransformer configuration to use.
165+
///
166+
/// @param configFile The configuration file to use
167+
void setConfig(RegularFile configFile);
168+
169+
/// Sets the AccessTransformer configuration to use.
170+
///
171+
/// @param configFile The configuration file to use
172+
void setConfig(File configFile);
173+
174+
/// Sets the AccessTransformer configuration to use.
175+
///
176+
/// The given object is resolved using [org.gradle.api.Project#file(Object)].
177+
///
178+
/// @param configFile The configuration file to use
179+
void setConfig(Object configFile);
180+
181+
/// Sets the log level to pipe the output of AccessTransformers to.
182+
///
183+
/// @param level The log level to use
184+
/// @apiNote This is [experimental][ApiStatus.Experimental] due to the fact that AccessTransformers treats both
185+
/// [System#out] and [System#err] equally, while preferring to use the latter. This will be addressed in a
186+
/// future version of AccessTransformers.
187+
@ApiStatus.Experimental
188+
void setLogLevel(LogLevel level);
189+
190+
/// Sets the log level to pipe the output of AccessTransformers to.
191+
///
192+
/// @param level The log level to use
193+
/// @apiNote This is [experimental][ApiStatus.Experimental] due to the fact that AccessTransformers treats both
194+
/// [System#out] and [System#err] equally, while preferring to use the latter. This will be addressed in a
195+
/// future version of AccessTransformers.
196+
@ApiStatus.Experimental
197+
void setLogLevel(Provider<? extends LogLevel> level);
198+
155199
/// Sets the configuration to use as the classpath for AccessTransformers.
156200
///
157201
/// @param files The file collection to use
@@ -338,30 +382,5 @@ default void setClasspath(ProviderConvertible<?> dependencyNotation) {
338382
default void setArgs(String... args) {
339383
this.setArgs(Arrays.asList(args));
340384
}
341-
342-
/// Sets the AccessTransformer configuration to use.
343-
///
344-
/// If the given provider does not provide a [file][File] or [regular file][RegularFile], the result will be
345-
/// resolved using [org.gradle.api.Project#file(Object)], similarly to [#setConfig(Object)].
346-
///
347-
/// @param configFile The configuration file to use
348-
void setConfig(Provider<?> configFile);
349-
350-
/// Sets the AccessTransformer configuration to use.
351-
///
352-
/// @param configFile The configuration file to use
353-
void setConfig(RegularFile configFile);
354-
355-
/// Sets the AccessTransformer configuration to use.
356-
///
357-
/// @param configFile The configuration file to use
358-
void setConfig(File configFile);
359-
360-
/// Sets the AccessTransformer configuration to use.
361-
///
362-
/// The given object is resolved using [org.gradle.api.Project#file(Object)].
363-
///
364-
/// @param configFile The configuration file to use
365-
void setConfig(Object configFile);
366385
}
367386
}

at-gradle/src/main/java/net/minecraftforge/accesstransformers/gradle/AccessTransformersContainerImpl.java

Lines changed: 83 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import org.gradle.api.file.FileSystemLocation;
2626
import org.gradle.api.file.RegularFile;
2727
import org.gradle.api.file.RegularFileProperty;
28+
import org.gradle.api.logging.LogLevel;
2829
import org.gradle.api.model.ObjectFactory;
2930
import org.gradle.api.provider.ListProperty;
3031
import org.gradle.api.provider.Property;
@@ -34,6 +35,8 @@
3435

3536
import javax.inject.Inject;
3637
import java.io.File;
38+
import java.io.FileNotFoundException;
39+
import java.io.IOException;
3740
import java.io.Serial;
3841
import java.util.ArrayList;
3942
import java.util.Objects;
@@ -67,6 +70,8 @@ public AccessTransformersContainerImpl(
6770
}
6871

6972
private void finish(Project project) {
73+
this.validateATFile(this.attribute, this.options.config);
74+
7075
project.dependencies(Closures.<DependencyHandler>consumer(this, dependencies -> {
7176
dependencies.attributesSchema(attributesSchema -> {
7277
if (attributesSchema.hasAttribute(this.attribute))
@@ -82,11 +87,12 @@ private void finish(Project project) {
8287

8388
dependencies.registerTransform(ArtifactAccessTransformer.class, spec -> {
8489
spec.parameters(parameters -> {
90+
parameters.getConfig().set(this.options.config);
91+
parameters.getLogLevel().set(this.options.logLevel);
8592
parameters.getClasspath().setFrom(this.options.classpath);
8693
parameters.getMainClass().set(this.options.mainClass);
8794
parameters.getJavaLauncher().set(this.options.javaLauncher);
8895
parameters.getArgs().set(this.options.args);
89-
parameters.getConfig().set(this.options.config);
9096

9197
parameters.getCachesDir().convention(this.plugin.localCaches());
9298
});
@@ -97,6 +103,31 @@ private void finish(Project project) {
97103
}));
98104
}
99105

106+
private void validateATFile(Attribute<Boolean> attribute, RegularFileProperty atFileProperty) {
107+
// check that consumer has defined the config
108+
RegularFile atFileSource;
109+
try {
110+
atFileSource = atFileProperty.get();
111+
} catch (IllegalStateException e) {
112+
throw this.problems.accessTransformerConfigNotDefined(new RuntimeException("Failed to resolve config file property", e), attribute);
113+
}
114+
115+
// check that the file exists
116+
File atFile = atFileSource.getAsFile();
117+
if (!atFile.exists())
118+
throw this.problems.accessTransformerConfigMissing(new RuntimeException(new FileNotFoundException("Config file does not exist at " + atFile)), attribute, atFile);
119+
120+
// check that the file can be read and isn't empty
121+
String atFileContents;
122+
try {
123+
atFileContents = this.getProviders().fileContents(atFileSource).getAsText().get();
124+
} catch (Throwable e) {
125+
throw this.problems.accessTransformerConfigUnreadable(new RuntimeException(new IOException("Failed to read config file at " + atFile, e)), attribute, atFile);
126+
}
127+
if (atFileContents.isBlank())
128+
throw this.problems.accessTransformerConfigEmpty(new IllegalStateException("Config file must not be blank at " + atFile), attribute, atFile);
129+
}
130+
100131
private void setAttributes(AttributeContainer attributes, boolean value) {
101132
attributes.attribute(ArtifactTypeDefinition.ARTIFACT_TYPE_ATTRIBUTE, ArtifactTypeDefinition.JAR_TYPE)
102133
.attribute(this.attribute, value);
@@ -168,11 +199,12 @@ private static class MappedExternalModuleDependencyBundle extends ArrayList<Mini
168199
static abstract class OptionsImpl implements AccessTransformersContainerInternal.Options {
169200
private final Project project;
170201

202+
private final Property<LogLevel> logLevel = this.getObjects().property(LogLevel.class);
203+
private final RegularFileProperty config = this.getObjects().fileProperty();
171204
private FileCollection classpath;
172-
private final Property<String> mainClass;
173-
private final Property<String> javaLauncher;
174-
private final ListProperty<String> args;
175-
private final RegularFileProperty config;
205+
private final Property<String> mainClass = this.getObjects().property(String.class)/*.convention(Tools.ACCESSTRANSFORMERS.getMainClass())*/;
206+
private final Property<String> javaLauncher = this.getObjects().property(String.class);
207+
private final ListProperty<String> args = this.getObjects().listProperty(String.class);
176208

177209
protected abstract @Inject ObjectFactory getObjects();
178210
protected abstract @Inject ProviderFactory getProviders();
@@ -183,11 +215,53 @@ public OptionsImpl(Project project) {
183215

184216
var plugin = project.getPlugins().getPlugin(AccessTransformersPlugin.class);
185217

218+
this.logLevel.convention(LogLevel.INFO);
186219
this.classpath = this.getObjects().fileCollection().from(plugin.getTool(Tools.ACCESSTRANSFORMERS));
187-
this.mainClass = this.getObjects().property(String.class)/*.convention(Tools.ACCESSTRANSFORMERS.getMainClass())*/;
188-
this.javaLauncher = this.getObjects().property(String.class).convention(Util.launcherFor(project, Tools.ACCESSTRANSFORMERS.getJavaVersion()).map(Util.LAUNCHER_EXECUTABLE));
189-
this.args = this.getObjects().listProperty(String.class).convention(Constants.AT_DEFAULT_ARGS);
190-
this.config = this.getObjects().fileProperty();
220+
//this.mainClass.convention(Tools.ACCESSTRANSFORMERS.getMainClass());
221+
this.javaLauncher.convention(Util.launcherFor(project, Tools.ACCESSTRANSFORMERS.getJavaVersion()).map(Util.LAUNCHER_EXECUTABLE));
222+
this.args.convention(Constants.AT_DEFAULT_ARGS);
223+
}
224+
225+
@Override
226+
public void setLogLevel(Provider<? extends LogLevel> level) {
227+
this.logLevel.set(level);
228+
}
229+
230+
@Override
231+
public void setLogLevel(LogLevel level) {
232+
this.logLevel.set(level);
233+
}
234+
235+
@Override
236+
public void setConfig(Provider<?> configFile) {
237+
this.config.fileProvider(this.getProviders().provider(() -> {
238+
Object value = configFile.getOrNull();
239+
if (value == null)
240+
return null;
241+
else if (value instanceof FileSystemLocation)
242+
value = ((FileSystemLocation) value).getAsFile();
243+
244+
// if Project#file becomes inaccessible, use ProjectLayout#files
245+
return this.project.file(value, PathValidation.FILE);
246+
}));
247+
}
248+
249+
@Override
250+
public void setConfig(RegularFile configFile) {
251+
this.config.set(configFile);
252+
}
253+
254+
@Override
255+
public void setConfig(File configFile) {
256+
this.config.set(configFile);
257+
}
258+
259+
@Override
260+
public void setConfig(Object configFile) {
261+
this.config.fileProvider(this.getProviders().provider(
262+
// if Project#file becomes inaccessible, use ProjectLayout#files
263+
() -> this.project.file(configFile, PathValidation.FILE)
264+
));
191265
}
192266

193267
@Override
@@ -237,37 +311,5 @@ public void setArgs(Iterable<String> args) {
237311
public void setArgs(Provider<? extends Iterable<String>> args) {
238312
this.args.set(args);
239313
}
240-
241-
@Override
242-
public void setConfig(Provider<?> configFile) {
243-
this.config.fileProvider(this.getProviders().provider(() -> {
244-
Object value = configFile.getOrNull();
245-
if (value == null)
246-
return null;
247-
else if (value instanceof FileSystemLocation)
248-
value = ((FileSystemLocation) value).getAsFile();
249-
250-
// if Project#file becomes inaccessible, use ProjectLayout#files
251-
return this.project.file(value, PathValidation.FILE);
252-
}));
253-
}
254-
255-
@Override
256-
public void setConfig(RegularFile configFile) {
257-
this.config.set(configFile);
258-
}
259-
260-
@Override
261-
public void setConfig(File configFile) {
262-
this.config.set(configFile);
263-
}
264-
265-
@Override
266-
public void setConfig(Object configFile) {
267-
this.config.fileProvider(this.getProviders().provider(
268-
// if Project#file becomes inaccessible, use ProjectLayout#files
269-
() -> this.project.file(configFile, PathValidation.FILE)
270-
));
271-
}
272314
}
273315
}

at-gradle/src/main/java/net/minecraftforge/accesstransformers/gradle/AccessTransformersPlugin.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,6 @@ abstract class AccessTransformersPlugin extends EnhancedPlugin<Project> {
2121
static final String NAME = "accesstransformers";
2222
static final String DISPLAY_NAME = "AccessTransformers Gradle";
2323

24-
static final Logger LOGGER = Logging.getLogger(AccessTransformersPlugin.class);
25-
2624
@Inject
2725
public AccessTransformersPlugin() {
2826
super(NAME, DISPLAY_NAME);

at-gradle/src/main/java/net/minecraftforge/accesstransformers/gradle/AccessTransformersProblems.java

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
import net.minecraftforge.gradleutils.shared.EnhancedProblems;
88
import org.gradle.api.artifacts.Dependency;
9+
import org.gradle.api.attributes.Attribute;
910
import org.gradle.api.problems.Severity;
1011

1112
import javax.inject.Inject;
@@ -35,6 +36,53 @@ void reportIllegalTargetDependency(Dependency dependency) {
3536
.solution(HELP_MESSAGE));
3637
}
3738

39+
RuntimeException accessTransformerConfigNotDefined(Exception e, Attribute<Boolean> attribute) {
40+
return this.getReporter().throwing(e, this.id("access-transformer-config-not-defined", "Access transformer config not defined"), spec -> spec
41+
.details("""
42+
The access transformer cannot transform the input artifact without a configuration file.
43+
Attribute: %s""".formatted(attribute))
44+
.severity(Severity.ERROR)
45+
.solution("Ensure you have defined your configuration file.")
46+
.solution("Do not use AccessTransformers if you do not need to.")
47+
.solution(HELP_MESSAGE));
48+
}
49+
50+
RuntimeException accessTransformerConfigMissing(Exception e, Attribute<Boolean> attribute, File atFile) {
51+
return this.getReporter().throwing(e, this.id("access-transformer-config-missing", "Access transformer config file not found"), spec -> spec
52+
.details("""
53+
The access transformer cannot transform the input artifact because the configuration file could not be found.
54+
Attribute: %s
55+
AccessTransformer Config: %s""".formatted(attribute, atFile))
56+
.severity(Severity.ERROR)
57+
.solution("Ensure your AccessTransformers configuration file exists.")
58+
.solution("Do not use AccessTransformers if you do not need to.")
59+
.solution(HELP_MESSAGE));
60+
}
61+
62+
RuntimeException accessTransformerConfigUnreadable(Throwable e, Attribute<Boolean> attribute, File atFile) {
63+
return this.getReporter().throwing(e, this.id("access-transformer-config-unreadable", "Access transformer config file not read"), spec -> spec
64+
.details("""
65+
The access transformer cannot transform the input artifact because the configuration file could not be read.
66+
This may be due to insufficient file permissions or a corrupted file.
67+
Attribute: %s
68+
AccessTransformer Config: %s""".formatted(attribute, atFile))
69+
.severity(Severity.ERROR)
70+
.solution("Ensure you (and/or Gradle) have proper read/write permissions to the AccessTransformer config file.")
71+
.solution(HELP_MESSAGE));
72+
}
73+
74+
RuntimeException accessTransformerConfigEmpty(Exception e, Attribute<Boolean> attribute, File atFile) {
75+
return this.getReporter().throwing(e, this.id("access-transformer-config-empty", "Access transformer config missing or empty"), spec -> spec
76+
.details("""
77+
The access transformer cannot transform the input artifact because the configuration file is empty or blank.
78+
Attribute: %s
79+
AccessTransformer Config: %s""".formatted(attribute, atFile))
80+
.severity(Severity.ERROR)
81+
.solution("Ensure your AccessTransformers configuration file contains definitions to be used.")
82+
.solution("Do not use AccessTransformers if you do not need to.")
83+
.solution(HELP_MESSAGE));
84+
}
85+
3886
void reportAccessTransformerCannotValidateOutput(Exception e, File inJar, File atFile, File outJar, File logFile) {
3987
this.getLogger().warn("WARNING: Access transformer completed, but failed to validate the output. Output jar: {}", outJar.getAbsolutePath());
4088
this.getReporter().report(this.id("access-transformer-output-validation-failed", "Failed to validate the access transformed output"), spec -> spec

0 commit comments

Comments
 (0)