package install; import java.io.File; import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Locale; import javax.swing.JFileChooser; import javax.swing.JOptionPane; import wizard.BIRCHWizard; /** * The main body of the GetBIRCH installer. ** * @author Graham Alvare * @author Dale Hamel * @author Brian Fristensky */ public class Main { // PLATFORM TYPES public static enum ArchSys { /** * AMD64 Linux (64-bit) */ LINUX_64 ("linux-x86_64"), /** * 64-bit Mac OS X Intel */ OSX_64 ("osx-x86_64"), /** * 64-bit Mac OS X ARM */ MACOS_ARM64 ("macos-arm64"), /** * 32-bit Windows (XP compatible) */ WINXP_32 ("winxp-32"); /** * Stores the name of the platform */ private String name; /** * Construct a new platform type for GetBIRCH ** * @param name the name of the platform */ private ArchSys(String name) { this.name = name; } /** * Returns the name of the platform type ** * @return the name of the platform */ @Override public String toString() { return this.name; } }; // INSTALLER/GETBIRCH CONSTANTS /** * The base URL string for the BIRCH FTP site */ public static final String BASE_URL = "http://home.cc.umanitoba.ca/~psgendb/birchhomedir/FTP/BIRCH/"; /** * The timestamp format specification used by BIRCH and GetBIRCH */ public static final SimpleDateFormat TSTAMP_FMT = new SimpleDateFormat ("EE MMM dd HH:mm:ss zzz yyyy", Locale.ENGLISH); /** * Whether GetBIRCH is running in Windows */ public static final boolean IS_WINDOWS = System.getProperty("os.name").trim().toLowerCase().startsWith("win"); /** * The path of the Java executable on the host operating system */ public static final String JAVA_EXEC = get_java_exec(); /** * The platform/architecture of the machine GetBIRCH is running on */ public static final ArchSys PLATFORM = detect_platform(); /** * The most recent stable version number of BIRCH (on the BIRCH FTP site) */ public static final String VERSION = detect_version(); /** * The timestamp for most recent stable version of BIRCH (on the BIRCH FTP site) */ public static final Date TIMESTAMP = detect_timestamp(); /** * Whether GetBIRCH is running from a disc (true = from disc) */ public static final boolean DISC_INSTALL = is_disc_install(); /** * The path of Python to use for the BIRCH installation process. */ public static final String PYTHON_PATH = find_python(); // WINDOWS TAR AND GZ PATHS /** * The extracted EXE path for tar.exe */ public static File WIN_TAR = null; /** * The extracted EXE path for gunzip.exe */ public static File WIN_GZ = null; /** * The main method body for the GetBIRCH install process. * This method is called from the Bootstrap.main method. ** * @param args the program arguments for the GetBIRCH installer */ public static void main(String[] args) { BIRCHWizard wizard; if (PYTHON_PATH != null && csh_ok()) { if (!DISC_INSTALL) { extractLib(); } wizard = new BIRCHWizard(); } // memCheck(); } /** * Determines whether GetBIRCH is running off of a BIRCH install CD ** * @return true if GetBIRCH is running off of a BIRCH install CD */ private static boolean is_disc_install() { final File LIB = new File(BIRCHIO.JAR_PATH, "getbirch_temp"); final File BIN = new File(BIRCHIO.JAR_PATH, "binaries.tar.gz"); final File FRAME = new File(BIRCHIO.JAR_PATH, "framework.tar.gz"); boolean isDisc = false; // determine whether GetBIRCH is running from a BIRCH install disc if (BIRCHIO.JAR_PATH.isDirectory()) { isDisc = LIB.exists() && BIN.exists() && FRAME.exists(); } else { JOptionPane.showMessageDialog(null, "Not current directory:" + BIRCHIO.JAR_PATH); } return isDisc; } /** * Detects the platform which GetBIRCH is running on. ** * @return the host computer's operating system and architecture */ private static ArchSys detect_platform() { String osName = System.getProperty("os.name").toLowerCase(); String arch = System.getProperty("os.arch").toLowerCase(); ArchSys result = ArchSys.LINUX_64; // default to x86_64 Linux // determine the operating system from os.name if (IS_WINDOWS) { // WINDOWS result = ArchSys.WINXP_32; } else if (osName.startsWith("mac")) { // MAC OS X if (arch.equals("aarch64")) { result = ArchSys.MACOS_ARM64; } else { result = ArchSys.OSX_64; } } else { // PROBE FOR 64-BIT LINUX! // this probing is done via a library which is only executatble on // 64-bit linux. This library is extracted from the GetBIRCH JAR // file and loaded into the JVM. If the library is loaded properly, // the JNI method probe_linux64() is run (this method is provided // by the library). If probe_linux64() returns true, then the user // has an AMD64 compatible process. // // If there are any errors, or if probe_linux64() returns false // (which it SHOULDN'T), then we default to 32-bit Intel Linux try { // extract the Linux AMD64 probe library File linux64path = BIRCHIO.extract_temp("/resources/probe_linux64.so", "linux_64", ".so"); // load the AMD64 probe library System.load(linux64path.getAbsolutePath()); // execute the JNI method probe_linux64() if (probe_linux64()) { result = ArchSys.LINUX_64; } } catch (UnsatisfiedLinkError e) { } } // print the platform autodetection results to the console System.out.println("Setting platform to \"" + result + "\" "); return result; } /** * Stub method used for determining if we are running on 32-bit or 64-bit * Linux. If we are running GetBIRCH on Linux - then detect_platform will * extract a file (/resources/probe_linux64.so) which implements this * method. The file is a 64-bit Linux library, so only 64-bit Linux will * be able to load this library and this method. The C code for this * method is simple - the method just returns TRUE. The reason this works, * is because only 64-bit Linux will be able to load the library, therefore * 32-bit Linux will fail to load the library, and fail to execute this * method. * * See detect_platform for more details ** * @return true is the current machine is running 64-bit Linux */ private static native boolean probe_linux64(); /** * Detects the most recent version of BIRCH. * This is done by obtaining the contents of the file CURRENT/VERSION on the * BIRCH FTP site. The contents of this file is the current version number * for BIRCH. ** * @return the current stable version number of BIRCH */ private static String detect_version() { String VERSION_URL = BASE_URL + "CURRENT/VERSION"; String versionStr = ""; try { // read the version string from the FTP site versionStr = BIRCHIO.oneLineFile(new URL(VERSION_URL)); // print the current stable version of BIRCH System.out.println("BIRCH version " + versionStr); } catch (IOException ioe) { // FAILURE! System.err.println("Could not detect the current version of BIRCH!"); ioe.printStackTrace(System.err); } return versionStr; } /** * Detects the timestamp for the current version of BIRCH. * This is done by obtaining the contents of the file CURRENT/TIMESTAMP on the * BIRCH FTP site. The contents of this file is the current timestamp for BIRCH. ** * @return the current stable version number of BIRCH */ private static Date detect_timestamp() { String TIMESTAMP_URL = BASE_URL + "CURRENT/TIMESTAMP"; String timestampStr = ""; Date tstamp = null; try { // read the timestamp string from the BIRCH FTP site timestampStr = BIRCHIO.oneLineFile(new URL(TIMESTAMP_URL)); // parse the timestamp into a Java Date object tstamp = TSTAMP_FMT.parse(timestampStr); // print the current stable version of BIRCH's timestamp System.out.println("BIRCH timestamp " + TSTAMP_FMT.format(tstamp)); } catch (IOException ioe) { // FAILURE! System.err.println("Could not detect the current timetamp of BIRCH!"); ioe.printStackTrace(System.err); } catch (ParseException pe) { // FAILURE! System.err.println("Could not parse the current timetamp of BIRCH!"); System.out.println("String: " + timestampStr); pe.printStackTrace(System.err); } return tstamp; } /** * Determines the path to the Java executable on the current machine ** * @return the absolute path to the Java executable */ private static String get_java_exec() { File javaBin = new File(System.getProperty("java.home"), "bin"); File winJava = new File(javaBin, "javaw.exe"); File unxJava = new File(javaBin, "java"); // if the user is running GetBIRCH from Windows, then 'javaw.exe' is the // system's Java runtime executable, otherwise, we should assume the // user is running GetBIRCH on Unix, and use 'java' as the system's // Java runtime executable. In either case, the parent directory is // specified by the local variable 'javaBin'. return (IS_WINDOWS ? winJava.getAbsolutePath() : unxJava.getAbsolutePath()); } /** * Extract the libraries for the GetBIRCH install process. */ public static void extractLib() { if (IS_WINDOWS) { // if the user is running GetBIRCH on Windows, we will need to // extract tar.exe and gunzip.exe to handle the .tar.gz files from // the BIRCH FTP site. Thus, we extract these files and set the // global variables WIN_TAR and WIN_GZ to point to these resources Main.WIN_TAR = BIRCHIO.extract_temp("/resources/tar.exe", "tar", ".exe"); Main.WIN_GZ = BIRCHIO.extract_temp("/resources/gunzip.exe", "gunzip", ".exe"); } } /** * Cancels the current GetBIRCH installation process */ public static void cancel() { // display to the user that the installation was cancelled JOptionPane.showMessageDialog(null, "Installation cancelled."); System.out.println("Installation cancelled!"); // exit the installation process System.exit(0); } /** * Searches the system's execution path for a valid Python executable ** * @return the system's Python path (or null, if Python was not found) */ public static String find_python() { String pypath = null; String pyexec = (Main.IS_WINDOWS ? "python3.exe" : "python3"); final String[] PATH = System.getenv("PATH").split(File.pathSeparator); for (String testString : PATH) { File test = new File(testString, pyexec); //if (test.exists() && test.canExecute()) { if (test.exists()) { try { pypath = test.getCanonicalPath(); break; } catch (IOException ex) { ex.printStackTrace(System.err); } } } if (pypath == null && IS_WINDOWS) { File pytest; pytest = new File("C:\\Python\\python3.exe"); if (pytest.exists() && pytest.isFile() && pytest.canExecute()) { try { pypath = pytest.getCanonicalPath(); } catch (IOException ex) { ex.printStackTrace(System.err); } } else { pytest = new File("C:\\Program Files\\Python\\python3.exe"); if (pytest.exists() && pytest.isFile() && pytest.canExecute()) { try { pypath = pytest.getCanonicalPath(); } catch (IOException ex) { ex.printStackTrace(System.err); } } else { int selection = JOptionPane.showOptionDialog(null, "ERROR -- could not find Python!\n" + "Would you like to install Python or help GetBIRCH find python3.exe?", "GetBIRCH", JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, null, new Object[] {"Install", "Find", "Cancel"}, 0); if (selection == 0) { JFileChooser fc = new JFileChooser(BIRCHIO.DEFAULT_HOME); fc.setDialogTitle("Choose a directory to install Python"); fc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); fc.showOpenDialog(null); File pydir = fc.getSelectedFile(); if (pydir != null) { try { pypath = win_install_python(new File(pydir, "Python")); } catch (MalformedURLException ex) { ex.printStackTrace(System.err); } catch (IOException ex) { ex.printStackTrace(System.err); } } } else if (selection == 1) { JFileChooser fc = new JFileChooser(BIRCHIO.DEFAULT_HOME); fc.setDialogTitle("Find python3.exe"); fc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); fc.showOpenDialog(null); pytest = new File(fc.getSelectedFile(), "python3.exe"); if (pytest.exists() && pytest.isFile() && pytest.canExecute()) { try { pypath = pytest.getCanonicalPath(); } catch (IOException ex) { ex.printStackTrace(System.err); } } } } } } return pypath; } /** * Downloads and runs the Windows Python installer. ** * @param pydir the path to install Python to. * @return the path to the Python executable */ public static String win_install_python(File pydir) throws MalformedURLException, IOException { URL pyurl = new URL(Main.BASE_URL + "pyinstall.msi"); File pymsi = File.createTempFile("pyinstall", ".msi"); System.out.println("Downloading Python: " + pyurl.toString()); BIRCHIO.wget(pyurl, pymsi, null, true); // see: http://www.python.org/download/releases/2.5/msi/ for more info. BIRCHIO.runCommand(new String[] { "msiexec", "/i", pymsi.getCanonicalPath(), "TARGETDIR=" + pydir.getCanonicalPath(), "/qb!" }, null, pymsi.getParentFile(), System.out, System.err); return new File(new File(pydir, "bin"), "python3.exe").getCanonicalPath(); } /** * Determines whether CSH/TCSH is installed on the host system ** * @return true if TCSH or CSH is installed on the host system */ private static boolean csh_ok() { File csh_test = new File("/bin/csh"); return (csh_test.exists() && csh_test.isFile() && csh_test.canExecute()) || JOptionPane.showConfirmDialog(null, "Error: could not find /bin/csh, do you want to proceed anyways?", "GetBIRCH", JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION; } }