|
| 1 | +def buildValues = project.ext.oreSpawnBuild |
| 2 | +def trackedDocumentationFiles = buildValues.trackedDocumentationFiles |
| 3 | +def documentationRelativePath = buildValues.documentationRelativePath |
| 4 | + |
| 5 | +// These Eclipse shortcuts run the same Gradle world and client checks used on |
| 6 | +// the command line, each in its own disposable directory. |
| 7 | +task syncForge12EclipseIntegrationLaunches { |
| 8 | + group = 'ide' |
| 9 | + doLast { |
| 10 | + def writeGradleLaunch = { String fileName, String displayName, String arguments -> |
| 11 | + File launch = file(fileName) |
| 12 | + String escapedArguments = arguments |
| 13 | + .replace('&', '&') |
| 14 | + .replace('"', '"') |
| 15 | + launch.setText("""<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?> |
| 16 | +<launchConfiguration type=\"org.eclipse.ui.externaltools.ProgramLaunchConfigurationType\"> |
| 17 | +<stringAttribute key=\"org.eclipse.debug.core.ATTR_REFRESH_SCOPE\" value=\"${'$'}{workspace}\"/> |
| 18 | +<stringAttribute key=\"org.eclipse.ui.externaltools.ATTR_LOCATION\" value=\"${'$'}{project_loc}\\gradlew.bat\"/> |
| 19 | +<stringAttribute key=\"org.eclipse.ui.externaltools.ATTR_TOOL_ARGUMENTS\" value=\"${escapedArguments}\"/> |
| 20 | +<stringAttribute key=\"org.eclipse.ui.externaltools.ATTR_WORKING_DIRECTORY\" value=\"${'$'}{project_loc}\"/> |
| 21 | +<stringAttribute key=\"org.eclipse.ui.externaltools.ATTR_LAUNCH_CONFIGURATION_BUILD_SCOPE\" value=\"${'$'}{project}\"/> |
| 22 | +<stringAttribute key=\"org.eclipse.ui.externaltools.ATTR_NAME\" value=\"${displayName}\"/> |
| 23 | +</launchConfiguration> |
| 24 | +""", 'UTF-8') |
| 25 | + } |
| 26 | + |
| 27 | + writeGradleLaunch('OreSpawn_Surface_FreshReload.launch', |
| 28 | + 'OreSpawn surface fresh/reload gate', |
| 29 | + 'surfaceIntegrationTest --offline --no-daemon') |
| 30 | + writeGradleLaunch('OreSpawn_Migration_Fresh.launch', |
| 31 | + 'OreSpawn migration fresh gate', |
| 32 | + 'migrationIntegrationProcess --offline --no-daemon ' + |
| 33 | + '-PmigrationRunDir=build/eclipse-migration-run ' + |
| 34 | + '-PmigrationFamily=eclipse-manual -PmigrationPhase=fresh') |
| 35 | + writeGradleLaunch('OreSpawn_Migration_Reload.launch', |
| 36 | + 'OreSpawn migration reload gate', |
| 37 | + 'migrationIntegrationProcess --offline --no-daemon ' + |
| 38 | + '-PmigrationRunDir=build/eclipse-migration-run ' + |
| 39 | + '-PmigrationFamily=eclipse-manual -PmigrationPhase=reload') |
| 40 | + writeGradleLaunch('OreSpawn_Client_FreshReload.launch', |
| 41 | + 'OreSpawn client world and editor fresh/reload gate', |
| 42 | + 'clientIntegrationProcess --offline --no-daemon') |
| 43 | + } |
| 44 | +} |
| 45 | + |
| 46 | +tasks.named('genEclipseRuns') { |
| 47 | + finalizedBy syncForge12EclipseIntegrationLaunches |
| 48 | +} |
| 49 | + |
| 50 | + |
| 51 | +// Eclipse owns bin/main, but it still needs Gradle's expanded mcmod.info and |
| 52 | +// bundled guides there before a development launch can see the real mod. |
| 53 | +def prepareEclipseResources = tasks.register('prepareEclipseResources') { |
| 54 | + group = 'ide' |
| 55 | + description = 'Copies Gradle-processed production resources into Eclipse merged output.' |
| 56 | + dependsOn tasks.named('processResources') |
| 57 | + doLast { |
| 58 | + // Do not declare bin/main as a Gradle-owned output. Eclipse owns that |
| 59 | + // directory and ForgeGradle inspects it while generating launches. |
| 60 | + project.copy { |
| 61 | + from(layout.buildDirectory.dir('resources/main')) |
| 62 | + into(layout.projectDirectory.dir('bin/main')) |
| 63 | + } |
| 64 | + } |
| 65 | +} |
| 66 | + |
| 67 | + |
| 68 | +// Older ForgeGradle workspaces can retain GradleStart launchers alongside the |
| 69 | +// ForgeGradle 7 Buildship launches. Remove only those exact obsolete launch |
| 70 | +// types; leave independently maintained launch files untouched. |
| 71 | +def obsoleteForgeGradleEclipseLaunches = [ |
| 72 | + 'OreSpawn_Client.launch': 'GradleStart', |
| 73 | + 'OreSpawn_Server.launch': 'GradleStartServer' |
| 74 | +] |
| 75 | + |
| 76 | +eclipse { |
| 77 | + classpath { |
| 78 | + downloadSources = true |
| 79 | + downloadJavadoc = true |
| 80 | + } |
| 81 | + synchronizationTasks 'isolateEclipseProductionRuns' |
| 82 | +} |
| 83 | + |
| 84 | +// Point Buildship at this project's wrapper and the cache used by Gradle runs. |
| 85 | +tasks.register('configureEclipseBuildship') { |
| 86 | + group = 'ide' |
| 87 | + description = 'Creates the Buildship project preferences used by ForgeGradle 7 imports.' |
| 88 | + doLast { |
| 89 | + File preferencesFile = file('.settings/org.eclipse.buildship.core.prefs') |
| 90 | + Properties preferences = new Properties() |
| 91 | + Map<String, String> requiredPreferences = [ |
| 92 | + 'eclipse.preferences.version' : '1', |
| 93 | + 'connection.gradle.distribution': 'GRADLE_DISTRIBUTION(WRAPPER)', |
| 94 | + 'connection.gradle.user.home' : gradle.gradleUserHomeDir.canonicalPath, |
| 95 | + 'connection.project.dir' : '', |
| 96 | + 'gradle.user.home' : gradle.gradleUserHomeDir.canonicalPath, |
| 97 | + 'override.workspace.settings' : 'true' |
| 98 | + ] |
| 99 | + requiredPreferences.each { key, value -> preferences.setProperty(key, value) } |
| 100 | + preferencesFile.parentFile.mkdirs() |
| 101 | + preferencesFile.withOutputStream { |
| 102 | + preferences.store(it, |
| 103 | + 'Generated by configureEclipseBuildship; keep Eclipse and command-line caches aligned.') |
| 104 | + } |
| 105 | + } |
| 106 | +} |
| 107 | + |
| 108 | +// ForgeGradle 7 generates a few launch values that Forge 1.12 cannot use as-is. |
| 109 | +// Fix those generated files and keep test code out of ordinary client/server runs. |
| 110 | +tasks.register('isolateEclipseProductionRuns') { |
| 111 | + group = 'ide' |
| 112 | + description = 'Normalizes ForgeGradle 7 Eclipse launches and marks ordinary launches as production-only.' |
| 113 | + dependsOn tasks.named('genEclipseRuns') |
| 114 | + dependsOn tasks.named('configureEclipseBuildship') |
| 115 | + dependsOn prepareEclipseResources |
| 116 | + |
| 117 | + doLast { |
| 118 | + obsoleteForgeGradleEclipseLaunches.each { String name, String mainClass -> |
| 119 | + File launch = file(name) |
| 120 | + if (launch.isFile()) { |
| 121 | + String contents = launch.getText('UTF-8') |
| 122 | + String obsoleteMainType = |
| 123 | + "org.eclipse.jdt.launching.MAIN_TYPE\" value=\"${mainClass}\"" |
| 124 | + if (contents.contains(obsoleteMainType) && !launch.delete()) { |
| 125 | + throw new GradleException("Could not remove obsolete ForgeGradle launch ${name}") |
| 126 | + } |
| 127 | + } |
| 128 | + } |
| 129 | + |
| 130 | + // ForgeGradle 7's legacy 1.12 run generator does not know its own |
| 131 | + // MC_VERSION token. It emits ${MC_VERSION} as an Eclipse variable, |
| 132 | + // which prevents every generated launch from starting. Store the |
| 133 | + // target version as a literal environment value instead. |
| 134 | + fileTree(project.projectDir) { |
| 135 | + include 'run*.launch' |
| 136 | + }.files.each { File launch -> |
| 137 | + String contents = launch.getText('UTF-8') |
| 138 | + contents = contents.replace( |
| 139 | + 'key="MC_VERSION" value="${MC_VERSION}"', |
| 140 | + "key=\"MC_VERSION\" value=\"${minecraft_version}\"") |
| 141 | + contents = contents.replace( |
| 142 | + 'key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="--nogui"', |
| 143 | + 'key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="nogui"') |
| 144 | + launch.setText(contents.replace('\r\n', '\n'), 'UTF-8') |
| 145 | + } |
| 146 | + |
| 147 | + ['runClient.launch', 'runServer.launch'].each { name -> |
| 148 | + File launch = file(name) |
| 149 | + if (!launch.isFile()) { |
| 150 | + throw new GradleException("ForgeGradle did not generate ${name}") |
| 151 | + } |
| 152 | + String contents = launch.getText('UTF-8') |
| 153 | + if (!contents.contains('org.eclipse.jdt.launching.ATTR_EXCLUDE_TEST_CODE')) { |
| 154 | + String marker = '</launchConfiguration>' |
| 155 | + String attribute = |
| 156 | + ' <booleanAttribute key="org.eclipse.jdt.launching.ATTR_EXCLUDE_TEST_CODE" value="true"/>' |
| 157 | + contents = contents.replace(marker, attribute + '\n' + marker) |
| 158 | + } |
| 159 | + launch.setText(contents.replace('\r\n', '\n'), 'UTF-8') |
| 160 | + } |
| 161 | + } |
| 162 | +} |
| 163 | + |
| 164 | +// Check the resulting Eclipse files themselves; a successful Gradle build does |
| 165 | +// not prove that an IDE launch has the right resources or classpath. |
| 166 | +tasks.register('verifyEclipseProductionClasspath') { |
| 167 | + group = 'verification' |
| 168 | + description = 'Verifies that ordinary generated Eclipse launches exclude tests, fixtures, and probe mods.' |
| 169 | + dependsOn tasks.named('isolateEclipseProductionRuns') |
| 170 | + dependsOn tasks.named('verifyLegacyOracleIsolation') |
| 171 | + |
| 172 | + doLast { |
| 173 | + File buildshipPreferences = file('.settings/org.eclipse.buildship.core.prefs') |
| 174 | + if (!buildshipPreferences.isFile()) { |
| 175 | + throw new GradleException('Missing Eclipse Buildship project preferences') |
| 176 | + } |
| 177 | + File eclipseResources = file('bin/main') |
| 178 | + List<File> trackedDocs = trackedDocumentationFiles() |
| 179 | + if (trackedDocs.size() != 20) { |
| 180 | + throw new GradleException("Expected 20 tracked documentation files, found ${trackedDocs.size()}") |
| 181 | + } |
| 182 | + trackedDocs.each { File source -> |
| 183 | + String relative = documentationRelativePath(source) |
| 184 | + File eclipseCopy = new File(eclipseResources, "META-INF/orespawn/docs/${relative}") |
| 185 | + if (!eclipseCopy.isFile() |
| 186 | + || !java.util.Arrays.equals(source.bytes, eclipseCopy.bytes)) { |
| 187 | + throw new GradleException("Eclipse documentation differs for ${relative}") |
| 188 | + } |
| 189 | + } |
| 190 | + [ |
| 191 | + 'mcmod.info', |
| 192 | + 'META-INF/orespawn/docs/README.md', |
| 193 | + 'META-INF/orespawn/docs/VERSIONS.md' |
| 194 | + ].each { relative -> |
| 195 | + File required = new File(eclipseResources, relative) |
| 196 | + if (!required.isFile()) { |
| 197 | + throw new GradleException( |
| 198 | + "Eclipse production output is missing processed resource ${relative}") |
| 199 | + } |
| 200 | + } |
| 201 | + String eclipseMetadata = new File(eclipseResources, 'mcmod.info').getText('UTF-8') |
| 202 | + if (!eclipseMetadata.contains("\"version\": \"${project.version}\"") |
| 203 | + || !eclipseMetadata.contains( |
| 204 | + "\"mcversion\": \"${minecraft_version}\"")) { |
| 205 | + throw new GradleException('Eclipse mcmod.info retains unexpanded build placeholders') |
| 206 | + } |
| 207 | + List<File> launchFiles = fileTree(project.projectDir) { |
| 208 | + include 'runClient.launch' |
| 209 | + include 'runServer.launch' |
| 210 | + include '.eclipse/runClient.launch' |
| 211 | + include '.eclipse/runServer.launch' |
| 212 | + }.files as List<File> |
| 213 | + if (launchFiles.size() < 2) { |
| 214 | + throw new GradleException('ForgeGradle did not generate ordinary client/server Eclipse launches') |
| 215 | + } |
| 216 | + List<File> allGeneratedLaunches = fileTree(project.projectDir) { |
| 217 | + include 'run*.launch' |
| 218 | + }.files as List<File> |
| 219 | + allGeneratedLaunches.each { launch -> |
| 220 | + String contents = launch.getText('UTF-8') |
| 221 | + if (contents.contains('--nogui')) { |
| 222 | + throw new GradleException( |
| 223 | + "${launch.name} uses invalid legacy server argument --nogui; expected bare nogui") |
| 224 | + } |
| 225 | + if (contents.contains('${MC_VERSION}')) { |
| 226 | + throw new GradleException( |
| 227 | + "${launch.name} retains ForgeGradle's unresolved MC_VERSION token") |
| 228 | + } |
| 229 | + if (!contents.contains( |
| 230 | + "key=\"MC_VERSION\" value=\"${minecraft_version}\"")) { |
| 231 | + throw new GradleException( |
| 232 | + "${launch.name} does not define literal MC_VERSION=${minecraft_version}") |
| 233 | + } |
| 234 | + } |
| 235 | + obsoleteForgeGradleEclipseLaunches.each { String name, String mainClass -> |
| 236 | + File launch = file(name) |
| 237 | + if (launch.isFile() && launch.getText('UTF-8').contains( |
| 238 | + "org.eclipse.jdt.launching.MAIN_TYPE\" value=\"${mainClass}\"")) { |
| 239 | + throw new GradleException( |
| 240 | + "Obsolete ForgeGradle ${mainClass} launch remains at ${name}") |
| 241 | + } |
| 242 | + } |
| 243 | + List<String> forbidden = [ |
| 244 | + 'src/test', 'bin/test', 'build/classes/java/test', |
| 245 | + 'biomeIntegrationTest', 'migrationIntegrationTest', 'clientIntegrationTest', |
| 246 | + 'os1AbiFixture', 'os3AbiFixture', 'surfaceprobe', 'migrationprobe', 'clientprobe', |
| 247 | + 'OreSourceEditorProbeBridge', 'junit-', 'opentest4j-', 'junit-platform-', |
| 248 | + 'Mineralogy-1.10.2-3.3.8.26.jar', 'Mineralogy-1.12.2-3.8.0.53.jar' |
| 249 | + ] |
| 250 | + launchFiles.each { launch -> |
| 251 | + String contents = launch.getText('UTF-8').replace('\\', '/') |
| 252 | + List<String> present = forbidden.findAll { contents.contains(it) } |
| 253 | + if (!present.isEmpty()) { |
| 254 | + throw new GradleException("${launch.name} exposes test code/dependencies: ${present}") |
| 255 | + } |
| 256 | + if (!contents.contains('org.eclipse.jdt.launching.ATTR_EXCLUDE_TEST_CODE') |
| 257 | + || !contents.contains('value="true"')) { |
| 258 | + throw new GradleException("${launch.name} does not exclude test code") |
| 259 | + } |
| 260 | + if (!contents.contains('PROJECT_ATTR" value="OreSpawn"')) { |
| 261 | + throw new GradleException("${launch.name} targets the wrong Eclipse project") |
| 262 | + } |
| 263 | + } |
| 264 | + } |
| 265 | +} |
0 commit comments