diff --git a/src/main/java/net/fabricmc/loom/configuration/processors/SpecContextImpl.java b/src/main/java/net/fabricmc/loom/configuration/processors/SpecContextImpl.java index 996fd8d2..1ad69b72 100644 --- a/src/main/java/net/fabricmc/loom/configuration/processors/SpecContextImpl.java +++ b/src/main/java/net/fabricmc/loom/configuration/processors/SpecContextImpl.java @@ -38,24 +38,31 @@ import java.util.function.Function; import java.util.stream.Stream; import org.gradle.api.Project; +import org.gradle.api.artifacts.Configuration; +import org.gradle.api.artifacts.ProjectDependency; +import org.gradle.api.plugins.JavaPlugin; import org.gradle.api.tasks.SourceSet; import net.fabricmc.loom.LoomGradleExtension; import net.fabricmc.loom.api.RemapConfigurationSettings; import net.fabricmc.loom.api.processor.SpecContext; +import net.fabricmc.loom.util.Constants; import net.fabricmc.loom.util.fmj.FabricModJson; import net.fabricmc.loom.util.fmj.FabricModJsonFactory; +import net.fabricmc.loom.util.gradle.GradleUtils; import net.fabricmc.loom.util.gradle.SourceSetHelper; /** * @param modDependencies External mods that are depended on - * @param localMods The main mod being built. In the future this may also include other mods. + * @param localMods Mods found in the current project. + * @param compileRuntimeMods Dependent mods found in both the compile and runtime classpath. */ public record SpecContextImpl(List modDependencies, List localMods, List compileRuntimeMods) implements SpecContext { public static SpecContextImpl create(Project project) { - return new SpecContextImpl(getDependentMods(project), getMods(project), getCompileRuntimeMods(project)); + return new SpecContextImpl(getDependentMods(project), getModsInProject(project), getCompileRuntimeMods(project)); } + // Reruns a list of mods found on both the compile and/or runtime classpaths private static List getDependentMods(Project project) { final LoomGradleExtension extension = LoomGradleExtension.get(project); var mods = new ArrayList(); @@ -72,12 +79,26 @@ public record SpecContextImpl(List modDependencies, List getMods(Project project) { + private static Stream getDependentProjects(Project project) { + final Stream runtimeProjects = getLoomProjectDependencies(project.getConfigurations().getByName(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME)); + final Stream compileProjects = getLoomProjectDependencies(project.getConfigurations().getByName(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME)); + + return Stream.concat(runtimeProjects, compileProjects) + .distinct(); + } + + // Returns a list of Mods found in the provided project + private static List getModsInProject(Project project) { final LoomGradleExtension extension = LoomGradleExtension.get(project); var sourceSets = new ArrayList(); sourceSets.add(SourceSetHelper.getMainSourceSet(project)); @@ -99,7 +120,19 @@ public record SpecContextImpl(List modDependencies, List getCompileRuntimeMods(Project project) { + var mods = new ArrayList<>(getCompileRuntimeModsFromRemapConfigs(project).toList()); + + for (Project dependentProject : getCompileRuntimeProjectDependencies(project).toList()) { + mods.addAll(getModsInProject(dependentProject)); + } + + return Collections.unmodifiableList(mods); + } + + // Returns a list of jar mods that are found on the compile and runtime remapping configurations + private static Stream getCompileRuntimeModsFromRemapConfigs(Project project) { final LoomGradleExtension extension = LoomGradleExtension.get(project); final Function> resolve = settings -> settings.getSourceConfiguration().get().resolve().stream() @@ -115,8 +148,25 @@ public record SpecContextImpl(List modDependencies, List getCompileRuntimeProjectDependencies(Project project) { + final Stream runtimeProjects = getLoomProjectDependencies(project.getConfigurations().getByName(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME)); + final List compileProjects = getLoomProjectDependencies(project.getConfigurations().getByName(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME)).toList(); + + return runtimeProjects + .filter(compileProjects::contains); // Use the intersection of the two configurations. + } + + // Returns a list of Loom Projects found in the provided Configuration + private static Stream getLoomProjectDependencies(Configuration configuration) { + return configuration.getAllDependencies() + .withType(ProjectDependency.class) + .stream() + .map(ProjectDependency::getDependencyProject) + .filter(GradleUtils::isLoomProject); } // Sort to ensure stable caching diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftSourceSets.java b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftSourceSets.java index 1b32fed0..34f1d1eb 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftSourceSets.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftSourceSets.java @@ -220,15 +220,15 @@ public abstract sealed class MinecraftSourceSets permits MinecraftSourceSets.Sin // The client only sources to the combined sources jar. jar.from(clientOnlySourceSet.getAllSource()); }); + + project.getTasks().withType(AbstractRemapJarTask.class, task -> { + // Set the default client only source set name + task.getClientOnlySourceSetName().convention(CLIENT_ONLY_SOURCE_SET_NAME); + }); } @Override public void afterEvaluate(Project project) { } - - public static SourceSet getClientSourceSet(Project project) { - Preconditions.checkArgument(LoomGradleExtension.get(project).areEnvironmentSourceSetsSplit(), "Cannot get client only sourceset as project is not split"); - return SourceSetHelper.getSourceSetByName(CLIENT_ONLY_SOURCE_SET_NAME, project); - } } } diff --git a/src/main/java/net/fabricmc/loom/task/AbstractRemapJarTask.java b/src/main/java/net/fabricmc/loom/task/AbstractRemapJarTask.java index cb468e59..7ea67b47 100644 --- a/src/main/java/net/fabricmc/loom/task/AbstractRemapJarTask.java +++ b/src/main/java/net/fabricmc/loom/task/AbstractRemapJarTask.java @@ -51,6 +51,8 @@ import org.gradle.api.tasks.Input; import org.gradle.api.tasks.InputFile; import org.gradle.api.tasks.InputFiles; import org.gradle.api.tasks.Internal; +import org.gradle.api.tasks.Optional; +import org.gradle.api.tasks.SourceSet; import org.gradle.build.event.BuildEventsListenerRegistry; import org.gradle.jvm.tasks.Jar; import org.gradle.workers.WorkAction; @@ -65,6 +67,7 @@ import net.fabricmc.loom.build.IntermediaryNamespaces; import net.fabricmc.loom.task.service.JarManifestService; import net.fabricmc.loom.util.ZipReprocessorUtil; import net.fabricmc.loom.util.ZipUtils; +import net.fabricmc.loom.util.gradle.SourceSetHelper; public abstract class AbstractRemapJarTask extends Jar { public static final String MANIFEST_PATH = "META-INF/MANIFEST.MF"; @@ -104,6 +107,10 @@ public abstract class AbstractRemapJarTask extends Jar { @Input public abstract ListProperty getAdditionalClientOnlyEntries(); + @Input + @Optional + public abstract Property getClientOnlySourceSetName(); + private final Provider jarManifestServiceProvider; @Inject @@ -133,7 +140,7 @@ public abstract class AbstractRemapJarTask extends Jar { params.getJarManifestService().set(jarManifestServiceProvider); if (getIncludesClientOnlyClasses().get()) { - final List clientOnlyEntries = new ArrayList<>(getClientOnlyEntries()); + final List clientOnlyEntries = new ArrayList<>(getClientOnlyEntries(getClientSourceSet())); clientOnlyEntries.addAll(getAdditionalClientOnlyEntries().get()); applyClientOnlyManifestAttributes(params, clientOnlyEntries); params.getClientOnlyEntries().set(clientOnlyEntries.stream().filter(s -> s.endsWith(".class")).toList()); @@ -143,8 +150,7 @@ public abstract class AbstractRemapJarTask extends Jar { }); } - @Internal - protected abstract List getClientOnlyEntries(); + protected abstract List getClientOnlyEntries(SourceSet sourceSet); public interface AbstractRemapParams extends WorkParameters { RegularFileProperty getInputFile(); @@ -242,4 +248,9 @@ public abstract class AbstractRemapJarTask extends Jar { protected LoomGradleExtension getLoomExtension() { return LoomGradleExtension.get(getProject()); } + + private SourceSet getClientSourceSet() { + Preconditions.checkArgument(LoomGradleExtension.get(getProject()).areEnvironmentSourceSetsSplit(), "Cannot get client sourceset as project is not split"); + return SourceSetHelper.getSourceSetByName(getClientOnlySourceSetName().get(), getProject()); + } } diff --git a/src/main/java/net/fabricmc/loom/task/GenVsCodeProjectTask.java b/src/main/java/net/fabricmc/loom/task/GenVsCodeProjectTask.java index b5951afe..eae5bbcf 100644 --- a/src/main/java/net/fabricmc/loom/task/GenVsCodeProjectTask.java +++ b/src/main/java/net/fabricmc/loom/task/GenVsCodeProjectTask.java @@ -195,7 +195,7 @@ public class GenVsCodeProjectTask extends AbstractLoomTask { public String name; public String request = "launch"; public String cwd; - public String console = "internalConsole"; + public String console = "integratedTerminal"; public boolean stopOnEntry = false; public String mainClass; public String vmArgs; diff --git a/src/main/java/net/fabricmc/loom/task/RemapJarTask.java b/src/main/java/net/fabricmc/loom/task/RemapJarTask.java index 87cecf8e..396b14bc 100644 --- a/src/main/java/net/fabricmc/loom/task/RemapJarTask.java +++ b/src/main/java/net/fabricmc/loom/task/RemapJarTask.java @@ -76,7 +76,6 @@ import net.fabricmc.loom.build.nesting.IncludedJarFactory.LazyNestedFile; import net.fabricmc.loom.build.nesting.IncludedJarFactory.NestedFile; import net.fabricmc.loom.build.nesting.JarNester; import net.fabricmc.loom.configuration.accesswidener.AccessWidenerFile; -import net.fabricmc.loom.configuration.providers.minecraft.MinecraftSourceSets; import net.fabricmc.loom.extension.MixinExtension; import net.fabricmc.loom.task.service.MappingsService; import net.fabricmc.loom.task.service.TinyRemapperService; @@ -453,9 +452,7 @@ public abstract class RemapJarTask extends AbstractRemapJarTask { } @Override - protected List getClientOnlyEntries() { - final SourceSet clientSourceSet = MinecraftSourceSets.Split.getClientSourceSet(getProject()); - + protected List getClientOnlyEntries(SourceSet clientSourceSet) { final ConfigurableFileCollection output = getProject().getObjects().fileCollection(); output.from(clientSourceSet.getOutput().getClassesDirs()); output.from(clientSourceSet.getOutput().getResourcesDir()); diff --git a/src/main/java/net/fabricmc/loom/task/RemapSourcesJarTask.java b/src/main/java/net/fabricmc/loom/task/RemapSourcesJarTask.java index 67319696..cca9638e 100644 --- a/src/main/java/net/fabricmc/loom/task/RemapSourcesJarTask.java +++ b/src/main/java/net/fabricmc/loom/task/RemapSourcesJarTask.java @@ -38,7 +38,6 @@ import org.gradle.api.tasks.TaskAction; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import net.fabricmc.loom.configuration.providers.minecraft.MinecraftSourceSets; import net.fabricmc.loom.task.service.SourceRemapperService; import net.fabricmc.loom.util.service.BuildSharedServiceManager; import net.fabricmc.loom.util.service.UnsafeWorkQueueHelper; @@ -62,9 +61,7 @@ public abstract class RemapSourcesJarTask extends AbstractRemapJarTask { } @Override - protected List getClientOnlyEntries() { - final SourceSet clientSourceSet = MinecraftSourceSets.Split.getClientSourceSet(getProject()); - + protected List getClientOnlyEntries(SourceSet clientSourceSet) { return clientSourceSet.getAllSource().getFiles().stream() .map(relativePath(getRootPaths(clientSourceSet.getAllSource().getSrcDirs()))) .toList(); diff --git a/src/main/java/net/fabricmc/loom/util/Constants.java b/src/main/java/net/fabricmc/loom/util/Constants.java index 200a35e7..613c73ac 100644 --- a/src/main/java/net/fabricmc/loom/util/Constants.java +++ b/src/main/java/net/fabricmc/loom/util/Constants.java @@ -177,6 +177,7 @@ public class Constants { public static final String MULTI_PROJECT_OPTIMISATION = "fabric.loom.multiProjectOptimisation"; public static final String DONT_REMAP = "fabric.loom.dontRemap"; public static final String DISABLE_REMAPPED_VARIANTS = "fabric.loom.disableRemappedVariants"; + public static final String DISABLE_PROJECT_DEPENDENT_MODS = "fabric.loom.disableProjectDependentMods"; } public static final class Forge { diff --git a/src/main/java/net/fabricmc/loom/util/download/DownloadBuilder.java b/src/main/java/net/fabricmc/loom/util/download/DownloadBuilder.java index 15242936..0d3949a2 100644 --- a/src/main/java/net/fabricmc/loom/util/download/DownloadBuilder.java +++ b/src/main/java/net/fabricmc/loom/util/download/DownloadBuilder.java @@ -45,6 +45,7 @@ public class DownloadBuilder { private Duration maxAge = Duration.ZERO; private DownloadProgressListener progressListener = DownloadProgressListener.NONE; private int maxRetries = 3; + private boolean allowInsecureProtocol = false; private DownloadBuilder(URI url) { this.url = url; @@ -94,7 +95,16 @@ public class DownloadBuilder { return maxAge(ONE_DAY); } + public DownloadBuilder allowInsecureProtocol() { + this.allowInsecureProtocol = true; + return this; + } + private Download build() { + if (!allowInsecureProtocol && !isSecureUrl(url)) { + throw new IllegalArgumentException("Cannot create download for url (%s) with insecure protocol".formatted(url.toString())); + } + return new Download(this.url, this.expectedHash, this.useEtag, this.forceDownload, this.offline, maxAge, progressListener); } @@ -145,6 +155,16 @@ public class DownloadBuilder { throw new IllegalStateException(); } + // See comment on org.gradle.util.internal.GUtil.isSecureUrl + private static boolean isSecureUrl(URI url) { + if ("127.0.0.1".equals(url.getHost())) { + return true; + } + + final String scheme = url.getScheme(); + return !"http".equalsIgnoreCase(scheme); + } + @FunctionalInterface private interface DownloadSupplier { T get() throws DownloadException; diff --git a/src/main/java/net/fabricmc/loom/util/gradle/GradleUtils.java b/src/main/java/net/fabricmc/loom/util/gradle/GradleUtils.java index 6361d235..7fd5d159 100644 --- a/src/main/java/net/fabricmc/loom/util/gradle/GradleUtils.java +++ b/src/main/java/net/fabricmc/loom/util/gradle/GradleUtils.java @@ -48,12 +48,16 @@ public final class GradleUtils { public static void allLoomProjects(Gradle gradle, Consumer consumer) { gradle.allprojects(project -> { - if (project.getPluginManager().hasPlugin("fabric-loom")) { + if (isLoomProject(project)) { consumer.accept(project); } }); } + public static boolean isLoomProject(Project project) { + return project.getPluginManager().hasPlugin("fabric-loom"); + } + public static Provider getBooleanPropertyProvider(Project project, String key) { return project.getProviders().gradleProperty(key).map(string -> { try { diff --git a/src/test/groovy/net/fabricmc/loom/test/LoomTestConstants.groovy b/src/test/groovy/net/fabricmc/loom/test/LoomTestConstants.groovy index c05aed3b..4e004e03 100644 --- a/src/test/groovy/net/fabricmc/loom/test/LoomTestConstants.groovy +++ b/src/test/groovy/net/fabricmc/loom/test/LoomTestConstants.groovy @@ -27,7 +27,7 @@ package net.fabricmc.loom.test import org.gradle.util.GradleVersion class LoomTestConstants { - private final static String NIGHTLY_VERSION = "8.1-20221210231949+0000" + private final static String NIGHTLY_VERSION = "8.1-20221229234322+0000" private final static boolean NIGHTLY_EXISTS = nightlyExists(NIGHTLY_VERSION) public final static String DEFAULT_GRADLE = GradleVersion.current().getVersion() diff --git a/src/test/groovy/net/fabricmc/loom/test/unit/download/DownloadFileTest.groovy b/src/test/groovy/net/fabricmc/loom/test/unit/download/DownloadFileTest.groovy index 6d7a408b..e3ee8d08 100644 --- a/src/test/groovy/net/fabricmc/loom/test/unit/download/DownloadFileTest.groovy +++ b/src/test/groovy/net/fabricmc/loom/test/unit/download/DownloadFileTest.groovy @@ -339,6 +339,15 @@ class DownloadFileTest extends DownloadTest { Files.readAllBytes(output) == data } + def "File: Insecure protocol"() { + setup: + def output = new File(File.createTempDir(), "file").toPath() + when: + def result = Download.create("http://fabricmc.net").downloadPath(output) + then: + thrown IllegalArgumentException + } + // Known def "Download Mojang Mappings"() { setup: diff --git a/src/test/groovy/net/fabricmc/loom/test/unit/download/DownloadStringTest.groovy b/src/test/groovy/net/fabricmc/loom/test/unit/download/DownloadStringTest.groovy index f28b20b2..319bff8f 100644 --- a/src/test/groovy/net/fabricmc/loom/test/unit/download/DownloadStringTest.groovy +++ b/src/test/groovy/net/fabricmc/loom/test/unit/download/DownloadStringTest.groovy @@ -109,4 +109,11 @@ class DownloadStringTest extends DownloadTest { then: result == "Hello World!" } + + def "String: Insecure protocol"() { + when: + def result = Download.create("http://fabricmc.net").downloadString() + then: + thrown IllegalArgumentException + } } diff --git a/src/test/groovy/net/fabricmc/loom/test/unit/download/DownloadTest.groovy b/src/test/groovy/net/fabricmc/loom/test/unit/download/DownloadTest.groovy index 36074642..b4e3981b 100644 --- a/src/test/groovy/net/fabricmc/loom/test/unit/download/DownloadTest.groovy +++ b/src/test/groovy/net/fabricmc/loom/test/unit/download/DownloadTest.groovy @@ -29,7 +29,7 @@ import spock.lang.Shared import spock.lang.Specification abstract class DownloadTest extends Specification { - static final String PATH = "http://localhost:9081" + static final String PATH = "http://127.0.0.1:9081" @Shared Javalin server = Javalin.create { config -> diff --git a/src/test/resources/projects/multiproject/core/src/main/java/net/fabricmc/core/InjectedInterface.java b/src/test/resources/projects/multiproject/core/src/main/java/net/fabricmc/core/InjectedInterface.java new file mode 100644 index 00000000..be1b0003 --- /dev/null +++ b/src/test/resources/projects/multiproject/core/src/main/java/net/fabricmc/core/InjectedInterface.java @@ -0,0 +1,6 @@ +package net.fabricmc.core; + +public interface InjectedInterface { + default void newMethodThatDidNotExist() { + } +} diff --git a/src/test/resources/projects/multiproject/core/src/main/resources/fabric.mod.json b/src/test/resources/projects/multiproject/core/src/main/resources/fabric.mod.json index 66e9ea9e..bcd09423 100644 --- a/src/test/resources/projects/multiproject/core/src/main/resources/fabric.mod.json +++ b/src/test/resources/projects/multiproject/core/src/main/resources/fabric.mod.json @@ -29,5 +29,10 @@ }, "suggests": { "another-mod": "*" + }, + "custom": { + "loom:injected_interfaces": { + "net/minecraft/class_2248": ["net/fabricmc/core/InjectedInterface"] + } } } diff --git a/src/test/resources/projects/multiproject/example/src/main/java/net/fabricmc/example/ExampleMod.java b/src/test/resources/projects/multiproject/example/src/main/java/net/fabricmc/example/ExampleMod.java index 2afebd00..6a4f50a9 100644 --- a/src/test/resources/projects/multiproject/example/src/main/java/net/fabricmc/example/ExampleMod.java +++ b/src/test/resources/projects/multiproject/example/src/main/java/net/fabricmc/example/ExampleMod.java @@ -4,6 +4,7 @@ import net.fabricmc.api.ModInitializer; import net.minecraft.block.BlockState; import techreborn.blocks.cable.CableShapeUtil; import net.minecraft.util.shape.VoxelShape; +import net.minecraft.block.Blocks; public class ExampleMod implements ModInitializer { @Override @@ -18,6 +19,9 @@ public class ExampleMod implements ModInitializer { // Just here to make sure it compiles as named, not to test it runs BlockState state = null; VoxelShape shape = new CableShapeUtil(null).getShape(state); + + // Interface is injected by another project that we are depending on. + Blocks.AIR.newMethodThatDidNotExist(); } } }