Commits
md_5 authored 032e8ca0a3a
1 + | package org.spigotmc.builder; |
2 + | |
3 + | import com.google.common.base.Charsets; |
4 + | import com.google.common.base.Predicate; |
5 + | import com.google.common.base.Throwables; |
6 + | import com.google.common.collect.Iterables; |
7 + | import com.google.common.hash.Hasher; |
8 + | import com.google.common.hash.Hashing; |
9 + | import com.google.common.io.ByteStreams; |
10 + | import com.google.common.io.Files; |
11 + | import com.google.common.io.Resources; |
12 + | import difflib.DiffUtils; |
13 + | import difflib.Patch; |
14 + | import java.io.BufferedReader; |
15 + | import java.io.BufferedWriter; |
16 + | import java.io.File; |
17 + | import java.io.FileOutputStream; |
18 + | import java.io.FileWriter; |
19 + | import java.io.IOException; |
20 + | import java.io.InputStream; |
21 + | import java.io.InputStreamReader; |
22 + | import java.io.OutputStream; |
23 + | import java.io.PrintStream; |
24 + | import java.net.URL; |
25 + | import java.util.Date; |
26 + | import java.util.Enumeration; |
27 + | import java.util.List; |
28 + | import java.util.zip.ZipEntry; |
29 + | import java.util.zip.ZipFile; |
30 + | import lombok.RequiredArgsConstructor; |
31 + | import org.apache.commons.io.FileUtils; |
32 + | import org.eclipse.jgit.api.Git; |
33 + | import org.eclipse.jgit.api.ResetCommand; |
34 + | import org.eclipse.jgit.api.errors.GitAPIException; |
35 + | import org.eclipse.jgit.revwalk.RevCommit; |
36 + | |
37 + | public class Builder |
38 + | { |
39 + | |
40 + | public static final boolean IS_WINDOWS = System.getProperty( "os.name" ).startsWith( "Windows" ); |
41 + | public static final boolean IS_MAC = System.getProperty( "os.name" ).startsWith( "Mac" ); |
42 + | public static final File CWD = new File( "." ); |
43 + | public static final String MC_VERSION = "1.8"; |
44 + | |
45 + | public static void main(String[] args) throws Exception |
46 + | { |
47 + | if ( IS_MAC ) |
48 + | { |
49 + | System.out.println( "Sorry, but Macintosh is not currently a supported platform for compilation at this time." ); |
50 + | System.out.println( "Please run this script on a Windows or Linux PC and then copy the jars to this computer." ); |
51 + | return; |
52 + | } |
53 + | |
54 + | try |
55 + | { |
56 + | runProcess( "bash -c exit", CWD ); |
57 + | } catch ( Exception ex ) |
58 + | { |
59 + | System.out.println( "You must run this jar through bash (msysgit)" ); |
60 + | return; |
61 + | } |
62 + | |
63 + | try |
64 + | { |
65 + | runProcess( "git config --global user.name", CWD ); |
66 + | } catch ( Exception ex ) |
67 + | { |
68 + | System.out.println( "Git name not set, setting it to default value." ); |
69 + | runProcess( "git config --global user.name BuildTools", CWD ); |
70 + | } |
71 + | try |
72 + | { |
73 + | runProcess( "git config --global user.email", CWD ); |
74 + | } catch ( Exception ex ) |
75 + | { |
76 + | System.out.println( "Git email not set, setting it to default value." ); |
77 + | runProcess( "git config --global user.email unconfigured@null.spigotmc.org", CWD ); |
78 + | } |
79 + | |
80 + | File workDir = new File( "work" ); |
81 + | workDir.mkdir(); |
82 + | |
83 + | File bukkit = new File( "Bukkit" ); |
84 + | if ( !bukkit.exists() ) |
85 + | { |
86 + | clone( "https://hub.spigotmc.org/stash/scm/spigot/bukkit.git", bukkit ); |
87 + | } |
88 + | |
89 + | File craftBukkit = new File( "CraftBukkit" ); |
90 + | if ( !craftBukkit.exists() ) |
91 + | { |
92 + | clone( "https://hub.spigotmc.org/stash/scm/spigot/craftbukkit.git", craftBukkit ); |
93 + | } |
94 + | |
95 + | File spigot = new File( "Spigot" ); |
96 + | if ( !spigot.exists() ) |
97 + | { |
98 + | clone( "https://hub.spigotmc.org/stash/scm/spigot/spigot.git", spigot ); |
99 + | } |
100 + | |
101 + | File buildData = new File( "BuildData" ); |
102 + | if ( !buildData.exists() ) |
103 + | { |
104 + | clone( "https://hub.spigotmc.org/stash/scm/spigot/builddata.git", buildData ); |
105 + | } |
106 + | |
107 + | File maven = new File( "apache-maven-3.2.3" ); |
108 + | if ( !maven.exists() ) |
109 + | { |
110 + | System.out.println( "Maven does not exist, downloading. Please wait." ); |
111 + | |
112 + | File mvnTemp = new File( "mvn.zip" ); |
113 + | mvnTemp.deleteOnExit(); |
114 + | |
115 + | download( "http://static.spigotmc.org/maven/apache-maven-3.2.3-bin.zip", mvnTemp ); |
116 + | unzip( mvnTemp, new File( "." ) ); |
117 + | } |
118 + | |
119 + | String mvnCmd = maven.getAbsolutePath() + "/bin/mvn"; |
120 + | if ( IS_WINDOWS ) |
121 + | { |
122 + | mvnCmd += ".bat"; |
123 + | } else |
124 + | { |
125 + | mvnCmd = "/bin/sh " + mvnCmd; |
126 + | } |
127 + | |
128 + | Git bukkitGit = Git.open( bukkit ); |
129 + | Git craftBukkitGit = Git.open( craftBukkit ); |
130 + | Git spigotGit = Git.open( spigot ); |
131 + | Git buildGit = Git.open( buildData ); |
132 + | |
133 + | pull( bukkitGit ); |
134 + | pull( craftBukkitGit ); |
135 + | pull( spigotGit ); |
136 + | pull( buildGit ); |
137 + | |
138 + | File vanillaJar = new File( workDir, "minecraft_server." + MC_VERSION + ".jar" ); |
139 + | if ( !vanillaJar.exists() ) |
140 + | { |
141 + | download( String.format( "https://s3.amazonaws.com/Minecraft.Download/versions/%1$s/minecraft_server.%1$s.jar", MC_VERSION ), vanillaJar ); |
142 + | } |
143 + | |
144 + | Iterable<RevCommit> mappings = buildGit.log() |
145 + | .addPath( "mappings/bukkit-1.8.at" ) |
146 + | .addPath( "mappings/bukkit-1.8-cl.csrg" ) |
147 + | .addPath( "mappings/bukkit-1.8-members.csrg" ) |
148 + | .addPath( "mappings/package.srg" ) |
149 + | .setMaxCount( 1 ).call(); |
150 + | |
151 + | Hasher mappingsHash = Hashing.md5().newHasher(); |
152 + | for ( RevCommit rev : mappings ) |
153 + | { |
154 + | mappingsHash.putString( rev.getName(), Charsets.UTF_8 ); |
155 + | } |
156 + | String mappingsVersion = mappingsHash.hash().toString().substring( 24 ); // Last 8 chars |
157 + | |
158 + | File finalMappedJar = new File( workDir, "mapped." + mappingsVersion + ".jar" ); |
159 + | if ( !finalMappedJar.exists() ) |
160 + | { |
161 + | System.out.println( "Final mapped jar: " + finalMappedJar + " does not exist, creating!" ); |
162 + | |
163 + | File clMappedJar = new File( finalMappedJar + "-cl" ); |
164 + | File mMappedJar = new File( finalMappedJar + "-m" ); |
165 + | |
166 + | runProcess( "java -jar BuildData/bin/SpecialSource.jar -i " + vanillaJar + " -m BuildData/mappings/bukkit-1.8-cl.csrg -o " + clMappedJar, CWD ); |
167 + | runProcess( "java -jar BuildData/bin/SpecialSource-2.jar map -i " + clMappedJar + " -m " + "BuildData/mappings/bukkit-1.8-members.csrg -o " + mMappedJar, CWD ); |
168 + | runProcess( "java -jar BuildData/bin/SpecialSource.jar -i " + mMappedJar + " --access-transformer BuildData/mappings/bukkit-1.8.at " |
169 + | + "-m BuildData/mappings/package.srg -o " + finalMappedJar, CWD ); |
170 + | } |
171 + | |
172 + | runProcess( mvnCmd + " install:install-file -Dfile=" + finalMappedJar + " -Dpackaging=jar -DgroupId=org.spigotmc -DartifactId=minecraft-server -Dversion=1.8-SNAPSHOT", CWD ); |
173 + | |
174 + | File decompileDir = new File( workDir, "decompile-" + mappingsVersion ); |
175 + | if ( !decompileDir.exists() ) |
176 + | { |
177 + | decompileDir.mkdir(); |
178 + | |
179 + | File clazzDir = new File( decompileDir, "classes" ); |
180 + | unzip( finalMappedJar, clazzDir, new Predicate<String>() |
181 + | { |
182 + | |
183 + | |
184 + | public boolean apply(String input) |
185 + | { |
186 + | return input.startsWith( "net/minecraft/server" ); |
187 + | } |
188 + | } ); |
189 + | |
190 + | runProcess( "java -jar BuildData/bin/fernflower.jar -dgs=1 -hdc=0 -rbr=0 -asc=1 " + clazzDir + " " + decompileDir, CWD ); |
191 + | |
192 + | String jacobePath = "BuildData/bin/jacobe"; |
193 + | if ( IS_WINDOWS ) |
194 + | { |
195 + | jacobePath += ".exe"; |
196 + | } |
197 + | runProcess( jacobePath + " -cfg=BuildData/bin/jacobe.cfg -nobackup -overwrite -outext=java " + decompileDir + "/net/minecraft/server", CWD ); |
198 + | } |
199 + | |
200 + | System.out.println( "Applying CraftBukkit Patches" ); |
201 + | File nmsDir = new File( craftBukkit, "src/main/java/net" ); |
202 + | if ( nmsDir.exists() ) |
203 + | { |
204 + | System.out.println( "Backing up NMS dir" ); |
205 + | FileUtils.moveDirectory( nmsDir, new File( workDir, "nms.old." + System.currentTimeMillis() ) ); |
206 + | } |
207 + | File patchDir = new File( craftBukkit, "nms-patches" ); |
208 + | for ( File file : patchDir.listFiles() ) |
209 + | { |
210 + | String targetFile = "net/minecraft/server/" + file.getName().replaceAll( ".patch", ".java" ); |
211 + | |
212 + | File clean = new File( decompileDir, targetFile ); |
213 + | File t = new File( nmsDir.getParentFile(), targetFile ); |
214 + | t.getParentFile().mkdirs(); |
215 + | |
216 + | System.out.println( "Patching with " + file.getName() ); |
217 + | |
218 + | Patch parsedPatch = DiffUtils.parseUnifiedDiff( Files.readLines( file, Charsets.UTF_8 ) ); |
219 + | List<?> modifiedLines = DiffUtils.patch( Files.readLines( clean, Charsets.UTF_8 ), parsedPatch ); |
220 + | |
221 + | BufferedWriter bw = new BufferedWriter( new FileWriter( t ) ); |
222 + | for ( String line : (List<String>) modifiedLines ) |
223 + | { |
224 + | bw.write( line ); |
225 + | bw.newLine(); |
226 + | } |
227 + | bw.close(); |
228 + | } |
229 + | File tmpNms = new File( craftBukkit, "tmp-nms" ); |
230 + | FileUtils.copyDirectory( nmsDir, tmpNms ); |
231 + | |
232 + | craftBukkitGit.branchDelete().setBranchNames( "patched" ).setForce( true ).call(); |
233 + | craftBukkitGit.checkout().setCreateBranch( true ).setForce( true ).setName( "patched" ).call(); |
234 + | craftBukkitGit.add().addFilepattern( "src/main/java/net/" ).call(); |
235 + | craftBukkitGit.commit().setMessage( "CraftBukkit $ " + new Date() ).call(); |
236 + | craftBukkitGit.checkout().setName( "master" ).call(); |
237 + | |
238 + | FileUtils.moveDirectory( tmpNms, nmsDir ); |
239 + | |
240 + | File spigotApi = new File( spigot, "Bukkit" ); |
241 + | if ( !spigotApi.exists() ) |
242 + | { |
243 + | clone( "file://" + bukkit.getAbsolutePath(), spigotApi ); |
244 + | } |
245 + | File spigotServer = new File( spigot, "CraftBukkit" ); |
246 + | if ( !spigotServer.exists() ) |
247 + | { |
248 + | clone( "file://" + craftBukkit.getAbsolutePath(), spigotServer ); |
249 + | } |
250 + | |
251 + | // Git spigotApiGit = Git.open( spigotApi ); |
252 + | // Git spigotServerGit = Git.open( spigotServer ); |
253 + | System.out.println( "Compiling Bukkit" ); |
254 + | runProcess( mvnCmd + " clean install", bukkit ); |
255 + | |
256 + | System.out.println( "Compiling CraftBukkit" ); |
257 + | runProcess( mvnCmd + " clean install", craftBukkit ); |
258 + | |
259 + | try |
260 + | { |
261 + | runProcess( "bash applyPatches.sh", spigot ); |
262 + | System.out.println( "*** Spigot patches applied!" ); |
263 + | System.out.println( "Compiling Spigot & Spigot-API" ); |
264 + | runProcess( mvnCmd + " clean install", spigot ); |
265 + | } catch ( Exception ex ) |
266 + | { |
267 + | System.err.println( "Error compiling Spigot, are you running this jar via msysgit?" ); |
268 + | ex.printStackTrace(); |
269 + | } |
270 + | } |
271 + | |
272 + | public static void pull(Git repo) throws Exception |
273 + | { |
274 + | System.out.println( "Pulling updates for " + repo.getRepository().getDirectory() ); |
275 + | |
276 + | repo.reset().setRef( "origin/master" ).setMode( ResetCommand.ResetType.HARD ).call(); |
277 + | boolean result = repo.pull().call().isSuccessful(); |
278 + | |
279 + | if ( !result ) |
280 + | { |
281 + | throw new RuntimeException( "Could not pull updates!" ); |
282 + | } |
283 + | |
284 + | System.out.println( "Successfully pulled updates!" ); |
285 + | } |
286 + | |
287 + | public static int runProcess(String command, File workDir) throws Exception |
288 + | { |
289 + | final Process ps = new ProcessBuilder( command.split( " " ) ).directory( workDir ).start(); |
290 + | |
291 + | new Thread( new StreamRedirector( ps.getInputStream(), System.out ) ).start(); |
292 + | new Thread( new StreamRedirector( ps.getErrorStream(), System.err ) ).start(); |
293 + | |
294 + | int status = ps.waitFor(); |
295 + | |
296 + | if ( status != 0 ) |
297 + | { |
298 + | throw new RuntimeException( "Error running command, return status !=0: " + command ); |
299 + | } |
300 + | |
301 + | return status; |
302 + | } |
303 + | |
304 + | |
305 + | private static class StreamRedirector implements Runnable |
306 + | { |
307 + | |
308 + | private final InputStream in; |
309 + | private final PrintStream out; |
310 + | |
311 + | |
312 + | public void run() |
313 + | { |
314 + | BufferedReader br = new BufferedReader( new InputStreamReader( in ) ); |
315 + | try |
316 + | { |
317 + | String line; |
318 + | while ( ( line = br.readLine() ) != null ) |
319 + | { |
320 + | out.println( line ); |
321 + | } |
322 + | } catch ( IOException ex ) |
323 + | { |
324 + | throw Throwables.propagate( ex ); |
325 + | } |
326 + | } |
327 + | } |
328 + | |
329 + | public static void unzip(File zipFile, File targetFolder) throws IOException |
330 + | { |
331 + | unzip( zipFile, targetFolder, null ); |
332 + | } |
333 + | |
334 + | public static void unzip(File zipFile, File targetFolder, Predicate<String> filter) throws IOException |
335 + | { |
336 + | targetFolder.mkdir(); |
337 + | ZipFile zip = new ZipFile( zipFile ); |
338 + | |
339 + | for ( Enumeration<? extends ZipEntry> entries = zip.entries(); entries.hasMoreElements(); ) |
340 + | { |
341 + | ZipEntry entry = entries.nextElement(); |
342 + | |
343 + | if ( filter != null ) |
344 + | { |
345 + | if ( !filter.apply( entry.getName() ) ) |
346 + | { |
347 + | continue; |
348 + | } |
349 + | } |
350 + | |
351 + | File outFile = new File( targetFolder, entry.getName() ); |
352 + | |
353 + | if ( entry.isDirectory() ) |
354 + | { |
355 + | outFile.mkdirs(); |
356 + | continue; |
357 + | } |
358 + | if ( outFile.getParentFile() != null ) |
359 + | { |
360 + | outFile.getParentFile().mkdirs(); |
361 + | } |
362 + | |
363 + | InputStream is = zip.getInputStream( entry ); |
364 + | OutputStream os = new FileOutputStream( outFile ); |
365 + | try |
366 + | { |
367 + | ByteStreams.copy( is, os ); |
368 + | } finally |
369 + | { |
370 + | is.close(); |
371 + | os.close(); |
372 + | } |
373 + | |
374 + | System.out.println( "Extracted: " + outFile ); |
375 + | } |
376 + | } |
377 + | |
378 + | public static void clone(String url, File target) throws GitAPIException |
379 + | { |
380 + | System.out.println( "Starting clone of " + url + " to " + target ); |
381 + | |
382 + | Git result = Git.cloneRepository().setURI( url ).setDirectory( target ).call(); |
383 + | |
384 + | try |
385 + | { |
386 + | System.out.println( "Cloned git repository " + url + " to " + url + ". Current HEAD: " + commitHash( result ) ); |
387 + | |
388 + | } finally |
389 + | { |
390 + | result.close(); |
391 + | } |
392 + | } |
393 + | |
394 + | public static String commitHash(Git repo) throws GitAPIException |
395 + | { |
396 + | return Iterables.getOnlyElement( repo.log().setMaxCount( 1 ).call() ).getName(); |
397 + | } |
398 + | |
399 + | public static File download(String url, File target) throws IOException |
400 + | { |
401 + | System.out.println( "Starting download of " + url ); |
402 + | |
403 + | byte[] bytes = Resources.toByteArray( new URL( url ) ); |
404 + | |
405 + | System.out.println( "Downloaded file: " + target + " with md5: " + Hashing.md5().hashBytes( bytes ).toString() ); |
406 + | |
407 + | Files.write( bytes, target ); |
408 + | |
409 + | return target; |
410 + | } |
411 + | } |