Support ARM natives, rewrite native handling and misc cleanup. (#554)

* Rewrite natives handling, upgrade LWJGL on ARM machines.

* Remove old natives override hack, should now always be done via gradle.

* Use "idea" everywhere

* Add server specific libraries onto their own classpath. Includes misc code cleanup.

* Start on writing the excluded server libs.

* Gradle deprecation fixes, and misc cleanup

* Add support mod.

* Make native support mod none transitive.

* Update gradle.
dev/0.11
modmuss50 2021-12-17 15:37:00 +00:00 committed by GitHub
parent 4ace257c37
commit 5a16440c1e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
53 changed files with 905 additions and 470 deletions

View File

@ -13,7 +13,7 @@ jobs:
steps:
- uses: actions/checkout@v2
- uses: gradle/wrapper-validation-action@v1
- run: gradle build check -x test --stacktrace
- run: gradle build check -x test --stacktrace --warning-mode fail
# This job is used to feed the test matrix of next job to allow the tests to run in parallel
prepare_test_matrix:
@ -27,7 +27,7 @@ jobs:
steps:
- uses: actions/checkout@v2
- run: gradle writeActionsTestMatrix --stacktrace
- run: gradle writeActionsTestMatrix --stacktrace --warning-mode fail
-
id: set-matrix
run: echo "::set-output name=matrix::$(cat build/test_matrix.json)"
@ -51,7 +51,7 @@ jobs:
steps:
- uses: actions/checkout@v2
- run: gradle test --tests ${{ matrix.test }} --stacktrace
- run: gradle test --tests ${{ matrix.test }} --stacktrace --warning-mode fail
env:
TEST_WARNING_MODE: fail
@ -78,7 +78,7 @@ jobs:
uses: actions/setup-java@v1
with:
java-version: ${{ matrix.java }}
- run: ./gradlew test --tests ${{ matrix.test }} --stacktrace
- run: ./gradlew test --tests ${{ matrix.test }} --stacktrace --warning-mode fail
env:
TEST_WARNING_MODE: fail
@ -105,7 +105,7 @@ jobs:
with:
java-version: ${{ matrix.java }}
- run: ./gradlew test --tests *ReproducibleBuildTest --stacktrace
- run: ./gradlew test --tests *ReproducibleBuildTest --stacktrace --warning-mode fail
- uses: actions/upload-artifact@v2
if: ${{ failure() }}

View File

@ -37,6 +37,9 @@ repositories {
url = 'https://maven.fabricmc.net/'
}
mavenCentral()
maven {
url = 'https://plugins.gradle.org/m2/'
}
mavenLocal()
}
@ -92,6 +95,9 @@ dependencies {
// source code remapping
implementation ('net.fabricmc:mercury:0.2.4')
// IDEA support
implementation ('gradle.plugin.org.jetbrains.gradle.plugin.idea-ext:gradle-idea-ext:1.1.1')
// Kapt integration
compileOnly('org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.0')
@ -162,8 +168,8 @@ jacoco {
jacocoTestReport {
dependsOn test
reports {
xml.enabled false
csv.enabled false
xml.required = false
csv.required = false
html.destination file("${buildDir}/jacocoHtml")
}
}

View File

@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.1-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.2-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

View File

@ -31,6 +31,7 @@ import java.util.function.Supplier;
import org.cadixdev.lorenz.MappingSet;
import org.cadixdev.mercury.Mercury;
import org.gradle.api.Action;
import org.gradle.api.NamedDomainObjectProvider;
import org.gradle.api.Project;
import org.gradle.api.artifacts.Configuration;
@ -54,7 +55,12 @@ public interface LoomGradleExtension extends LoomGradleExtensionAPI {
LoomFiles getFiles();
NamedDomainObjectProvider<Configuration> createLazyConfiguration(String name);
default NamedDomainObjectProvider<Configuration> createLazyConfiguration(String name) {
return createLazyConfiguration(name, config -> {
});
}
NamedDomainObjectProvider<Configuration> createLazyConfiguration(String name, Action<? super Configuration> configurationAction);
NamedDomainObjectProvider<Configuration> getLazyConfigurationProvider(String name);
@ -94,10 +100,6 @@ public interface LoomGradleExtension extends LoomGradleExtensionAPI {
boolean isRootProject();
default boolean ideSync() {
return Boolean.parseBoolean(System.getProperty("idea.sync.active", "false"));
}
default String getIntermediaryUrl(String minecraftVersion) {
return String.format(this.getIntermediaryUrl().get(), minecraftVersion);
}

View File

@ -40,6 +40,7 @@ import net.fabricmc.loom.configuration.CompileConfiguration;
import net.fabricmc.loom.configuration.FabricApiExtension;
import net.fabricmc.loom.configuration.MavenPublication;
import net.fabricmc.loom.configuration.ide.IdeConfiguration;
import net.fabricmc.loom.configuration.ide.idea.IdeaConfiguration;
import net.fabricmc.loom.decompilers.DecompilerConfiguration;
import net.fabricmc.loom.extension.LoomFiles;
import net.fabricmc.loom.extension.LoomGradleExtensionImpl;
@ -84,5 +85,6 @@ public class LoomGradlePlugin implements BootstrappedPlugin {
MavenPublication.configure(project);
LoomTasks.registerTasks(project);
DecompilerConfiguration.setup(project);
IdeaConfiguration.setup(project);
}
}

View File

@ -27,9 +27,10 @@ package net.fabricmc.loom.api.mappings.layered.spec;
import groovy.lang.Closure;
import groovy.lang.DelegatesTo;
import org.gradle.api.Action;
import org.gradle.util.ConfigureUtil;
import org.jetbrains.annotations.ApiStatus;
import net.fabricmc.loom.util.ClosureAction;
/**
* Used to configure a layered mapping spec.
*/
@ -52,7 +53,7 @@ public interface LayeredMappingSpecBuilder {
*/
@SuppressWarnings("rawtypes")
default LayeredMappingSpecBuilder officialMojangMappings(@DelegatesTo(value = MojangMappingsSpecBuilder.class, strategy = Closure.DELEGATE_FIRST) Closure closure) {
return officialMojangMappings(mojangMappingsSpecBuilder -> ConfigureUtil.configure(closure, mojangMappingsSpecBuilder));
return officialMojangMappings(new ClosureAction<>(closure));
}
/**
@ -66,7 +67,7 @@ public interface LayeredMappingSpecBuilder {
@SuppressWarnings("rawtypes")
default LayeredMappingSpecBuilder parchment(Object object, @DelegatesTo(value = ParchmentMappingsSpecBuilder.class, strategy = Closure.DELEGATE_FIRST) Closure closure) {
return parchment(object, parchmentMappingsSpecBuilder -> ConfigureUtil.configure(closure, parchmentMappingsSpecBuilder));
return parchment(object, new ClosureAction<>(closure));
}
LayeredMappingSpecBuilder parchment(Object object, Action<ParchmentMappingsSpecBuilder> action);

View File

@ -99,7 +99,7 @@ public class JarRemapper {
outputConsumer.addNonClassFiles(data.input);
data.processAccessWidener(remapper.getRemapper());
data.processAccessWidener(remapper.getEnvironment().getRemapper());
remapper.apply(outputConsumer, data.tag);
}

View File

@ -29,7 +29,6 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Supplier;
import java.util.zip.ZipFile;
import com.google.common.io.Files;
import org.gradle.api.Project;
@ -58,6 +57,7 @@ import net.fabricmc.loom.configuration.processors.dependency.ModDependencyInfo;
import net.fabricmc.loom.configuration.processors.dependency.RemapData;
import net.fabricmc.loom.util.Checksum;
import net.fabricmc.loom.util.Constants;
import net.fabricmc.loom.util.ModUtils;
import net.fabricmc.loom.util.OperatingSystem;
import net.fabricmc.loom.util.SourceRemapper;
@ -91,7 +91,7 @@ public class ModCompileRemapper {
String name = artifact.getModuleVersion().getId().getName();
String version = replaceIfNullOrEmpty(artifact.getModuleVersion().getId().getVersion(), () -> Checksum.truncatedSha256(artifact.getFile()));
if (!isFabricMod(logger, artifact.getFile(), artifact.getId())) {
if (!ModUtils.isMod(artifact.getFile())) {
addToRegularCompile(project, regularConfig, artifact);
continue;
}
@ -118,7 +118,7 @@ public class ModCompileRemapper {
// Create a mod dependency for each file in the file collection
for (File artifact : files) {
if (!isFabricMod(logger, artifact, artifact.getName())) {
if (!ModUtils.isMod(artifact)) {
dependencies.add(regularConfig.getName(), project.files(artifact));
continue;
}
@ -161,22 +161,6 @@ public class ModCompileRemapper {
}
}
/**
* Checks if an artifact is a fabric mod, according to the presence of a fabric.mod.json.
*/
private static boolean isFabricMod(Logger logger, File artifact, Object id) {
try (ZipFile zipFile = new ZipFile(artifact)) {
if (zipFile.getEntry("fabric.mod.json") != null) {
logger.info("Found Fabric mod in modCompile: {}", id);
return true;
}
return false;
} catch (IOException e) {
return false;
}
}
private static void addToRegularCompile(Project project, Configuration regularCompile, ResolvedArtifact artifact) {
project.getLogger().info(":providing " + artifact);
DependencyHandler dependencies = project.getDependencies();

View File

@ -39,8 +39,9 @@ import org.gradle.api.artifacts.Configuration;
import org.gradle.api.artifacts.ConfigurationContainer;
import org.gradle.api.tasks.SourceSet;
import net.fabricmc.loom.extension.MixinExtension;
import net.fabricmc.loom.LoomGradleExtension;
import net.fabricmc.loom.configuration.ide.idea.IdeaUtils;
import net.fabricmc.loom.extension.MixinExtension;
import net.fabricmc.loom.util.Constants;
/**
@ -97,9 +98,8 @@ public abstract class AnnotationProcessorInvoker<T extends Task> {
public void configureMixin() {
ConfigurationContainer configs = project.getConfigurations();
LoomGradleExtension extension = LoomGradleExtension.get(project);
if (!extension.ideSync()) {
if (!IdeaUtils.isIdeaSync()) {
for (Configuration processorConfig : apConfigurations) {
project.getLogger().info("Adding mixin to classpath of AP config: " + processorConfig.getName());
// Pass named MC classpath to mixin AP classpath

View File

@ -58,7 +58,7 @@ public class JavaApInvoker extends AnnotationProcessorInvoker<JavaCompile> {
@Override
protected File getRefmapDestinationDir(JavaCompile task) {
return task.getDestinationDir();
return task.getDestinationDirectory().getAsFile().get();
}
private static String getAptConfigurationName(String sourceSet) {

View File

@ -84,7 +84,7 @@ public class KaptApInvoker extends AnnotationProcessorInvoker<JavaCompile> {
try {
String refmapName = Objects.requireNonNull(MixinExtension.getMixinInformationContainer(sourceSet)).refmapNameProvider().get();
Path src = Paths.get(getRefmapDestination(task, refmapName));
Path dest = Paths.get(task.getDestinationDir().toString(), refmapName);
Path dest = Paths.get(task.getDestinationDirectory().get().getAsFile().toString(), refmapName);
// Possible that no mixin annotations exist
if (Files.exists(src)) {

View File

@ -59,6 +59,6 @@ public class ScalaApInvoker extends AnnotationProcessorInvoker<ScalaCompile> {
@Override
protected File getRefmapDestinationDir(ScalaCompile task) {
return task.getDestinationDir();
return task.getDestinationDirectory().get().getAsFile();
}
}

View File

@ -53,6 +53,7 @@ import net.fabricmc.loom.LoomGradleExtension;
import net.fabricmc.loom.LoomGradlePlugin;
import net.fabricmc.loom.task.RemapJarTask;
import net.fabricmc.loom.util.Constants;
import net.fabricmc.loom.util.ModUtils;
import net.fabricmc.loom.util.ZipUtils;
public final class NestedDependencyProvider implements NestedJarProvider {
@ -158,7 +159,7 @@ public final class NestedDependencyProvider implements NestedJarProvider {
File file = metaFile.file;
//A lib that doesnt have a mod.json, we turn it into a fake mod
if (!ZipUtils.contains(file.toPath(), "fabric.mod.json")) {
if (!ModUtils.isMod(file)) {
LoomGradleExtension extension = LoomGradleExtension.get(project);
File tempDir = new File(extension.getFiles().getUserCache(), "temp/modprocessing");

View File

@ -26,9 +26,11 @@ package net.fabricmc.loom.configuration;
import java.nio.charset.StandardCharsets;
import org.gradle.api.NamedDomainObjectProvider;
import org.gradle.api.Project;
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.plugins.JavaPlugin;
import org.gradle.api.plugins.JavaPluginConvention;
import org.gradle.api.plugins.JavaPluginExtension;
import org.gradle.api.tasks.AbstractCopyTask;
import org.gradle.api.tasks.SourceSet;
import org.gradle.api.tasks.compile.JavaCompile;
@ -39,7 +41,6 @@ import net.fabricmc.loom.LoomGradleExtension;
import net.fabricmc.loom.build.mixin.JavaApInvoker;
import net.fabricmc.loom.build.mixin.KaptApInvoker;
import net.fabricmc.loom.build.mixin.ScalaApInvoker;
import net.fabricmc.loom.configuration.ide.SetupIntelijRunConfigs;
import net.fabricmc.loom.configuration.providers.LaunchProvider;
import net.fabricmc.loom.configuration.providers.MinecraftProviderImpl;
import net.fabricmc.loom.configuration.providers.mappings.MappingsProviderImpl;
@ -53,15 +54,20 @@ public final class CompileConfiguration {
public static void setupConfigurations(Project project) {
LoomGradleExtension extension = LoomGradleExtension.get(project);
extension.createLazyConfiguration(Constants.Configurations.MOD_COMPILE_CLASSPATH).configure(configuration -> configuration.setTransitive(true));
extension.createLazyConfiguration(Constants.Configurations.MOD_COMPILE_CLASSPATH_MAPPED).configure(configuration -> configuration.setTransitive(false));
extension.createLazyConfiguration(Constants.Configurations.MINECRAFT_NAMED).configure(configuration -> configuration.setTransitive(false)); // The launchers do not recurse dependencies
extension.createLazyConfiguration(Constants.Configurations.MINECRAFT_DEPENDENCIES).configure(configuration -> configuration.setTransitive(false));
extension.createLazyConfiguration(Constants.Configurations.LOADER_DEPENDENCIES).configure(configuration -> configuration.setTransitive(false));
extension.createLazyConfiguration(Constants.Configurations.MINECRAFT).configure(configuration -> configuration.setTransitive(false));
extension.createLazyConfiguration(Constants.Configurations.INCLUDE).configure(configuration -> configuration.setTransitive(false)); // Dont get transitive deps
extension.createLazyConfiguration(Constants.Configurations.MOD_COMPILE_CLASSPATH, configuration -> configuration.setTransitive(true));
extension.createLazyConfiguration(Constants.Configurations.MOD_COMPILE_CLASSPATH_MAPPED, configuration -> configuration.setTransitive(false));
extension.createLazyConfiguration(Constants.Configurations.MINECRAFT_NAMED, configuration -> configuration.setTransitive(false)); // The launchers do not recurse dependencies
NamedDomainObjectProvider<Configuration> serverDeps = extension.createLazyConfiguration(Constants.Configurations.MINECRAFT_SERVER_DEPENDENCIES, configuration -> configuration.setTransitive(false));
extension.createLazyConfiguration(Constants.Configurations.MINECRAFT_DEPENDENCIES, configuration -> {
configuration.extendsFrom(serverDeps.get());
configuration.setTransitive(false);
});
extension.createLazyConfiguration(Constants.Configurations.MINECRAFT_NATIVES, configuration -> configuration.setTransitive(false));
extension.createLazyConfiguration(Constants.Configurations.LOADER_DEPENDENCIES, configuration -> configuration.setTransitive(false));
extension.createLazyConfiguration(Constants.Configurations.MINECRAFT, configuration -> configuration.setTransitive(false));
extension.createLazyConfiguration(Constants.Configurations.INCLUDE, configuration -> configuration.setTransitive(false)); // Dont get transitive deps
extension.createLazyConfiguration(Constants.Configurations.MAPPING_CONSTANTS);
extension.createLazyConfiguration(Constants.Configurations.NAMED_ELEMENTS).configure(configuration -> {
extension.createLazyConfiguration(Constants.Configurations.NAMED_ELEMENTS, configuration -> {
configuration.setCanBeConsumed(true);
configuration.setCanBeResolved(false);
configuration.extendsFrom(project.getConfigurations().getByName(JavaPlugin.API_CONFIGURATION_NAME));
@ -117,9 +123,9 @@ public final class CompileConfiguration {
}
public static void configureCompile(Project p) {
JavaPluginConvention javaModule = (JavaPluginConvention) p.getConvention().getPlugins().get("java");
final JavaPluginExtension javaPluginExtension = p.getExtensions().getByType(JavaPluginExtension.class);
SourceSet main = javaModule.getSourceSets().getByName(SourceSet.MAIN_SOURCE_SET_NAME);
SourceSet main = javaPluginExtension.getSourceSets().getByName(SourceSet.MAIN_SOURCE_SET_NAME);
Javadoc javadoc = (Javadoc) p.getTasks().getByName(JavaPlugin.JAVADOC_TASK_NAME);
javadoc.setClasspath(main.getOutput().plus(main.getCompileClasspath()));
@ -140,7 +146,6 @@ public final class CompileConfiguration {
project.getTasks().getByName("eclipse").finalizedBy(project.getTasks().getByName("genEclipseRuns"));
project.getTasks().getByName("cleanEclipse").finalizedBy(project.getTasks().getByName("cleanEclipseRuns"));
SetupIntelijRunConfigs.setup(project);
extension.getRemapArchives().finalizeValue();
// Enables the default mod remapper

View File

@ -46,6 +46,7 @@ import net.fabricmc.loom.LoomGradlePlugin;
import net.fabricmc.loom.LoomRepositoryPlugin;
import net.fabricmc.loom.build.ModCompileRemapper;
import net.fabricmc.loom.configuration.DependencyProvider.DependencyInfo;
import net.fabricmc.loom.configuration.ide.idea.IdeaUtils;
import net.fabricmc.loom.configuration.providers.mappings.MappingsProviderImpl;
import net.fabricmc.loom.util.Constants;
import net.fabricmc.loom.util.SourceRemapper;
@ -209,7 +210,7 @@ public class LoomDependencyManager {
loaderDepsConfig.getDependencies().add(modDep);
// TODO: work around until https://github.com/FabricMC/Mixin/pull/60 and https://github.com/FabricMC/fabric-mixin-compile-extensions/issues/14 is fixed.
if (!extension.ideSync() && extension.getMixin().getUseLegacyMixinAp().get()) {
if (!IdeaUtils.isIdeaSync() && extension.getMixin().getUseLegacyMixinAp().get()) {
apDepsConfig.getDependencies().add(modDep);
}

View File

@ -152,7 +152,7 @@ public class TransitiveAccessWidenerJarProcessor implements JarProcessor {
try {
AccessWidenerRemapper remappingVisitor = new AccessWidenerRemapper(
accessWidener,
tinyRemapper.getRemapper(),
tinyRemapper.getEnvironment().getRemapper(),
MappingsNamespace.INTERMEDIARY.toString(),
MappingsNamespace.NAMED.toString()
);

View File

@ -45,6 +45,7 @@ import org.w3c.dom.Node;
import net.fabricmc.loom.LoomGradleExtension;
import net.fabricmc.loom.configuration.InstallerData;
import net.fabricmc.loom.configuration.ide.idea.IdeaSyncTask;
public class RunConfig {
public String configName;
@ -174,7 +175,7 @@ public class RunConfig {
public String fromDummy(String dummy, boolean relativeDir, Project project) throws IOException {
String dummyConfig;
try (InputStream input = SetupIntelijRunConfigs.class.getClassLoader().getResourceAsStream(dummy)) {
try (InputStream input = IdeaSyncTask.class.getClassLoader().getResourceAsStream(dummy)) {
dummyConfig = new String(input.readAllBytes(), StandardCharsets.UTF_8);
}

View File

@ -34,7 +34,7 @@ import java.util.function.Function;
import org.gradle.api.Named;
import org.gradle.api.Project;
import org.gradle.api.plugins.JavaPluginConvention;
import org.gradle.api.plugins.JavaPluginExtension;
import org.gradle.api.tasks.SourceSet;
import net.fabricmc.loom.LoomGradleExtension;
@ -229,10 +229,7 @@ public final class RunConfigSettings implements Named {
}
public void source(String source) {
setSource(proj -> {
JavaPluginConvention conv = proj.getConvention().getPlugin(JavaPluginConvention.class);
return conv.getSourceSets().getByName(source);
});
setSource(proj -> project.getExtensions().getByType(JavaPluginExtension.class).getSourceSets().getByName(source));
}
public void ideConfigGenerated(boolean ideConfigGenerated) {
@ -243,7 +240,7 @@ public final class RunConfigSettings implements Named {
* Add the {@code -XstartOnFirstThread} JVM argument when on OSX.
*/
public void startFirstThread() {
if (OperatingSystem.getOS().equalsIgnoreCase("osx")) {
if (OperatingSystem.CURRENT_OS.equals(OperatingSystem.MAC_OS)) {
vmArg("-XstartOnFirstThread");
}
}

View File

@ -1,90 +0,0 @@
/*
* This file is part of fabric-loom, licensed under the MIT License (MIT).
*
* Copyright (c) 2016-2021 FabricMC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package net.fabricmc.loom.configuration.ide;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import org.apache.commons.io.FileUtils;
import org.gradle.api.Project;
import net.fabricmc.loom.LoomGradleExtension;
import net.fabricmc.loom.configuration.providers.minecraft.MinecraftNativesProvider;
import net.fabricmc.loom.configuration.providers.minecraft.assets.MinecraftAssetsProvider;
public class SetupIntelijRunConfigs {
public static void setup(Project project) {
File projectDir = project.getRootProject().file(".idea");
if (!projectDir.exists()) {
return;
}
try {
generate(project);
} catch (IOException e) {
throw new RuntimeException("Failed to generate run configs", e);
}
}
private static void generate(Project project) throws IOException {
Project rootProject = project.getRootProject();
LoomGradleExtension extension = LoomGradleExtension.get(project);
if (extension.ideSync()) {
//Ensures the assets are downloaded when idea is syncing a project
MinecraftAssetsProvider.provide(extension.getMinecraftProvider(), project);
MinecraftNativesProvider.provide(project);
}
String projectPath = project == rootProject ? "" : project.getPath().replace(':', '_');
File projectDir = rootProject.file(".idea");
File runConfigsDir = new File(projectDir, "runConfigurations");
if (!runConfigsDir.exists()) {
runConfigsDir.mkdirs();
}
for (RunConfigSettings settings : extension.getRunConfigs()) {
if (!settings.isIdeConfigGenerated()) {
continue;
}
RunConfig config = RunConfig.runConfig(project, settings);
String name = config.configName.replaceAll("[^a-zA-Z0-9$_]", "_");
File runConfigs = new File(runConfigsDir, name + projectPath + ".xml");
String runConfigXml = config.fromDummy("idea_run_config_template.xml", true, project);
if (!runConfigs.exists()) {
FileUtils.writeStringToFile(runConfigs, runConfigXml, StandardCharsets.UTF_8);
}
settings.makeRunDir();
}
}
}

View File

@ -0,0 +1,83 @@
/*
* This file is part of fabric-loom, licensed under the MIT License (MIT).
*
* Copyright (c) 2021 FabricMC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package net.fabricmc.loom.configuration.ide.idea;
import java.util.Objects;
import org.gradle.api.NamedDomainObjectContainer;
import org.gradle.api.Project;
import org.gradle.api.plugins.ExtensionAware;
import org.gradle.api.tasks.TaskProvider;
import org.gradle.plugins.ide.idea.model.IdeaModel;
import org.gradle.plugins.ide.idea.model.IdeaProject;
import org.jetbrains.gradle.ext.ActionDelegationConfig;
import org.jetbrains.gradle.ext.IdeaExtPlugin;
import org.jetbrains.gradle.ext.ProjectSettings;
import org.jetbrains.gradle.ext.RunConfiguration;
import org.jetbrains.gradle.ext.TaskTriggersConfig;
public class IdeaConfiguration {
public static void setup(Project project) {
TaskProvider<IdeaSyncTask> ideaSyncTask = project.getTasks().register("ideaSyncTask", IdeaSyncTask.class, ideaSyncTask1 -> {
ideaSyncTask1.dependsOn(project.getTasks().named("downloadAssets"));
});
if (!IdeaUtils.isIdeaSync()) {
return;
}
project.getPlugins().apply(IdeaExtPlugin.class);
project.getPlugins().withType(IdeaExtPlugin.class, ideaExtPlugin -> {
if (project != project.getRootProject()) {
// Also ensure it's applied to the root project.
project.getRootProject().getPlugins().apply(IdeaExtPlugin.class);
}
final IdeaModel ideaModel = project.getRootProject().getExtensions().findByType(IdeaModel.class);
if (ideaModel == null) {
return;
}
final IdeaProject ideaProject = ideaModel.getProject();
if (ideaProject == null) {
return;
}
final ProjectSettings settings = getExtension(ideaProject, ProjectSettings.class);
final ActionDelegationConfig delegateActions = getExtension(settings, ActionDelegationConfig.class);
final TaskTriggersConfig taskTriggers = getExtension(settings, TaskTriggersConfig.class);
final NamedDomainObjectContainer<RunConfiguration> runConfigurations = (NamedDomainObjectContainer<RunConfiguration>) ((ExtensionAware) settings).getExtensions().getByName("runConfigurations");
// Run the sync task on import
taskTriggers.afterSync(ideaSyncTask);
});
}
private static <T> T getExtension(Object extensionAware, Class<T> type) {
return Objects.requireNonNull(((ExtensionAware) extensionAware).getExtensions().getByType(type));
}
}

View File

@ -0,0 +1,130 @@
/*
* This file is part of fabric-loom, licensed under the MIT License (MIT).
*
* Copyright (c) 2021 FabricMC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package net.fabricmc.loom.configuration.ide.idea;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import javax.inject.Inject;
import org.apache.commons.io.FileUtils;
import org.gradle.api.Project;
import org.gradle.api.tasks.TaskAction;
import net.fabricmc.loom.LoomGradleExtension;
import net.fabricmc.loom.configuration.ide.RunConfig;
import net.fabricmc.loom.configuration.ide.RunConfigSettings;
import net.fabricmc.loom.configuration.providers.BundleMetadata;
import net.fabricmc.loom.task.AbstractLoomTask;
import net.fabricmc.loom.util.Constants;
public abstract class IdeaSyncTask extends AbstractLoomTask {
@Inject
public IdeaSyncTask() {
// Always re-run this task.
getOutputs().upToDateWhen(element -> false);
}
@TaskAction
public void runTask() throws IOException {
File projectDir = getProject().getRootProject().file(".idea");
if (!projectDir.exists()) {
throw new RuntimeException("No .idea directory found");
}
generateRunConfigs();
}
// See: https://github.com/FabricMC/fabric-loom/pull/206#issuecomment-986054254 for the reason why XML's are still used to provide the run configs
private void generateRunConfigs() throws IOException {
Project rootProject = getProject().getRootProject();
LoomGradleExtension extension = LoomGradleExtension.get(getProject());
String projectPath = getProject() == rootProject ? "" : getProject().getPath().replace(':', '_');
File runConfigsDir = new File(rootProject.file(".idea"), "runConfigurations");
if (!runConfigsDir.exists()) {
runConfigsDir.mkdirs();
}
final List<String> excludedServerLibraries = getExcludedServerLibraries();
for (RunConfigSettings settings : extension.getRunConfigs()) {
if (!settings.isIdeConfigGenerated()) {
continue;
}
RunConfig config = RunConfig.runConfig(getProject(), settings);
String name = config.configName.replaceAll("[^a-zA-Z0-9$_]", "_");
File runConfigs = new File(runConfigsDir, name + projectPath + ".xml");
String runConfigXml = config.fromDummy("idea_run_config_template.xml", true, getProject());
if (!runConfigs.exists()) {
FileUtils.writeStringToFile(runConfigs, runConfigXml, StandardCharsets.UTF_8);
}
settings.makeRunDir();
if (settings.getEnvironment().equals("server") && !excludedServerLibraries.isEmpty()) {
try {
setClasspathModifications(runConfigs, excludedServerLibraries);
} catch (Exception e) {
getProject().getLogger().error("Failed to modify run configuration xml", e);
}
}
}
}
private List<String> getExcludedServerLibraries() {
final BundleMetadata bundleMetadata = getExtension().getMinecraftProvider().getServerBundleMetadata();
if (bundleMetadata == null) {
// Legacy version
return Collections.emptyList();
}
final Set<File> allLibraries = getProject().getConfigurations().getByName(Constants.Configurations.MINECRAFT_DEPENDENCIES).getFiles();
final Set<File> serverLibraries = getProject().getConfigurations().getByName(Constants.Configurations.MINECRAFT_SERVER_DEPENDENCIES).getFiles();
final List<String> clientOnlyLibraries = new LinkedList<>();
for (File commonLibrary : allLibraries) {
if (!serverLibraries.contains(commonLibrary)) {
clientOnlyLibraries.add(commonLibrary.getAbsolutePath());
}
}
return clientOnlyLibraries;
}
private void setClasspathModifications(File runConfig, List<String> exclusions) throws Exception {
// TODO modify the xml
}
}

View File

@ -0,0 +1,45 @@
/*
* This file is part of fabric-loom, licensed under the MIT License (MIT).
*
* Copyright (c) 2021 FabricMC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package net.fabricmc.loom.configuration.ide.idea;
import java.util.Objects;
public class IdeaUtils {
public static boolean isIdeaSync() {
return Boolean.parseBoolean(System.getProperty("idea.sync.active", "false"));
}
public static String getIdeaVersion() {
return Objects.requireNonNull(System.getProperty("idea.version"), "Could not get idea version");
}
// 2021.3 or newer
public static boolean supportsCustomizableClasspath() {
final String[] split = getIdeaVersion().split("\\.");
final int major = Integer.parseInt(split[0]);
final int minor = Integer.parseInt(split[1]);
return major > 2021 || (major == 2021 && minor >= 3);
}
}

View File

@ -30,7 +30,7 @@ import java.io.IOException;
import com.google.gson.JsonObject;
import org.gradle.api.Project;
import org.gradle.api.plugins.JavaPluginConvention;
import org.gradle.api.plugins.JavaPluginExtension;
import net.fabricmc.loom.LoomGradlePlugin;
@ -67,7 +67,7 @@ public class ModVersionParser {
}
private File locateModJsonFile() {
return project.getConvention().getPlugin(JavaPluginConvention.class).getSourceSets()
return project.getExtensions().getByType(JavaPluginExtension.class).getSourceSets()
.getByName("main")
.getResources()
.matching(patternFilterable -> patternFilterable.include("fabric.mod.json"))

View File

@ -0,0 +1,93 @@
/*
* This file is part of fabric-loom, licensed under the MIT License (MIT).
*
* Copyright (c) 2021 FabricMC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package net.fabricmc.loom.configuration.providers;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.jetbrains.annotations.Nullable;
import net.fabricmc.loom.util.FileSystemUtil;
public record BundleMetadata(List<Entry> libraries, List<Entry> versions, String mainClass) {
private static final String LIBRARIES_LIST_PATH = "META-INF/libraries.list";
private static final String VERSIONS_LIST_PATH = "META-INF/versions.list";
private static final String MAINCLASS_PATH = "META-INF/main-class";
@Nullable
public static BundleMetadata fromJar(Path jar) throws IOException {
final List<Entry> libraries;
final List<Entry> versions;
final String mainClass;
try (FileSystemUtil.Delegate fs = FileSystemUtil.getJarFileSystem(jar)) {
if (!Files.exists(fs.get().getPath(VERSIONS_LIST_PATH))) {
// Legacy jar
return null;
}
libraries = readEntries(fs.readString(LIBRARIES_LIST_PATH), "META-INF/libraries/");
versions = readEntries(fs.readString(VERSIONS_LIST_PATH), "META-INF/versions/");
mainClass = fs.readString(MAINCLASS_PATH).trim();
}
return new BundleMetadata(libraries, versions, mainClass);
}
private static List<Entry> readEntries(String content, String pathPrefix) {
List<Entry> entries = new ArrayList<>();
for (String entry : content.split("\n")) {
if (entry.isBlank()) {
continue;
}
String[] split = entry.split("\t");
if (split.length != 3) {
continue;
}
entries.add(new Entry(split[0], split[1], pathPrefix + split[2]));
}
return Collections.unmodifiableList(entries);
}
public record Entry(String sha1, String name, String path) {
public void unpackEntry(Path jar, Path dest) throws IOException {
try (FileSystemUtil.Delegate fs = FileSystemUtil.getJarFileSystem(jar);
InputStream is = Files.newInputStream(fs.get().getPath(path()))) {
Files.copy(is, dest, StandardCopyOption.REPLACE_EXISTING);
}
}
}
}

View File

@ -54,13 +54,15 @@ public class LaunchProvider extends DependencyProvider {
@Override
public void provide(DependencyInfo dependency, Consumer<Runnable> postPopulationScheduler) throws IOException {
final String nativesPath = getExtension().getFiles().getNativesDirectory(getProject()).getAbsolutePath();
final LaunchConfig launchConfig = new LaunchConfig()
.property("fabric.development", "true")
.property("fabric.remapClasspathFile", getRemapClasspathFile().getAbsolutePath())
.property("log4j.configurationFile", getAllLog4JConfigFiles())
.property("client", "java.library.path", getExtension().getMinecraftProvider().nativesDir().getAbsolutePath())
.property("client", "org.lwjgl.librarypath", getExtension().getMinecraftProvider().nativesDir().getAbsolutePath())
.property("client", "java.library.path", nativesPath)
.property("client", "org.lwjgl.librarypath", nativesPath)
.argument("client", "--assetIndex")
.argument("client", getExtension().getMinecraftProvider().getVersionInfo().assetIndex().fabricId(getExtension().getMinecraftProvider().minecraftVersion()))

View File

@ -31,10 +31,6 @@ import net.fabricmc.loom.configuration.providers.minecraft.MinecraftVersionMeta;
public interface MinecraftProvider {
File workingDir();
boolean hasCustomNatives();
File nativesDir();
File dir(String path);
File file(String path);

View File

@ -27,20 +27,16 @@ package net.fabricmc.loom.configuration.providers;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.StandardCopyOption;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import com.google.common.io.Files;
import org.gradle.api.GradleException;
import org.gradle.api.Project;
import org.gradle.api.logging.Logger;
import org.jetbrains.annotations.Nullable;
import net.fabricmc.loom.LoomGradlePlugin;
import net.fabricmc.loom.configuration.DependencyProvider;
@ -48,9 +44,9 @@ import net.fabricmc.loom.configuration.providers.minecraft.ManifestVersion;
import net.fabricmc.loom.configuration.providers.minecraft.MinecraftLibraryProvider;
import net.fabricmc.loom.configuration.providers.minecraft.MinecraftVersionMeta;
import net.fabricmc.loom.util.Constants;
import net.fabricmc.loom.util.MirrorUtil;
import net.fabricmc.loom.util.DownloadUtil;
import net.fabricmc.loom.util.HashedDownloadUtil;
import net.fabricmc.loom.util.MirrorUtil;
import net.fabricmc.stitch.merge.JarMerger;
public class MinecraftProviderImpl extends DependencyProvider implements MinecraftProvider {
@ -66,6 +62,8 @@ public class MinecraftProviderImpl extends DependencyProvider implements Minecra
private File minecraftServerJar;
// The extracted server jar from the boostrap, only exists in >=21w39a
private File minecraftExtractedServerJar;
@Nullable
private BundleMetadata serverBundleMetadata;
private File minecraftMergedJar;
private File versionManifestJson;
private File experimentalVersionsJson;
@ -101,6 +99,8 @@ public class MinecraftProviderImpl extends DependencyProvider implements Minecra
downloadJars(getProject().getLogger());
}
serverBundleMetadata = BundleMetadata.fromJar(minecraftServerJar.toPath());
libraryProvider = new MinecraftLibraryProvider();
libraryProvider.provide(this, getProject());
@ -257,61 +257,22 @@ public class MinecraftProviderImpl extends DependencyProvider implements Minecra
private void mergeJars(Logger logger) throws IOException {
logger.info(":merging jars");
try (JarMerger jarMerger = new JarMerger(minecraftClientJar, getServerJarToMerge(logger), minecraftMergedJar)) {
jarMerger.enableSyntheticParamsOffset();
jarMerger.merge();
}
}
private File getServerJarToMerge(Logger logger) throws IOException {
try (ZipFile zipFile = new ZipFile(minecraftServerJar)) {
ZipEntry versionsListEntry = zipFile.getEntry("META-INF/versions.list");
if (versionsListEntry == null) {
// Legacy pre 21w38a jar
return minecraftServerJar;
}
File jarToMerge = minecraftServerJar;
if (serverBundleMetadata != null) {
logger.info(":Extracting server jar from bootstrap");
String versionsList;
try (InputStream is = zipFile.getInputStream(versionsListEntry)) {
versionsList = new String(is.readAllBytes(), StandardCharsets.UTF_8);
if (serverBundleMetadata.versions().size() != 1) {
throw new UnsupportedOperationException("Expected only 1 version in META-INF/versions.list, but got %d".formatted(serverBundleMetadata.versions().size()));
}
String jarPath = null;
String[] versions = versionsList.split("\n");
serverBundleMetadata.versions().get(0).unpackEntry(minecraftServerJar.toPath(), minecraftExtractedServerJar.toPath());
jarToMerge = minecraftExtractedServerJar;
}
if (versions.length != 1) {
throw new UnsupportedOperationException("Expected only 1 version in META-INF/versions.list, but got %d".formatted(versions.length));
}
for (String version : versions) {
if (version.isBlank()) continue;
String[] split = version.split("\t");
if (split.length != 3) continue;
final String hash = split[0];
final String id = split[1];
final String path = split[2];
// Take the first (only) version we find.
jarPath = path;
break;
}
Objects.requireNonNull(jarPath, "Could not find minecraft server jar for " + minecraftVersion());
ZipEntry serverJarEntry = zipFile.getEntry("META-INF/versions/" + jarPath);
Objects.requireNonNull(serverJarEntry, "Could not find server jar in boostrap@ " + jarPath);
try (InputStream is = zipFile.getInputStream(serverJarEntry)) {
java.nio.file.Files.copy(is, minecraftExtractedServerJar.toPath(), StandardCopyOption.REPLACE_EXISTING);
}
return minecraftExtractedServerJar;
try (JarMerger jarMerger = new JarMerger(minecraftClientJar, jarToMerge, minecraftMergedJar)) {
jarMerger.enableSyntheticParamsOffset();
jarMerger.merge();
}
}
@ -324,20 +285,6 @@ public class MinecraftProviderImpl extends DependencyProvider implements Minecra
return workingDir;
}
@Override
public boolean hasCustomNatives() {
return getProject().getProperties().get("fabric.loom.natives.dir") != null;
}
@Override
public File nativesDir() {
if (hasCustomNatives()) {
return new File((String) getProject().property("fabric.loom.natives.dir"));
}
return dir("natives");
}
@Override
public File dir(String path) {
File dir = file(path);
@ -368,4 +315,9 @@ public class MinecraftProviderImpl extends DependencyProvider implements Minecra
public String getTargetConfig() {
return Constants.Configurations.MINECRAFT;
}
@Nullable
public BundleMetadata getServerBundleMetadata() {
return serverBundleMetadata;
}
}

View File

@ -0,0 +1,108 @@
/*
* This file is part of fabric-loom, licensed under the MIT License (MIT).
*
* Copyright (c) 2021 FabricMC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package net.fabricmc.loom.configuration.providers.minecraft;
import static net.fabricmc.loom.util.OperatingSystem.LINUX;
import static net.fabricmc.loom.util.OperatingSystem.MAC_OS;
import static net.fabricmc.loom.util.OperatingSystem.WINDOWS;
import java.util.List;
import org.gradle.api.Project;
import org.jetbrains.annotations.Nullable;
import net.fabricmc.loom.util.Architecture;
import net.fabricmc.loom.util.OperatingSystem;
public class LWJGLVersionOverride {
public static final String LWJGL_VERSION = "3.3.0";
@Nullable
public static final String NATIVE_CLASSIFIER = getNativesClassifier();
public static final List<String> DEPENDENCIES = List.of(
"org.lwjgl:lwjgl:" + LWJGL_VERSION,
"org.lwjgl:lwjgl-glfw:" + LWJGL_VERSION,
"org.lwjgl:lwjgl-jemalloc:" + LWJGL_VERSION,
"org.lwjgl:lwjgl-openal:" + LWJGL_VERSION,
"org.lwjgl:lwjgl-opengl:" + LWJGL_VERSION,
"org.lwjgl:lwjgl-stb:" + LWJGL_VERSION,
"org.lwjgl:lwjgl-tinyfd:" + LWJGL_VERSION
);
public static final List<String> NATIVES = DEPENDENCIES.stream().map(s -> s + ":" + NATIVE_CLASSIFIER).toList();
/**
* Update lwjgl by default when running on arm and a supported configuration.
*/
public static boolean overrideByDefault() {
return NATIVE_CLASSIFIER != null && Architecture.CURRENT.isArm();
}
public static boolean forceOverride(Project project) {
return project.getProperties().get("fabric.loom.override-lwjgl") != null;
}
@Nullable
private static String getNativesClassifier() {
return switch (OperatingSystem.CURRENT_OS) {
case WINDOWS -> getWindowsClassifier();
case MAC_OS -> getMacOSClassifier();
case LINUX -> getLinuxClassifier();
default -> null;
};
}
private static String getWindowsClassifier() {
if (Architecture.CURRENT.is64Bit()) {
if (Architecture.CURRENT.isArm()) {
// Arm 64 bit
return "natives-windows-arm64";
}
// None arm 64bit
return "natives-windows";
}
// All 32bit, including arm
return "natives-windows-x86";
}
private static String getMacOSClassifier() {
if (Architecture.CURRENT.isArm()) {
// Apple silicone arm
return "natives-macos-arm64";
}
// Intel 64bit.
return "natives-macos";
}
private static String getLinuxClassifier() {
if (Architecture.CURRENT.isArm()) {
return Architecture.CURRENT.is64Bit() ? "natives-linux-arm64" : "natives-linux-arm32";
}
return "natives-linux";
}
}

View File

@ -24,31 +24,77 @@
package net.fabricmc.loom.configuration.providers.minecraft;
import java.io.File;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.gradle.api.Project;
import org.gradle.api.artifacts.ExternalModuleDependency;
import net.fabricmc.loom.LoomGradleExtension;
import net.fabricmc.loom.configuration.providers.BundleMetadata;
import net.fabricmc.loom.configuration.providers.MinecraftProviderImpl;
import net.fabricmc.loom.util.Constants;
public class MinecraftLibraryProvider {
public File MINECRAFT_LIBS;
private static final Pattern NATIVES_PATTERN = Pattern.compile("^(?<group>.*)/(.*?)/(?<version>.*)/((?<name>.*?)-([0-9].*?)-)(?<classifier>.*).jar$");
public void provide(MinecraftProviderImpl minecraftProvider, Project project) {
MinecraftVersionMeta versionInfo = minecraftProvider.getVersionInfo();
BundleMetadata serverBundleMetadata = minecraftProvider.getServerBundleMetadata();
initFiles(project, minecraftProvider);
final boolean overrideLWJGL = LWJGLVersionOverride.overrideByDefault() || LWJGLVersionOverride.forceOverride(project) || Boolean.getBoolean("loom.test.lwjgloverride");
if (overrideLWJGL) {
project.getLogger().warn("Loom is upgrading Minecraft's LWJGL version to {}", LWJGLVersionOverride.LWJGL_VERSION);
}
for (MinecraftVersionMeta.Library library : versionInfo.libraries()) {
if (library.isValidForOS() && !library.hasNatives() && library.artifact() != null) {
project.getDependencies().add(Constants.Configurations.MINECRAFT_DEPENDENCIES, project.getDependencies().module(library.name()));
if (overrideLWJGL && library.name().startsWith("org.lwjgl")) {
continue;
}
if (library.isValidForOS() && !library.hasNatives() && library.artifact() != null) {
if (serverBundleMetadata != null && isLibraryInBundle(serverBundleMetadata, library)) {
project.getDependencies().add(Constants.Configurations.MINECRAFT_SERVER_DEPENDENCIES, library.name());
} else {
// Client only library, or legacy version
project.getDependencies().add(Constants.Configurations.MINECRAFT_DEPENDENCIES, library.name());
}
}
if (library.hasNativesForOS()) {
MinecraftVersionMeta.Download nativeDownload = library.classifierForOS();
Matcher matcher = NATIVES_PATTERN.matcher(nativeDownload.path());
if (!matcher.find()) {
project.getLogger().warn("Failed to match regex for natives path : " + nativeDownload.path());
continue;
}
final String group = matcher.group("group").replace("/", ".");
final String name = matcher.group("name");
final String version = matcher.group("version");
final String classifier = matcher.group("classifier");
final String dependencyNotation = "%s:%s:%s:%s".formatted(group, name, version, classifier);
project.getLogger().debug("Add native dependency '{}'", dependencyNotation);
project.getDependencies().add(Constants.Configurations.MINECRAFT_NATIVES, dependencyNotation);
}
}
if (overrideLWJGL) {
LWJGLVersionOverride.DEPENDENCIES.forEach(s -> project.getDependencies().add(Constants.Configurations.MINECRAFT_DEPENDENCIES, s));
LWJGLVersionOverride.NATIVES.forEach(s -> project.getDependencies().add(Constants.Configurations.MINECRAFT_NATIVES, s));
// Add the native support mod that fixes a handful of issues related to the LWJGL update at runtime.
ExternalModuleDependency dependency = (ExternalModuleDependency) project.getDependencies().create(Constants.Dependencies.NATIVE_SUPPORT + Constants.Dependencies.Versions.NATIVE_SUPPORT_VERSION);
dependency.setTransitive(false);
project.getDependencies().add("modLocalRuntime", dependency);
}
}
private void initFiles(Project project, MinecraftProviderImpl minecraftProvider) {
LoomGradleExtension extension = LoomGradleExtension.get(project);
MINECRAFT_LIBS = new File(extension.getFiles().getUserCache(), "libraries");
private static boolean isLibraryInBundle(BundleMetadata bundleMetadata, MinecraftVersionMeta.Library library) {
return bundleMetadata.libraries().stream().anyMatch(entry -> entry.name().equals(library.name()));
}
}

View File

@ -155,7 +155,7 @@ public class MinecraftMappedProvider extends DependencyProvider {
// Remap the sig fixes from intermediary to the target namespace
final Map<String, String> remapped = new HashMap<>();
final TinyRemapper sigTinyRemapper = TinyRemapperHelper.getTinyRemapper(getProject(), MappingsNamespace.INTERMEDIARY.toString(), toM);
final Remapper sigAsmRemapper = sigTinyRemapper.getRemapper();
final Remapper sigAsmRemapper = sigTinyRemapper.getEnvironment().getRemapper();
// Remap the class names and the signatures using a new tiny remapper instance.
for (Map.Entry<String, String> entry : mappingsProvider.getSignatureFixes().entrySet()) {

View File

@ -1,150 +0,0 @@
/*
* This file is part of fabric-loom, licensed under the MIT License (MIT).
*
* Copyright (c) 2016-2021 FabricMC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package net.fabricmc.loom.configuration.providers.minecraft;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import org.apache.commons.io.FileUtils;
import org.gradle.api.GradleException;
import org.gradle.api.Project;
import net.fabricmc.loom.LoomGradleExtension;
import net.fabricmc.loom.LoomGradlePlugin;
import net.fabricmc.loom.util.HashedDownloadUtil;
import net.fabricmc.loom.util.MirrorUtil;
import net.fabricmc.loom.util.ZipUtils;
public class MinecraftNativesProvider {
private final Project project;
private final LoomGradleExtension extension;
private final File nativesDir;
private final File jarStore;
public MinecraftNativesProvider(Project project) {
this.project = project;
extension = LoomGradleExtension.get(project);
nativesDir = extension.getMinecraftProvider().nativesDir();
jarStore = extension.getFiles().getNativesJarStore();
}
public static void provide(Project project) throws IOException {
new MinecraftNativesProvider(project).provide();
}
private void provide() throws IOException {
if (extension.getMinecraftProvider().hasCustomNatives()) {
if (!nativesDir.exists()) {
throw new RuntimeException("Could no find custom natives directory at " + nativesDir.getAbsolutePath());
}
return;
}
if (!LoomGradlePlugin.refreshDeps && !requiresExtract()) {
project.getLogger().info("Natives do no need extracting, skipping");
return;
}
extractNatives();
}
private void extractNatives() throws IOException {
boolean offline = project.getGradle().getStartParameter().isOffline();
if (nativesDir.exists()) {
try {
FileUtils.deleteDirectory(nativesDir);
} catch (IOException e) {
throw new IOException("Failed to delete the natives directory, is the game running?", e);
}
}
nativesDir.mkdirs();
for (MinecraftVersionMeta.Download library : getNatives()) {
File libJarFile = library.relativeFile(jarStore);
if (!offline) {
HashedDownloadUtil.downloadIfInvalid(new URL(MirrorUtil.getLibrariesBase(project) + library.path()), libJarFile, library.sha1(), project.getLogger(), false);
}
if (!libJarFile.exists()) {
throw new GradleException("Native jar not found at " + libJarFile.getAbsolutePath());
}
ZipUtils.unpackAll(libJarFile.toPath(), nativesDir.toPath());
// Store a file containing the hash of the extracted natives, used on subsequent runs to skip extracting all the natives if they haven't changed
File libSha1File = new File(nativesDir, libJarFile.getName() + ".sha1");
FileUtils.writeStringToFile(libSha1File, library.sha1(), StandardCharsets.UTF_8);
}
}
private boolean requiresExtract() {
List<MinecraftVersionMeta.Download> natives = getNatives();
if (natives.isEmpty()) {
throw new IllegalStateException("No natives found for the current system");
}
for (MinecraftVersionMeta.Download library : natives) {
File libJarFile = library.relativeFile(jarStore);
File libSha1File = new File(nativesDir, libJarFile.getName() + ".sha1");
if (!libSha1File.exists()) {
return true;
}
try {
String sha1 = FileUtils.readFileToString(libSha1File, StandardCharsets.UTF_8);
if (!sha1.equalsIgnoreCase(library.sha1())) {
return true;
}
} catch (IOException e) {
project.getLogger().error("Failed to read " + libSha1File.getAbsolutePath(), e);
return true;
}
}
// All looks good, no need to re-extract
return false;
}
private List<MinecraftVersionMeta.Download> getNatives() {
return extension.getMinecraftProvider().getVersionInfo().libraries().stream()
.filter((MinecraftVersionMeta.Library::hasNativesForOS))
.map(MinecraftVersionMeta.Library::classifierForOS)
.filter(Objects::nonNull)
.collect(Collectors.toList());
}
}

View File

@ -81,7 +81,7 @@ public record MinecraftVersionMeta(
return false;
}
if (natives.get(OperatingSystem.getOS()) == null) {
if (natives.get(OperatingSystem.CURRENT_OS) == null) {
return false;
}
@ -89,7 +89,7 @@ public record MinecraftVersionMeta(
}
public Download classifierForOS() {
return downloads().classifier(natives.get(OperatingSystem.getOS()));
return downloads().classifier(natives.get(OperatingSystem.CURRENT_OS));
}
public Download artifact() {
@ -119,7 +119,7 @@ public record MinecraftVersionMeta(
public record OS(String name) {
public boolean isValidForOS() {
return name() == null || name().equalsIgnoreCase(OperatingSystem.getOS());
return name() == null || name().equalsIgnoreCase(OperatingSystem.CURRENT_OS);
}
}

View File

@ -43,7 +43,7 @@ public interface LoomFiles {
File getProjectPersistentCache();
File getProjectBuildCache();
File getRemappedModCache();
File getNativesJarStore();
File getNativesDirectory(Project project);
File getDefaultLog4jConfigFile();
File getDevLauncherConfig();
File getUnpickLoggingConfigFile();

View File

@ -26,6 +26,10 @@ package net.fabricmc.loom.extension;
import java.io.File;
import org.gradle.api.Project;
import net.fabricmc.loom.LoomGradleExtension;
public abstract class LoomFilesBaseImpl implements LoomFiles {
protected abstract File getGradleUserHomeDir();
protected abstract File getRootDir();
@ -70,8 +74,8 @@ public abstract class LoomFilesBaseImpl implements LoomFiles {
}
@Override
public File getNativesJarStore() {
return createFile(getUserCache(), "natives/jars");
public File getNativesDirectory(Project project) {
return createFile(getRootProjectPersistentCache(), "natives/" + LoomGradleExtension.get(project).getMinecraftProvider().minecraftVersion());
}
@Override

View File

@ -37,6 +37,7 @@ import java.util.function.Supplier;
import org.cadixdev.lorenz.MappingSet;
import org.cadixdev.mercury.Mercury;
import org.gradle.api.Action;
import org.gradle.api.NamedDomainObjectProvider;
import org.gradle.api.Project;
import org.gradle.api.artifacts.Configuration;
@ -145,8 +146,8 @@ public class LoomGradleExtensionImpl extends LoomGradleExtensionApiImpl implemen
}
@Override
public NamedDomainObjectProvider<Configuration> createLazyConfiguration(String name) {
NamedDomainObjectProvider<Configuration> provider = project.getConfigurations().register(name);
public NamedDomainObjectProvider<Configuration> createLazyConfiguration(String name, Action<? super Configuration> configurationAction) {
NamedDomainObjectProvider<Configuration> provider = project.getConfigurations().register(name, configurationAction);
if (lazyConfigurations.containsKey(name)) {
throw new IllegalStateException("Duplicate configuration name" + name);

View File

@ -29,7 +29,7 @@ import java.util.Objects;
import org.gradle.api.Action;
import org.gradle.api.InvalidUserDataException;
import org.gradle.api.Project;
import org.gradle.api.plugins.JavaPluginConvention;
import org.gradle.api.plugins.JavaPluginExtension;
import org.gradle.api.provider.Property;
import org.gradle.api.provider.Provider;
import org.gradle.api.tasks.SourceSet;
@ -110,8 +110,7 @@ public abstract class MixinExtensionApiImpl implements MixinExtensionAPI {
private SourceSet resolveSourceSet(String sourceSetName) {
// try to find sourceSet with name sourceSetName in this project
SourceSet sourceSet = project.getConvention().getPlugin(JavaPluginConvention.class)
.getSourceSets().findByName(sourceSetName);
SourceSet sourceSet = project.getExtensions().getByType(JavaPluginExtension.class).getSourceSets().findByName(sourceSetName);
if (sourceSet == null) {
throw new InvalidUserDataException("No sourceSet " + sourceSetName + " was found");

View File

@ -38,8 +38,8 @@ import org.gradle.api.Project;
import org.gradle.api.Task;
import org.gradle.api.UnknownTaskException;
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.plugins.BasePluginConvention;
import org.gradle.api.plugins.JavaPluginConvention;
import org.gradle.api.plugins.BasePluginExtension;
import org.gradle.api.plugins.JavaPluginExtension;
import org.gradle.api.provider.Property;
import org.gradle.api.provider.Provider;
import org.gradle.api.tasks.Input;
@ -67,7 +67,7 @@ public class MixinExtensionImpl extends MixinExtensionApiImpl implements MixinEx
}
private String getDefaultMixinRefmapName() {
String defaultRefmapName = project.getConvention().getPlugin(BasePluginConvention.class).getArchivesBaseName() + "-refmap.json";
String defaultRefmapName = project.getExtensions().getByType(BasePluginExtension.class).getArchivesName().get() + "-refmap.json";
project.getLogger().info("Could not find refmap definition, will be using default name: " + defaultRefmapName);
return defaultRefmapName;
}
@ -87,7 +87,7 @@ public class MixinExtensionImpl extends MixinExtensionApiImpl implements MixinEx
@Override
@NotNull
public Stream<SourceSet> getMixinSourceSetsStream() {
return project.getConvention().getPlugin(JavaPluginConvention.class).getSourceSets().stream()
return project.getExtensions().getByType(JavaPluginExtension.class).getSourceSets().stream()
.filter(sourceSet -> MixinExtension.getMixinInformationContainer(sourceSet) != null);
}
@ -122,7 +122,7 @@ public class MixinExtensionImpl extends MixinExtensionApiImpl implements MixinEx
@Override
public void init() {
if (isDefault) {
project.getConvention().getPlugin(JavaPluginConvention.class).getSourceSets().forEach(this::add);
project.getExtensions().getByType(JavaPluginExtension.class).getSourceSets().forEach(this::add);
}
isDefault = false;

View File

@ -46,6 +46,7 @@ public abstract class AbstractRunTask extends JavaExec {
setClasspath(config.sourceSet.getRuntimeClasspath());
args(config.programArgs);
getMainClass().set(config.mainClass);
}
@Override
@ -64,11 +65,6 @@ public abstract class AbstractRunTask extends JavaExec {
super.setWorkingDir(dir);
}
@Override
public String getMain() {
return config.mainClass;
}
@Override
public List<String> getJvmArgs() {
List<String> superArgs = super.getJvmArgs();

View File

@ -30,7 +30,6 @@ import org.gradle.api.Project;
import org.gradle.api.tasks.TaskAction;
import net.fabricmc.loom.LoomGradleExtension;
import net.fabricmc.loom.configuration.providers.minecraft.MinecraftNativesProvider;
import net.fabricmc.loom.configuration.providers.minecraft.assets.MinecraftAssetsProvider;
public class DownloadAssetsTask extends AbstractLoomTask {
@ -40,6 +39,5 @@ public class DownloadAssetsTask extends AbstractLoomTask {
LoomGradleExtension extension = getExtension();
MinecraftAssetsProvider.provide(extension.getMinecraftProvider(), project);
MinecraftNativesProvider.provide(project);
}
}

View File

@ -0,0 +1,50 @@
/*
* This file is part of fabric-loom, licensed under the MIT License (MIT).
*
* Copyright (c) 2021 FabricMC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package net.fabricmc.loom.task;
import java.io.File;
import javax.inject.Inject;
import org.gradle.api.tasks.Sync;
import net.fabricmc.loom.LoomGradleExtension;
import net.fabricmc.loom.util.Constants;
public abstract class ExtractNativesTask extends Sync {
@Inject
public ExtractNativesTask() {
// Is there a lazy way to do this for many files? - Doesnt seem there is...
for (File nativeFile : getProject().getConfigurations().getByName(Constants.Configurations.MINECRAFT_NATIVES).getFiles()) {
from(getProject().zipTree(nativeFile), copySpec -> {
copySpec.exclude("META-INF/**");
});
}
into(LoomGradleExtension.get(getProject()).getFiles().getNativesDirectory(getProject()));
setDescription("Downloads and extracts the minecraft natives");
}
}

View File

@ -43,6 +43,7 @@ public final class LoomTasks {
public static void registerTasks(Project project) {
TaskContainer tasks = project.getTasks();
LoomGradleExtension extension = LoomGradleExtension.get(project);
tasks.register("migrateMappings", MigrateMappingsTask.class, t -> {
t.setDescription("Migrates mappings to a new version.");
@ -54,7 +55,11 @@ public final class LoomTasks {
t.setGroup(Constants.TaskGroup.FABRIC);
});
tasks.register("downloadAssets", DownloadAssetsTask.class, t -> t.setDescription("Downloads required assets for Fabric."));
TaskProvider<ExtractNativesTask> extractNatives = tasks.register("extractNatives", ExtractNativesTask.class);
tasks.register("downloadAssets", DownloadAssetsTask.class, t -> {
t.dependsOn(extractNatives);
t.setDescription("Downloads required assets for Fabric.");
});
tasks.register("remapSourcesJar", RemapSourcesJarTask.class, t -> t.setDescription("Remaps the project sources jar to intermediary names."));
TaskProvider<ValidateAccessWidenerTask> validateAccessWidener = tasks.register("validateAccessWidener", ValidateAccessWidenerTask.class, t -> {

View File

@ -42,7 +42,7 @@ import org.gradle.api.IllegalDependencyNotation;
import org.gradle.api.JavaVersion;
import org.gradle.api.Project;
import org.gradle.api.artifacts.Dependency;
import org.gradle.api.plugins.JavaPluginConvention;
import org.gradle.api.plugins.JavaPluginExtension;
import org.gradle.api.tasks.TaskAction;
import org.gradle.api.tasks.options.Option;
@ -171,12 +171,7 @@ public class MigrateMappingsTask extends AbstractLoomTask {
project.getLogger().lifecycle(":remapping");
Mercury mercury = SourceRemapper.createMercuryWithClassPath(project, false);
final JavaPluginConvention convention = project.getConvention().findPlugin(JavaPluginConvention.class);
final JavaVersion javaVersion = convention != null
?
convention.getSourceCompatibility()
:
JavaVersion.current();
final JavaVersion javaVersion = project.getExtensions().getByType(JavaPluginExtension.class).getSourceCompatibility();
mercury.setSourceCompatibility(javaVersion.toString());
mercury.getClassPath().add(minecraftMappedProvider.getMappedJar().toPath());

View File

@ -124,7 +124,7 @@ public class RemapJarTask extends Jar {
Project project = getProject();
LoomGradleExtension extension = LoomGradleExtension.get(getProject());
Path input = this.getInput().getAsFile().get().toPath();
Path output = this.getArchivePath().toPath();
Path output = this.getArchiveFile().get().getAsFile().toPath();
if (!Files.exists(input)) {
throw new FileNotFoundException(input.toString());

View File

@ -0,0 +1,47 @@
/*
* This file is part of fabric-loom, licensed under the MIT License (MIT).
*
* Copyright (c) 2021 FabricMC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package net.fabricmc.loom.util;
public class Architecture {
public static final Architecture CURRENT = new Architecture(System.getProperty("os.arch"));
private final String name;
public Architecture(String name) {
this.name = name;
}
public boolean is64Bit() {
return name.contains("64") || name.startsWith("armv8");
}
public boolean isArm() {
return name.startsWith("arm") || name.startsWith("aarch64");
}
public String getName() {
return name;
}
}

View File

@ -0,0 +1,36 @@
/*
* This file is part of fabric-loom, licensed under the MIT License (MIT).
*
* Copyright (c) 2021 FabricMC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package net.fabricmc.loom.util;
import groovy.lang.Closure;
import org.gradle.api.Action;
public record ClosureAction<T>(Closure closure) implements Action<T> {
@Override
public void execute(T t) {
closure.setDelegate(t);
closure.call(t);
}
}

View File

@ -40,8 +40,6 @@ public class Constants {
public static final String EXPERIMENTAL_VERSIONS = "https://maven.fabricmc.net/net/minecraft/experimental_versions.json";
public static final String FABRIC_REPOSITORY = "https://maven.fabricmc.net/";
public static final String SYSTEM_ARCH = System.getProperty("os.arch").equals("64") ? "64" : "32";
public static final int ASM_VERSION = Opcodes.ASM9;
public static final List<RemappedConfigurationEntry> MOD_COMPILE_ENTRIES = ImmutableList.of(
@ -64,8 +62,13 @@ public class Constants {
public static final String MOD_COMPILE_CLASSPATH_MAPPED = "modCompileClasspathMapped";
public static final String INCLUDE = "include";
public static final String MINECRAFT = "minecraft";
/**
* The server specific configuration will be empty when using a legacy (pre 21w38a server jar)
* find the client only dependencies on the "minecraftLibraries" config.
*/
public static final String MINECRAFT_SERVER_DEPENDENCIES = "minecraftServerLibraries";
public static final String MINECRAFT_DEPENDENCIES = "minecraftLibraries";
public static final String MINECRAFT_REMAP_CLASSPATH = "minecraftRemapClasspath";
public static final String MINECRAFT_NATIVES = "minecraftNatives";
public static final String MINECRAFT_NAMED = "minecraftNamed";
public static final String MAPPINGS = "mappings";
public static final String MAPPINGS_FINAL = "mappingsFinal";
@ -93,6 +96,7 @@ public class Constants {
public static final String DEV_LAUNCH_INJECTOR = "net.fabricmc:dev-launch-injector:";
public static final String TERMINAL_CONSOLE_APPENDER = "net.minecrell:terminalconsoleappender:";
public static final String JETBRAINS_ANNOTATIONS = "org.jetbrains:annotations:";
public static final String NATIVE_SUPPORT = "net.fabricmc:fabric-loom-native-support:";
private Dependencies() {
}
@ -105,6 +109,7 @@ public class Constants {
public static final String DEV_LAUNCH_INJECTOR = "0.2.1+build.8";
public static final String TERMINAL_CONSOLE_APPENDER = "1.2.0";
public static final String JETBRAINS_ANNOTATIONS = "23.0.0";
public static final String NATIVE_SUPPORT_VERSION = "1.0.1";
private Versions() {
}

View File

@ -28,9 +28,12 @@ import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.nio.file.FileSystem;
import java.nio.file.FileSystemAlreadyExistsException;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.util.Collections;
import java.util.Map;
@ -38,6 +41,20 @@ import java.util.function.Supplier;
public final class FileSystemUtil {
public record Delegate(FileSystem fs, boolean owner) implements AutoCloseable, Supplier<FileSystem> {
public byte[] readAllBytes(String path) throws IOException {
Path fsPath = get().getPath(path);
if (Files.exists(fsPath)) {
return Files.readAllBytes(fsPath);
} else {
throw new NoSuchFileException(fsPath.toString());
}
}
public String readString(String path) throws IOException {
return new String(readAllBytes(path), StandardCharsets.UTF_8);
}
@Override
public void close() throws IOException {
if (owner) {
@ -65,6 +82,10 @@ public final class FileSystemUtil {
return getJarFileSystem(path.toUri(), create);
}
public static Delegate getJarFileSystem(Path path) throws IOException {
return getJarFileSystem(path, false);
}
public static Delegate getJarFileSystem(URI uri, boolean create) throws IOException {
URI jarUri;

View File

@ -25,18 +25,12 @@
package net.fabricmc.loom.util;
import java.io.File;
import java.io.IOException;
import java.util.zip.ZipFile;
public final class ModUtils {
private ModUtils() {
}
public static boolean isMod(File input) {
try (ZipFile zipFile = new ZipFile(input)) {
return zipFile.getEntry("fabric.mod.json") != null;
} catch (IOException e) {
return false;
}
return ZipUtils.contains(input.toPath(), "fabric.mod.json");
}
}

View File

@ -30,23 +30,21 @@ import java.net.StandardProtocolFamily;
import java.nio.channels.ServerSocketChannel;
public class OperatingSystem {
public static String getOS() {
public static final String WINDOWS = "windows";
public static final String MAC_OS = "osx";
public static final String LINUX = "linux";
public static final String CURRENT_OS = getOS();
private static String getOS() {
String osName = System.getProperty("os.name").toLowerCase();
if (osName.contains("win")) {
return "windows";
return WINDOWS;
} else if (osName.contains("mac")) {
return "osx";
return MAC_OS;
} else {
return "linux";
}
}
public static String getArch() {
if (is64Bit()) {
return "64";
} else {
return "32";
return LINUX;
}
}
@ -54,10 +52,6 @@ public class OperatingSystem {
return System.getProperty("sun.arch.data.model").contains("64");
}
public static boolean isWindows() {
return getOS().equals("windows");
}
public static boolean isCIBuild() {
String loomProperty = System.getProperty("fabric.loom.ci");

View File

@ -83,13 +83,7 @@ public class ZipUtils {
public static byte[] unpack(Path zip, String path) throws IOException {
try (FileSystemUtil.Delegate fs = FileSystemUtil.getJarFileSystem(zip, false)) {
Path fsPath = fs.get().getPath(path);
if (Files.exists(fsPath)) {
return Files.readAllBytes(fsPath);
} else {
throw new NoSuchFileException(fsPath.toString());
}
return fs.readAllBytes(path);
}
}

View File

@ -28,7 +28,7 @@ import org.gradle.util.GradleVersion
class LoomTestConstants {
public final static String DEFAULT_GRADLE = GradleVersion.current().getVersion()
public final static String PRE_RELEASE_GRADLE = "7.4-20211203231050+0000"
public final static String PRE_RELEASE_GRADLE = "7.4-20211216231505+0000"
public final static String[] STANDARD_TEST_VERSIONS = [DEFAULT_GRADLE, PRE_RELEASE_GRADLE]
}

View File

@ -0,0 +1,86 @@
/*
* This file is part of fabric-loom, licensed under the MIT License (MIT).
*
* Copyright (c) 2021 FabricMC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package net.fabricmc.loom.test.integration
import net.fabricmc.loom.test.util.GradleProjectTestTrait
import spock.lang.Specification
import spock.lang.Unroll
import spock.util.environment.RestoreSystemProperties
import static net.fabricmc.loom.test.LoomTestConstants.STANDARD_TEST_VERSIONS
import static org.gradle.testkit.runner.TaskOutcome.SUCCESS
import static org.gradle.testkit.runner.TaskOutcome.UP_TO_DATE
class NativesTest extends Specification implements GradleProjectTestTrait {
@Unroll
def "Default natives for 1.18 (gradle #version)"() {
setup:
def gradle = gradleProject(project: "minimalBase", version: version)
gradle.buildGradle << '''
dependencies {
minecraft "com.mojang:minecraft:1.18"
mappings loom.officialMojangMappings()
}
'''
when:
// Run the task twice to ensure its up to date
def result = gradle.run(task: "extractNatives")
def result2 = gradle.run(task: "extractNatives")
then:
result.task(":extractNatives").outcome == SUCCESS
result2.task(":extractNatives").outcome == UP_TO_DATE
where:
version << STANDARD_TEST_VERSIONS
}
@RestoreSystemProperties
@Unroll
def "Overridden natives for 1.18 (gradle #version)"() {
setup:
System.setProperty('loom.test.lwjgloverride', "true")
def gradle = gradleProject(project: "minimalBase", version: version)
gradle.buildGradle << '''
dependencies {
minecraft "com.mojang:minecraft:1.18"
mappings loom.officialMojangMappings()
}
'''
when:
// Run the task twice to ensure its up to date
def result = gradle.run(task: "extractNatives")
then:
result.task(":extractNatives").outcome == SUCCESS
where:
version << STANDARD_TEST_VERSIONS
}
}

View File

@ -30,29 +30,29 @@ import net.fabricmc.loom.configuration.providers.mappings.LayeredMappingSpecBuil
import net.fabricmc.loom.configuration.providers.mappings.intermediary.IntermediaryMappingsSpec
import net.fabricmc.loom.configuration.providers.mappings.mojmap.MojangMappingsSpec
import net.fabricmc.loom.configuration.providers.mappings.parchment.ParchmentMappingsSpec
import org.gradle.api.Action
import org.gradle.util.ConfigureUtil
import net.fabricmc.loom.util.ClosureAction
import spock.lang.Specification
class LayeredMappingSpecBuilderTest extends Specification {
def "simple mojmap" () {
when:
def spec = layered() {
def spec = layered {
officialMojangMappings()
}
def layers = spec.layers()
then:
spec.version == "layered+hash.2198"
layers.size() == 2
spec.version == "layered+hash.2198"
layers[0].class == IntermediaryMappingsSpec
layers[1].class == MojangMappingsSpec
}
def "simple mojmap with parchment" () {
when:
def dep = "I like cake"
def spec = layered() {
officialMojangMappings()
parchment("I like cake")
parchment(dep)
}
def layers = spec.layers()
def parchment = layers[2] as ParchmentMappingsSpec
@ -88,7 +88,7 @@ class LayeredMappingSpecBuilderTest extends Specification {
def "simple mojmap with parchment keep prefix alternate hash" () {
when:
def spec = layered() {
def spec = layered {
officialMojangMappings()
parchment("I really like cake") {
it.removePrefix = false
@ -106,14 +106,9 @@ class LayeredMappingSpecBuilderTest extends Specification {
parchment.removePrefix() == false
}
// Gradle does this big of magic behind the scenes
LayeredMappingSpec layered(@DelegatesTo(LayeredMappingSpecBuilderImpl) Closure cl) {
return layeredAction(ConfigureUtil.configureUsing(cl))
}
LayeredMappingSpec layeredAction(Action<LayeredMappingSpecBuilderImpl> action) {
LayeredMappingSpecBuilderImpl builder = new LayeredMappingSpecBuilderImpl()
action.execute(builder)
new ClosureAction(cl).execute(builder)
return builder.build()
}
}