buildtools/src/main/java/org/spigotmc/builder/Builder.java

1042 lines
40 KiB
Java
Raw Normal View History

package org.spigotmc.builder;
import com.google.common.base.Charsets;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
import com.google.common.base.Throwables;
import com.google.common.collect.Iterables;
import com.google.common.collect.ObjectArrays;
import com.google.common.hash.HashFunction;
import com.google.common.hash.Hasher;
import com.google.common.hash.Hashing;
import com.google.common.io.ByteStreams;
import com.google.common.io.CharStreams;
import com.google.common.io.Files;
import com.google.common.io.Resources;
import com.google.gson.Gson;
import difflib.DiffUtils;
import difflib.Patch;
2015-08-31 19:31:00 +03:00
import java.awt.Desktop;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintStream;
import java.lang.management.ManagementFactory;
2015-08-31 19:31:00 +03:00
import java.net.URI;
import java.net.URL;
import java.net.URLConnection;
import java.nio.file.FileSystemException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import java.text.MessageFormat;
2014-12-06 09:12:53 +11:00
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.Enumeration;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
2015-08-31 19:31:00 +03:00
import javax.swing.JFrame;
import javax.swing.JLabel;
2015-02-23 09:32:50 +00:00
import joptsimple.OptionParser;
import joptsimple.OptionSet;
import joptsimple.OptionSpec;
import joptsimple.util.EnumConverter;
import lombok.RequiredArgsConstructor;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.output.TeeOutputStream;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.ResetCommand;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.api.errors.JGitInternalException;
import org.eclipse.jgit.lib.StoredConfig;
import org.eclipse.jgit.revwalk.RevCommit;
public class Builder
{
public static final String LOG_FILE = "BuildTools.log.txt";
public static final boolean IS_WINDOWS = System.getProperty( "os.name" ).startsWith( "Windows" );
public static final File CWD = new File( "." );
private static final boolean autocrlf = !"\n".equals( System.getProperty( "line.separator" ) );
2014-12-12 11:07:46 +11:00
private static boolean dontUpdate;
private static List<Compile> compile;
private static boolean generateSource;
private static boolean generateDocs;
private static boolean dev;
private static String applyPatchesShell = "sh";
//
private static File msysDir;
public static void main(String[] args) throws Exception
{
2019-04-23 16:38:24 +10:00
if ( CWD.getAbsolutePath().contains( "'" ) || CWD.getAbsolutePath().contains( "#" ) || CWD.getAbsolutePath().contains( "~" ) || CWD.getAbsolutePath().contains( "(" ) || CWD.getAbsolutePath().contains( ")" ) )
2016-03-26 15:58:22 +11:00
{
System.err.println( "Please do not run in a path with special characters!" );
return;
}
if ( CWD.getAbsolutePath().contains( "Dropbox" ) || CWD.getAbsolutePath().contains( "OneDrive" ) )
{
System.err.println( "Please do not run BuildTools in a Dropbox, OneDrive, or similar. You can always copy the completed jars there later." );
return;
}
if ( false && System.console() == null )
2015-08-31 19:31:00 +03:00
{
JFrame jFrame = new JFrame();
jFrame.setTitle( "SpigotMC - BuildTools" );
jFrame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
jFrame.getContentPane().add( new JLabel( "You have to run BuildTools through bash (msysgit). Please read our wiki." ) );
jFrame.pack();
jFrame.setVisible( true );
Desktop.getDesktop().browse( new URI( "https://www.spigotmc.org/wiki/buildtools/" ) );
return;
}
// May be null
String buildVersion = Builder.class.getPackage().getImplementationVersion();
int buildNumber = -1;
if ( buildVersion != null )
{
String[] split = buildVersion.split( "-" );
if ( split.length == 4 )
{
try
{
buildNumber = Integer.parseInt( split[3] );
} catch ( NumberFormatException ex )
{
}
}
}
System.out.println( "Loading BuildTools version: " + buildVersion + " (#" + buildNumber + ")" );
System.out.println( "Java Version: " + JavaVersion.getCurrentVersion() );
2015-02-23 09:32:50 +00:00
OptionParser parser = new OptionParser();
2019-03-17 12:40:22 +11:00
OptionSpec<Void> help = parser.accepts( "help", "Show the help" );
OptionSpec<Void> disableCertFlag = parser.accepts( "disable-certificate-check", "Disable HTTPS certificate check" );
OptionSpec<Void> disableJavaCheck = parser.accepts( "disable-java-check", "Disable Java version check" );
OptionSpec<Void> dontUpdateFlag = parser.accepts( "dont-update", "Don't pull updates from Git" );
OptionSpec<Void> skipCompileFlag = parser.accepts( "skip-compile", "Skip compilation" );
OptionSpec<Void> generateSourceFlag = parser.accepts( "generate-source", "Generate source jar" );
OptionSpec<Void> generateDocsFlag = parser.accepts( "generate-docs", "Generate Javadoc jar" );
OptionSpec<Void> devFlag = parser.accepts( "dev", "Development mode" );
OptionSpec<File> outputDir = parser.acceptsAll( Arrays.asList( "o", "output-dir" ), "Final jar output directory" ).withRequiredArg().ofType( File.class ).defaultsTo( CWD );
OptionSpec<String> jenkinsVersion = parser.accepts( "rev", "Version to build" ).withRequiredArg().defaultsTo( "latest" );
OptionSpec<Compile> toCompile = parser.accepts( "compile", "Software to compile" ).withRequiredArg().ofType( Compile.class ).withValuesConvertedBy( new EnumConverter<Compile>( Compile.class )
{
} ).withValuesSeparatedBy( ',' );
2015-02-23 09:32:50 +00:00
OptionSet options = parser.parse( args );
2019-03-17 12:40:22 +11:00
if ( options.has( help ) )
{
parser.printHelpOn( System.out );
System.exit( 0 );
}
2015-02-23 09:32:50 +00:00
if ( options.has( disableCertFlag ) )
2014-12-12 11:07:46 +11:00
{
2015-02-23 09:32:50 +00:00
disableHttpsCertificateCheck();
}
2015-02-23 09:32:50 +00:00
dontUpdate = options.has( dontUpdateFlag );
generateSource = options.has( generateSourceFlag );
generateDocs = options.has( generateDocsFlag );
dev = options.has( devFlag );
compile = options.valuesOf( toCompile );
if ( options.has( skipCompileFlag ) )
{
compile = Collections.singletonList( Compile.NONE );
System.err.println( "--skip-compile is deprecated, please use --compile NONE" );
}
if ( ( dev || dontUpdate ) && options.has( jenkinsVersion ) )
{
System.err.println( "Using --dev or --dont-update with --rev makes no sense, exiting." );
System.exit( 1 );
}
logOutput();
try
{
runProcess( CWD, "sh", "-c", "exit" );
} catch ( Exception ex )
{
if ( IS_WINDOWS )
{
String gitVersion = "PortableGit-2.24.1.2-" + ( System.getProperty( "os.arch" ).endsWith( "64" ) ? "64" : "32" ) + "-bit";
// https://github.com/git-for-windows/git/releases/tag/v2.24.1.windows.2
String gitHash = System.getProperty( "os.arch" ).endsWith( "64" ) ? "cb75e4a557e01dd27b5af5eb59dfe28adcbad21638777dd686429dd905d13899" : "88f5525999228b0be8bb51788bfaa41b14430904bc65f1d4bbdcf441cac1f7fc";
msysDir = new File( gitVersion, "PortableGit" );
if ( !msysDir.isDirectory() )
{
System.out.println( "*** Could not find PortableGit installation, downloading. ***" );
String gitName = gitVersion + ".7z.exe";
File gitInstall = new File( gitVersion, gitName );
gitInstall.deleteOnExit();
gitInstall.getParentFile().mkdirs();
if ( !gitInstall.exists() )
{
download( "https://static.spigotmc.org/git/" + gitName, gitInstall, HashFormat.SHA256, gitHash );
}
System.out.println( "Extracting downloaded git install" );
// yes to all, silent, don't run. Only -y seems to work
runProcess( gitInstall.getParentFile(), gitInstall.getAbsolutePath(), "-y", "-gm2", "-nr" );
gitInstall.delete();
}
System.out.println( "*** Using downloaded git " + msysDir + " ***" );
System.out.println( "*** Please note that this is a beta feature, so if it does not work please also try a manual install of git from https://git-for-windows.github.io/ ***" );
} else
{
System.out.println( "You must run this jar through bash (msysgit)" );
System.exit( 1 );
}
}
try
{
runProcess( CWD, "git", "--version" );
} catch ( Exception ex )
{
System.out.println( "Could not successfully run git. Please ensure it is installed and functioning. " + ex.getMessage() );
System.exit( 1 );
}
try
{
runProcess( CWD, "git", "config", "--global", "--includes", "user.name" );
} catch ( Exception ex )
{
System.out.println( "Git name not set, setting it to default value." );
2014-12-06 09:12:53 +11:00
runProcess( CWD, "git", "config", "--global", "user.name", "BuildTools" );
}
try
{
runProcess( CWD, "git", "config", "--global", "--includes", "user.email" );
} catch ( Exception ex )
{
System.out.println( "Git email not set, setting it to default value." );
2014-12-06 09:12:53 +11:00
runProcess( CWD, "git", "config", "--global", "user.email", "unconfigured@null.spigotmc.org" );
}
File workDir = new File( "work" );
workDir.mkdir();
File bukkit = new File( "Bukkit" );
if ( !bukkit.exists() )
{
clone( "https://hub.spigotmc.org/stash/scm/spigot/bukkit.git", bukkit );
}
File craftBukkit = new File( "CraftBukkit" );
if ( !craftBukkit.exists() )
{
clone( "https://hub.spigotmc.org/stash/scm/spigot/craftbukkit.git", craftBukkit );
}
File spigot = new File( "Spigot" );
if ( !spigot.exists() )
{
clone( "https://hub.spigotmc.org/stash/scm/spigot/spigot.git", spigot );
}
File buildData = new File( "BuildData" );
if ( !buildData.exists() )
{
clone( "https://hub.spigotmc.org/stash/scm/spigot/builddata.git", buildData );
}
2015-06-10 16:21:17 +01:00
File maven;
String m2Home = System.getenv( "M2_HOME" );
if ( m2Home == null || !( maven = new File( m2Home ) ).exists() )
{
String mavenVersion = "apache-maven-3.6.0";
maven = new File( mavenVersion );
if ( !maven.exists() )
{
System.out.println( "Maven does not exist, downloading. Please wait." );
File mvnTemp = new File( mavenVersion + "-bin.zip" );
mvnTemp.deleteOnExit();
// https://www.apache.org/dist/maven/maven-3/3.6.0/binaries/apache-maven-3.6.0-bin.zip.sha512
download( "https://static.spigotmc.org/maven/" + mvnTemp.getName(), mvnTemp, HashFormat.SHA512, "7d14ab2b713880538974aa361b987231473fbbed20e83586d542c691ace1139026f232bd46fdcce5e8887f528ab1c3fbfc1b2adec90518b6941235952d3868e9" );
unzip( mvnTemp, new File( "." ) );
mvnTemp.delete();
}
}
String mvn = maven.getAbsolutePath() + "/bin/mvn";
Git bukkitGit = Git.open( bukkit );
Git craftBukkitGit = Git.open( craftBukkit );
Git spigotGit = Git.open( spigot );
Git buildGit = Git.open( buildData );
BuildInfo buildInfo = new BuildInfo( "Dev Build", "Development", 0, null, new BuildInfo.Refs( "master", "master", "master", "master" ) );
2014-12-12 11:07:46 +11:00
if ( !dontUpdate )
{
if ( !dev )
{
String askedVersion = options.valueOf( jenkinsVersion );
System.out.println( "Attempting to build version: '" + askedVersion + "' use --rev <version> to override" );
String verInfo;
try
{
verInfo = get( "https://hub.spigotmc.org/versions/" + askedVersion + ".json" );
} catch ( IOException ex )
{
System.err.println( "Could not get version " + askedVersion + " does it exist? Try another version or use 'latest'" );
ex.printStackTrace();
return;
}
System.out.println( "Found version" );
System.out.println( verInfo );
buildInfo = new Gson().fromJson( verInfo, BuildInfo.class );
if ( buildNumber != -1 && buildInfo.getToolsVersion() != -1 && buildNumber < buildInfo.getToolsVersion() )
{
System.err.println( "**** Your BuildTools is out of date and will not build the requested version. Please grab a new copy from https://www.spigotmc.org/go/buildtools-dl" );
System.exit( 1 );
}
if ( !options.has( disableJavaCheck ) )
{
if ( buildInfo.getJavaVersions() == null )
{
buildInfo.setJavaVersions( new int[]
{
JavaVersion.JAVA_7.getVersion(), JavaVersion.JAVA_8.getVersion()
} );
}
Preconditions.checkArgument( buildInfo.getJavaVersions().length == 2, "Expected only two Java versions, got %s", JavaVersion.printVersions( buildInfo.getJavaVersions() ) );
JavaVersion curVersion = JavaVersion.getCurrentVersion();
JavaVersion minVersion = JavaVersion.getByVersion( buildInfo.getJavaVersions()[0] );
JavaVersion maxVersion = JavaVersion.getByVersion( buildInfo.getJavaVersions()[1] );
if ( curVersion.getVersion() < minVersion.getVersion() || curVersion.getVersion() > maxVersion.getVersion() )
{
System.err.println( "*** The version you have requested to build requires Java versions between " + JavaVersion.printVersions( buildInfo.getJavaVersions() ) + ", but you are using " + curVersion );
System.err.println( "*** Please rerun BuildTools using an appropriate Java version. For obvious reasons outdated MC versions do not support Java versions that did not exist at their release." );
System.exit( 1 );
}
}
}
pull( buildGit, buildInfo.getRefs().getBuildData() );
pull( bukkitGit, buildInfo.getRefs().getBukkit() );
pull( craftBukkitGit, buildInfo.getRefs().getCraftBukkit() );
pull( spigotGit, buildInfo.getRefs().getSpigot() );
2014-12-12 11:07:46 +11:00
}
2015-02-27 16:27:23 +11:00
VersionInfo versionInfo = new Gson().fromJson(
Files.toString( new File( "BuildData/info.json" ), Charsets.UTF_8 ),
2015-02-27 16:27:23 +11:00
VersionInfo.class
);
// Default to 1.8 builds.
if ( versionInfo == null )
{
versionInfo = new VersionInfo( "1.8", "bukkit-1.8.at", "bukkit-1.8-cl.csrg", "bukkit-1.8-members.csrg", "package.srg", null );
2015-02-27 16:27:23 +11:00
}
System.out.println( "Attempting to build Minecraft with details: " + versionInfo );
if ( buildNumber != -1 && versionInfo.getToolsVersion() != -1 && buildNumber < versionInfo.getToolsVersion() )
{
System.err.println( "" );
System.err.println( "**** Your BuildTools is out of date and will not build the requested version. Please grab a new copy from https://www.spigotmc.org/go/buildtools-dl" );
System.exit( 1 );
}
2015-02-27 16:27:23 +11:00
File vanillaJar = new File( workDir, "minecraft_server." + versionInfo.getMinecraftVersion() + ".jar" );
if ( !vanillaJar.exists() || !checkHash( vanillaJar, versionInfo ) )
{
if ( versionInfo.getServerUrl() != null )
{
download( versionInfo.getServerUrl(), vanillaJar, HashFormat.MD5, versionInfo.getMinecraftHash() );
} else
{
download( String.format( "https://s3.amazonaws.com/Minecraft.Download/versions/%1$s/minecraft_server.%1$s.jar", versionInfo.getMinecraftVersion() ), vanillaJar, HashFormat.MD5, versionInfo.getMinecraftHash() );
// Legacy versions can also specify a specific shell to build with which has to be bash-compatible
applyPatchesShell = System.getenv().get( "SHELL" );
if ( applyPatchesShell == null || applyPatchesShell.trim().isEmpty() )
{
applyPatchesShell = "bash";
}
}
}
Iterable<RevCommit> mappings = buildGit.log()
.addPath( "mappings/" )
.setMaxCount( 1 ).call();
Hasher mappingsHash = Hashing.md5().newHasher();
for ( RevCommit rev : mappings )
{
mappingsHash.putString( rev.getName(), Charsets.UTF_8 );
}
String mappingsVersion = mappingsHash.hash().toString().substring( 24 ); // Last 8 chars
File finalMappedJar = new File( workDir, "mapped." + mappingsVersion + ".jar" );
if ( !finalMappedJar.exists() )
{
System.out.println( "Final mapped jar: " + finalMappedJar + " does not exist, creating (please wait)!" );
File clMappedJar = new File( finalMappedJar + "-cl" );
File mMappedJar = new File( finalMappedJar + "-m" );
if ( versionInfo.getClassMapCommand() == null )
{
versionInfo.setClassMapCommand( "java -jar BuildData/bin/SpecialSource-2.jar map -i {0} -m {1} -o {2}" );
}
runProcess( CWD, MessageFormat.format( versionInfo.getClassMapCommand(), vanillaJar.getPath(), "BuildData/mappings/" + versionInfo.getClassMappings(), clMappedJar.getPath() ).split( " " ) );
2014-12-06 09:12:53 +11:00
if ( versionInfo.getMemberMapCommand() == null )
{
versionInfo.setMemberMapCommand( "java -jar BuildData/bin/SpecialSource-2.jar map -i {0} -m {1} -o {2}" );
}
runProcess( CWD, MessageFormat.format( versionInfo.getMemberMapCommand(), clMappedJar.getPath(),
"BuildData/mappings/" + versionInfo.getMemberMappings(), mMappedJar.getPath() ).split( " " ) );
2014-12-06 09:12:53 +11:00
if ( versionInfo.getFinalMapCommand() == null )
{
versionInfo.setFinalMapCommand( "java -jar BuildData/bin/SpecialSource.jar --kill-lvt -i {0} --access-transformer {1} -m {2} -o {3}" );
}
runProcess( CWD, MessageFormat.format( versionInfo.getFinalMapCommand(), mMappedJar.getPath(), "BuildData/mappings/" + versionInfo.getAccessTransforms(),
"BuildData/mappings/" + versionInfo.getPackageMappings(), finalMappedJar.getPath() ).split( " " ) );
}
runProcess( CWD, "sh", mvn, "install:install-file", "-Dfile=" + finalMappedJar, "-Dpackaging=jar", "-DgroupId=org.spigotmc",
2015-02-27 16:27:23 +11:00
"-DartifactId=minecraft-server", "-Dversion=" + versionInfo.getMinecraftVersion() + "-SNAPSHOT" );
File decompileDir = new File( workDir, "decompile-" + mappingsVersion );
if ( !decompileDir.exists() )
{
decompileDir.mkdir();
File clazzDir = new File( decompileDir, "classes" );
unzip( finalMappedJar, clazzDir, new Predicate<String>()
{
@Override
public boolean apply(String input)
{
return input.startsWith( "net/minecraft/server" );
}
} );
if ( versionInfo.getDecompileCommand() == null )
{
versionInfo.setDecompileCommand( "java -jar BuildData/bin/fernflower.jar -dgs=1 -hdc=0 -rbr=0 -asc=1 -udv=0 {0} {1}" );
}
runProcess( CWD, MessageFormat.format( versionInfo.getDecompileCommand(), clazzDir.getPath(), decompileDir.getPath() ).split( " " ) );
}
try
{
File latestLink = new File( workDir, "decompile-latest" );
latestLink.delete();
java.nio.file.Files.createSymbolicLink( latestLink.toPath(), decompileDir.getParentFile().toPath().relativize( decompileDir.toPath() ) );
} catch ( UnsupportedOperationException ex )
{
// Ignore if not possible
} catch ( FileSystemException ex )
{
// Not running as admin on Windows
} catch ( IOException ex )
{
System.out.println( "Did not create decompile-latest link " + ex.getMessage() );
}
System.out.println( "Applying CraftBukkit Patches" );
File nmsDir = new File( craftBukkit, "src/main/java/net" );
if ( nmsDir.exists() )
{
System.out.println( "Backing up NMS dir" );
FileUtils.moveDirectory( nmsDir, new File( workDir, "nms.old." + System.currentTimeMillis() ) );
}
File patchDir = new File( craftBukkit, "nms-patches" );
for ( File file : patchDir.listFiles() )
{
if ( !file.getName().endsWith( ".patch" ) )
{
continue;
}
2018-07-14 10:35:43 +10:00
String targetFile = "net/minecraft/server/" + file.getName().replace( ".patch", ".java" );
File clean = new File( decompileDir, targetFile );
File t = new File( nmsDir.getParentFile(), targetFile );
t.getParentFile().mkdirs();
System.out.println( "Patching with " + file.getName() );
2014-12-24 09:32:10 +11:00
List<String> readFile = Files.readLines( file, Charsets.UTF_8 );
// Manually append prelude if it is not found in the first few lines.
boolean preludeFound = false;
for ( int i = 0; i < Math.min( 3, readFile.size() ); i++ )
{
if ( readFile.get( i ).startsWith( "+++" ) )
{
preludeFound = true;
break;
}
}
if ( !preludeFound )
{
readFile.add( 0, "+++" );
}
Patch parsedPatch = DiffUtils.parseUnifiedDiff( readFile );
List<?> modifiedLines = DiffUtils.patch( Files.readLines( clean, Charsets.UTF_8 ), parsedPatch );
BufferedWriter bw = new BufferedWriter( new FileWriter( t ) );
for ( String line : (List<String>) modifiedLines )
{
bw.write( line );
bw.newLine();
}
bw.close();
}
File tmpNms = new File( craftBukkit, "tmp-nms" );
FileUtils.copyDirectory( nmsDir, tmpNms );
craftBukkitGit.branchDelete().setBranchNames( "patched" ).setForce( true ).call();
craftBukkitGit.checkout().setCreateBranch( true ).setForce( true ).setName( "patched" ).call();
craftBukkitGit.add().addFilepattern( "src/main/java/net/" ).call();
craftBukkitGit.commit().setMessage( "CraftBukkit $ " + new Date() ).call();
craftBukkitGit.checkout().setName( buildInfo.getRefs().getCraftBukkit() ).call();
FileUtils.moveDirectory( tmpNms, nmsDir );
if ( versionInfo.getToolsVersion() < 93 )
{
File spigotApi = new File( spigot, "Bukkit" );
if ( !spigotApi.exists() )
{
clone( "file://" + bukkit.getAbsolutePath(), spigotApi );
}
File spigotServer = new File( spigot, "CraftBukkit" );
if ( !spigotServer.exists() )
{
clone( "file://" + craftBukkit.getAbsolutePath(), spigotServer );
}
}
// Git spigotApiGit = Git.open( spigotApi );
// Git spigotServerGit = Git.open( spigotServer );
if ( compile == null || compile.isEmpty() )
{
if ( versionInfo.getToolsVersion() <= 104 || dev )
{
compile = Arrays.asList( Compile.CRAFTBUKKIT, Compile.SPIGOT );
} else
{
compile = Collections.singletonList( Compile.SPIGOT );
}
}
if ( compile.contains( Compile.CRAFTBUKKIT ) )
2014-12-12 11:25:00 +11:00
{
System.out.println( "Compiling Bukkit" );
if ( dev )
{
runProcess( bukkit, "sh", mvn, "-P", "development", "clean", "install" );
} else
{
runProcess( bukkit, "sh", mvn, "clean", "install" );
}
if ( generateDocs )
{
runProcess( bukkit, "sh", mvn, "javadoc:jar" );
}
if ( generateSource )
{
runProcess( bukkit, "sh", mvn, "source:jar" );
}
2014-12-12 11:25:00 +11:00
System.out.println( "Compiling CraftBukkit" );
if ( dev )
{
runProcess( craftBukkit, "sh", mvn, "-P", "development", "clean", "install" );
} else
{
runProcess( craftBukkit, "sh", mvn, "clean", "install" );
}
2014-12-12 11:25:00 +11:00
}
try
{
runProcess( spigot, applyPatchesShell, "applyPatches.sh" );
System.out.println( "*** Spigot patches applied!" );
2014-12-12 11:25:00 +11:00
if ( compile.contains( Compile.SPIGOT ) )
2014-12-12 11:25:00 +11:00
{
System.out.println( "Compiling Spigot & Spigot-API" );
if ( dev )
{
runProcess( spigot, "sh", mvn, "-P", "development", "clean", "install" );
} else
{
runProcess( spigot, "sh", mvn, "clean", "install" );
}
2014-12-12 11:25:00 +11:00
}
} catch ( Exception ex )
{
2016-03-01 19:09:59 +11:00
System.err.println( "Error compiling Spigot. Please check the wiki for FAQs." );
System.err.println( "If this does not resolve your issue then please pastebin the entire BuildTools.log.txt file when seeking support." );
ex.printStackTrace();
2014-11-29 10:49:54 +11:00
System.exit( 1 );
}
for ( int i = 0; i < 35; i++ )
{
System.out.println( " " );
}
2015-04-26 13:10:19 -05:00
System.out.println( "Success! Everything completed successfully. Copying final .jar files now." );
if ( ( versionInfo.getToolsVersion() < 101 && !compile.contains( Compile.NONE ) ) || ( versionInfo.getToolsVersion() > 104 && compile.contains( Compile.CRAFTBUKKIT ) ) )
{
copyJar( "CraftBukkit/target", "craftbukkit", new File( outputDir.value( options ), "craftbukkit-" + versionInfo.getMinecraftVersion() + ".jar" ) );
}
if ( compile.contains( Compile.SPIGOT ) )
2015-04-26 13:10:19 -05:00
{
2017-11-18 09:52:58 +11:00
copyJar( "Spigot/Spigot-Server/target", "spigot", new File( outputDir.value( options ), "spigot-" + versionInfo.getMinecraftVersion() + ".jar" ) );
2015-04-26 13:10:19 -05:00
}
}
private static boolean checkHash(File vanillaJar, VersionInfo versionInfo) throws IOException
{
String hash = Files.hash( vanillaJar, Hashing.md5() ).toString();
if ( !dev && versionInfo.getMinecraftHash() != null && !hash.equals( versionInfo.getMinecraftHash() ) )
{
System.err.println( "**** Warning, Minecraft jar hash of " + hash + " does not match stored hash of " + versionInfo.getMinecraftHash() );
return false;
} else
{
System.out.println( "Found good Minecraft hash (" + hash + ")" );
return true;
}
}
public static final String get(String url) throws IOException
{
URLConnection con = new URL( url ).openConnection();
con.setConnectTimeout( 5000 );
con.setReadTimeout( 5000 );
InputStreamReader r = null;
try
{
r = new InputStreamReader( con.getInputStream() );
return CharStreams.toString( r );
} finally
{
if ( r != null )
{
r.close();
}
}
}
2017-11-18 09:52:58 +11:00
public static void copyJar(String path, final String jarPrefix, File outJar) throws Exception
{
File[] files = new File( path ).listFiles( new FilenameFilter()
{
@Override
public boolean accept(File dir, String name)
{
return name.startsWith( jarPrefix ) && name.endsWith( ".jar" );
}
} );
2017-11-18 09:52:58 +11:00
if ( !outJar.getParentFile().isDirectory() )
{
outJar.getParentFile().mkdirs();
}
for ( File file : files )
{
2017-11-18 09:52:58 +11:00
System.out.println( "Copying " + file.getName() + " to " + outJar.getAbsolutePath() );
Files.copy( file, outJar );
System.out.println( " - Saved as " + outJar );
}
}
public static void pull(Git repo, String ref) throws Exception
{
System.out.println( "Pulling updates for " + repo.getRepository().getDirectory() );
try
{
repo.reset().setRef( "origin/master" ).setMode( ResetCommand.ResetType.HARD ).call();
} catch ( JGitInternalException ex )
{
System.err.println( "*** Warning, could not find origin/master ref, but continuing anyway." );
System.err.println( "*** If further errors occur please delete " + repo.getRepository().getDirectory().getParent() + " and retry." );
}
repo.fetch().call();
System.out.println( "Successfully fetched updates!" );
2015-02-27 16:12:05 +11:00
repo.reset().setRef( ref ).setMode( ResetCommand.ResetType.HARD ).call();
if ( ref.equals( "master" ) )
{
repo.reset().setRef( "origin/master" ).setMode( ResetCommand.ResetType.HARD ).call();
}
System.out.println( "Checked out: " + ref );
}
2014-12-06 09:12:53 +11:00
public static int runProcess(File workDir, String... command) throws Exception
{
if ( msysDir != null )
{
if ( "bash".equals( command[0] ) )
{
command[0] = "git-bash";
}
String[] shim = new String[]
{
"cmd.exe", "/C"
};
command = ObjectArrays.concat( shim, command, String.class );
}
return runProcess0( workDir, command );
}
private static int runProcess0(File workDir, String... command) throws Exception
{
Preconditions.checkArgument( workDir != null, "workDir" );
Preconditions.checkArgument( command != null && command.length > 0, "Invalid command" );
if ( command[0].equals( "java" ) )
{
command[0] = System.getProperty( "java.home" ) + "/bin/" + command[0];
}
ProcessBuilder pb = new ProcessBuilder( command );
pb.directory( workDir );
pb.environment().put( "JAVA_HOME", System.getProperty( "java.home" ) );
pb.environment().remove( "M2_HOME" ); // Just let maven figure this out from where it is invoked
2015-01-05 19:45:18 +11:00
if ( !pb.environment().containsKey( "MAVEN_OPTS" ) )
{
pb.environment().put( "MAVEN_OPTS", "-Xmx1024M" );
}
if ( !pb.environment().containsKey( "_JAVA_OPTIONS" ) )
{
String javaOptions = "-Djdk.net.URLClassPath.disableClassPathURLCheck=true";
for ( String arg : ManagementFactory.getRuntimeMXBean().getInputArguments() )
{
if ( arg.startsWith( "-Xmx" ) )
{
javaOptions += " " + arg;
}
}
pb.environment().put( "_JAVA_OPTIONS", javaOptions );
}
if ( msysDir != null )
{
String pathEnv = null;
for ( String key : pb.environment().keySet() )
{
if ( key.equalsIgnoreCase( "path" ) )
{
pathEnv = key;
}
}
if ( pathEnv == null )
{
throw new IllegalStateException( "Could not find path variable!" );
}
String path = msysDir.getAbsolutePath() + ";" + new File( msysDir, "bin" ).getAbsolutePath() + ";" + pb.environment().get( pathEnv );
pb.environment().put( pathEnv, path );
}
final Process ps = pb.start();
2018-12-01 08:27:24 +11:00
new Thread( new StreamRedirector( ps.getInputStream(), System.out ), "System.out redirector" ).start();
new Thread( new StreamRedirector( ps.getErrorStream(), System.err ), "System.err redirector" ).start();
int status = ps.waitFor();
if ( status != 0 )
{
2014-12-06 09:12:53 +11:00
throw new RuntimeException( "Error running command, return status !=0: " + Arrays.toString( command ) );
}
return status;
}
@RequiredArgsConstructor
private static class StreamRedirector implements Runnable
{
private final InputStream in;
private final PrintStream out;
@Override
public void run()
{
BufferedReader br = new BufferedReader( new InputStreamReader( in ) );
try
{
String line;
while ( ( line = br.readLine() ) != null )
{
out.println( line );
}
} catch ( IOException ex )
{
throw Throwables.propagate( ex );
}
}
}
public static void unzip(File zipFile, File targetFolder) throws IOException
{
unzip( zipFile, targetFolder, null );
}
public static void unzip(File zipFile, File targetFolder, Predicate<String> filter) throws IOException
{
targetFolder.mkdir();
ZipFile zip = new ZipFile( zipFile );
try
{
for ( Enumeration<? extends ZipEntry> entries = zip.entries(); entries.hasMoreElements(); )
{
ZipEntry entry = entries.nextElement();
if ( filter != null )
{
if ( !filter.apply( entry.getName() ) )
{
continue;
}
}
File outFile = new File( targetFolder, entry.getName() );
if ( entry.isDirectory() )
{
outFile.mkdirs();
continue;
}
if ( outFile.getParentFile() != null )
{
outFile.getParentFile().mkdirs();
}
InputStream is = zip.getInputStream( entry );
OutputStream os = new FileOutputStream( outFile );
try
{
ByteStreams.copy( is, os );
} finally
{
is.close();
os.close();
}
System.out.println( "Extracted: " + outFile );
}
} finally
{
zip.close();
}
}
public static void clone(String url, File target) throws GitAPIException, IOException
{
System.out.println( "Starting clone of " + url + " to " + target );
Git result = Git.cloneRepository().setURI( url ).setDirectory( target ).call();
try
{
StoredConfig config = result.getRepository().getConfig();
config.setBoolean( "core", null, "autocrlf", autocrlf );
config.save();
System.out.println( "Cloned git repository " + url + " to " + target.getAbsolutePath() + ". Current HEAD: " + commitHash( result ) );
} finally
{
result.close();
}
}
public static String commitHash(Git repo) throws GitAPIException
{
return Iterables.getOnlyElement( repo.log().setMaxCount( 1 ).call() ).getName();
}
public static File download(String url, File target, HashFormat hashFormat, String goodHash) throws IOException
{
System.out.println( "Starting download of " + url );
byte[] bytes = Resources.toByteArray( new URL( url ) );
String hash = hashFormat.getHash().hashBytes( bytes ).toString();
System.out.println( "Downloaded file: " + target + " with hash: " + hash );
if ( !dev && goodHash != null && !goodHash.equals( hash ) )
{
throw new IllegalStateException( "Downloaded file: " + target + " did not match expected hash: " + goodHash );
}
Files.write( bytes, target );
return target;
}
public static void disableHttpsCertificateCheck()
{
// This globally disables certificate checking
// http://stackoverflow.com/questions/19723415/java-overriding-function-to-disable-ssl-certificate-check
try
{
TrustManager[] trustAllCerts = new TrustManager[]
{
new X509TrustManager()
{
@Override
public java.security.cert.X509Certificate[] getAcceptedIssuers()
{
return null;
}
@Override
public void checkClientTrusted(X509Certificate[] certs, String authType)
{
}
@Override
public void checkServerTrusted(X509Certificate[] certs, String authType)
{
}
}
};
// Trust SSL certs
SSLContext sc = SSLContext.getInstance( "SSL" );
sc.init( null, trustAllCerts, new SecureRandom() );
HttpsURLConnection.setDefaultSSLSocketFactory( sc.getSocketFactory() );
// Trust host names
HostnameVerifier allHostsValid = new HostnameVerifier()
{
@Override
public boolean verify(String hostname, SSLSession session)
{
return true;
}
};
HttpsURLConnection.setDefaultHostnameVerifier( allHostsValid );
} catch ( NoSuchAlgorithmException ex )
{
System.out.println( "Failed to disable https certificate check" );
ex.printStackTrace( System.err );
} catch ( KeyManagementException ex )
{
System.out.println( "Failed to disable https certificate check" );
ex.printStackTrace( System.err );
}
}
public static void logOutput()
{
try
{
final OutputStream logOut = new BufferedOutputStream( new FileOutputStream( LOG_FILE ) );
Runtime.getRuntime().addShutdownHook( new Thread()
{
@Override
public void run()
{
System.setOut( new PrintStream( new FileOutputStream( FileDescriptor.out ) ) );
System.setErr( new PrintStream( new FileOutputStream( FileDescriptor.err ) ) );
try
{
logOut.close();
} catch ( IOException ex )
{
// We're shutting the jvm down anyway.
}
}
} );
System.setOut( new PrintStream( new TeeOutputStream( System.out, logOut ) ) );
System.setErr( new PrintStream( new TeeOutputStream( System.err, logOut ) ) );
} catch ( FileNotFoundException ex )
{
System.err.println( "Failed to create log file: " + LOG_FILE );
}
}
public enum HashFormat
{
MD5
{
@Override
public HashFunction getHash()
{
return Hashing.md5();
}
}, SHA256
{
@Override
public HashFunction getHash()
{
return Hashing.sha256();
}
}, SHA512
{
@Override
public HashFunction getHash()
{
return Hashing.sha512();
}
};
public abstract HashFunction getHash();
}
}