Java tutorial
/******************************************************************************* * Copyright (c) 2011 Tran Nam Quang. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Tran Nam Quang - initial API and implementation *******************************************************************************/ /** * @author Tran Nam Quang */ package net.sourceforge.vaticanfetcher.build; import java.io.File; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.List; import net.sourceforge.vaticanfetcher.Main; import net.sourceforge.vaticanfetcher.TestFiles; import net.sourceforge.vaticanfetcher.UtilGlobal; import net.sourceforge.vaticanfetcher.build.U.LineSep; import net.sourceforge.vaticanfetcher.man.Manual; import net.sourceforge.vaticanfetcher.util.AppUtil; import net.sourceforge.vaticanfetcher.util.Util; import net.sourceforge.vaticanfetcher.util.annotations.NotNull; import org.apache.tools.ant.Project; import org.apache.tools.ant.taskdefs.Jar; import org.apache.tools.ant.taskdefs.Javac; import org.apache.tools.ant.taskdefs.Manifest; import org.apache.tools.ant.taskdefs.Manifest.Attribute; import org.junit.runner.JUnitCore; import org.junit.runner.notification.Failure; import org.junit.runner.notification.RunListener; import com.google.common.base.Strings; public final class BuildMain { public static final String appName = "VaticanFetcher"; private static final String version = readVersionNumber(); private static final String packageId = Main.class.getPackage().getName(); private static final String packagePath = packageId.replace(".", "/"); private static final String mainPath = "build/tmp/src/" + packagePath; private static final DateFormat dateFormat = new SimpleDateFormat("yyyyMMdd-HHmm"); private static final String buildDate = dateFormat.format(new Date()); // Reads version number from file 'current-version.txt' @NotNull public static String readVersionNumber() { String versionStr = ""; try { versionStr = U.read("current-version.txt").trim(); } catch (Exception e) { Util.printErr(e); System.exit(0); } Util.checkThat(versionStr.split("\r?\n").length == 1); return versionStr; } public static void main(String[] args) throws Exception { Util.println("Copying sources to build directory..."); U.copyDir("src", "build/tmp/src"); // Licenses String licensePatterns = U.readPatterns("lib/license_patterns.txt"); String eplFilename = "epl-v10.html"; U.copyDir("lib", "build/tmp/licenses", licensePatterns, "**/license_patterns.txt"); U.copyBinaryFile("dist/" + eplFilename, "build/tmp/licenses/vaticanfetcher/" + eplFilename); U.zipDirContents("build/tmp/licenses", "build/tmp/licenses.zip"); Util.println("Compiling sources..."); Javac javac = new Javac(); javac.setProject(new Project()); javac.setSrcdir(new Paths().addDirSet("build/tmp/src").get()); javac.setClasspath(new Paths().addFileSet("lib", "**/*.jar").get()); javac.setSource("1.6"); javac.setTarget("1.6"); javac.setDebug(true); javac.setOptimize(true); javac.setFork(true); // Won't find javac executable without this javac.execute(); recreateJarFile("", false, LineSep.WINDOWS); // Needed for NSIS script File portableJar = recreateJarFile("portable_", true, LineSep.WINDOWS); File macOsXJar = recreateJarFile("macosx_", false, LineSep.UNIX); rebuildManuals(); createPortableBuild(portableJar); createMacOsXBuild(macOsXJar); runTests(); } private static File recreateJarFile(String jarPrefix, boolean isPortable, LineSep lineSep) throws Exception { String msgPrefix = isPortable ? "" : "non-"; Util.println(U.format("Creating %sportable jar file...", msgPrefix)); File systemConfDest = new File(mainPath + "/system-conf.txt"); systemConfDest.delete(); File programConfDest = new File(mainPath + "/program-conf.txt"); programConfDest.delete(); File mainJarFile = new File( String.format("build/tmp/%s%s_%s_%s.jar", jarPrefix, packageId, version, buildDate)); mainJarFile.delete(); U.copyTextFile("dist/system-template-conf.txt", systemConfDest.getPath(), lineSep, "${app_name}", appName, "${app_version}", version, "${build_date}", buildDate, "${is_portable}", String.valueOf(isPortable)); U.copyTextFile("dist/program-conf.txt", programConfDest.getPath(), lineSep); Jar jar = new Jar(); jar.setProject(new Project()); jar.setDestFile(mainJarFile); jar.setBasedir(new File("build/tmp/src")); Manifest manifest = new Manifest(); Attribute attr = new Attribute(); attr.setName("Main-Class"); attr.setValue(Main.class.getName()); manifest.addConfiguredAttribute(attr); jar.addConfiguredManifest(manifest); jar.execute(); return mainJarFile; } // Must be run before updating the version numbers in the manual pages private static void rebuildManuals() throws Exception { for (File dir : Util.listFiles(new File(Manual.manDir))) { if (!dir.isDirectory() || dir.getName().equals("all")) continue; Manual.main(new String[] { dir.getName() }); } } private static void createPortableBuild(File tmpMainJar) throws Exception { Util.println("Creating portable build..."); String releaseDir = U.format("build/%s-%s", appName, version); U.copyDir("dist/img", releaseDir + "/img"); U.copyDir("dist/help", releaseDir + "/help"); // U.copyDir("dist/templates", releaseDir + "/templates"); U.copyDir("dist/lang", releaseDir + "/lang"); updateManualVersionNumber(new File(releaseDir, "help")); String excludedLibs = U.readPatterns("lib/excluded_jar_patterns.txt"); U.copyFlatten("lib", releaseDir + "/lib", "**/*.jar", excludedLibs); U.copyFlatten("lib", releaseDir + "/lib/swt", "**/swt*.jar", null); U.copyFlatten("lib", releaseDir + "/lib", "**/*.so, **/*.dll, **/*.dylib", null); String jarName = U.removePrefix(tmpMainJar.getName(), "portable_"); String dstMainJar = U.format("%s/lib/%s", releaseDir, jarName); U.copyBinaryFile(tmpMainJar.getPath(), dstMainJar); String linuxLauncher = U.format("%s/%s.sh", releaseDir, appName); U.copyTextFile("dist/launchers/launcher-linux.sh", linuxLauncher, LineSep.UNIX, "${main_class}", Main.class.getName()); // Create vaticanfetcher.app launcher for Mac OS X String macOsXLauncher = U.format("%s/%s.app/Contents/MacOS/%s", releaseDir, appName, appName); U.copyTextFile("dist/launchers/launcher-macosx-portable.sh", macOsXLauncher, LineSep.UNIX, "${app_name}", appName, "${main_class}", Main.class.getName()); U.copyBinaryFile("dist/VaticanFetcher.icns", U.format("%s/%s.app/Contents/Resources/%s.icns", releaseDir, appName, appName)); deployInfoPlist(new File(U.format("%s/%s.app/Contents", releaseDir, appName))); makeExecutable("Cannot make the portable launcher shell scripts executable.", linuxLauncher, macOsXLauncher); String exeLauncher = U.format("%s/%s.exe", releaseDir, appName); U.copyBinaryFile("dist/launchers/VaticanFetcher-512.exe", exeLauncher); for (int heapSize : new int[] { 256, 512, 768, 1024 }) { String exeName = U.format("%s-%d.exe", appName, heapSize); exeLauncher = U.format("%s/misc/%s", releaseDir, exeName); U.copyBinaryFile("dist/launchers/" + exeName, exeLauncher); } String batLauncher = U.format("%s/misc/%s.bat", releaseDir, appName); U.copyTextFile("dist/launchers/launcher.bat", batLauncher, LineSep.WINDOWS, "${main_class}", Main.class.getName()); String[] daemonNames = new String[] { "docfetcher-daemon-windows.exe", "docfetcher-daemon-linux" }; for (String daemonName : daemonNames) { String dstPath = releaseDir + "/" + daemonName; U.copyBinaryFile("dist/daemon/" + daemonName, dstPath); } makeExecutable("Cannot make the Linux daemon executable.", releaseDir + "/" + daemonNames[1]); U.copyTextFile("dist/program-conf.txt", releaseDir + "/conf/program-conf.txt", LineSep.WINDOWS); U.copyTextFile("dist/paths.txt", releaseDir + "/misc/paths.txt", LineSep.WINDOWS); U.copyBinaryFile("build/tmp/licenses.zip", releaseDir + "/misc/licenses.zip"); /* Create an empty file 'indexes/.indexes.txt' to let the daemons know we're the portable version. */ U.write("", releaseDir + "/indexes/.indexes.txt"); U.copyTextFile("dist/Readme.txt", releaseDir + "/Readme.txt", LineSep.WINDOWS); // Wrap the portable build in a zip archive for deployment if (Util.IS_LINUX || Util.IS_MAC_OS_X) { String cmd = "zip -r -q %s-%s-portable.zip %s-%s"; U.execInDir(new File("build"), cmd, appName.toLowerCase(), version, appName, version); } else { String zipPath = U.format("build/%s-%s-portable.zip", appName.toLowerCase(), version); U.zipDir(releaseDir, zipPath); Util.println("** Warning: Could not preserve executable flags in archive: " + zipPath); } } private static void deployInfoPlist(File dstDir) throws Exception { U.copyTextFile("dist/Info.plist", new File(dstDir, "Info.plist").getPath(), LineSep.UNIX, "${app_name}", appName, "${app_version}", version, "${build_date}", buildDate, "${package_id}", packageId); } private static void makeExecutable(String errorMessage, String... paths) throws Exception { if (Util.IS_LINUX || Util.IS_MAC_OS_X) { for (String path : paths) U.exec("chmod +x %s", Util.getAbsPath(path)); } else { Util.printErr("** Warning: " + errorMessage); } } private static void createMacOsXBuild(File tmpMainJar) throws Exception { String suffix = Util.IS_MAC_OS_X ? "" : " (without disk image packaging)"; Util.println(U.format("Creating Mac OS X build%s...", suffix)); String appDir = U.format("build/%s.app", appName); String contentsDir = appDir + "/Contents"; String resourcesDir = contentsDir + "/Resources"; U.copyDir("dist/img", resourcesDir + "/img"); U.copyDir("dist/help", resourcesDir + "/help"); U.copyDir("dist/lang", resourcesDir + "/lang"); updateManualVersionNumber(new File(resourcesDir, "help")); U.copyBinaryFile("dist/VaticanFetcher.icns", U.format("%s/%s.icns", resourcesDir, appName)); deployInfoPlist(new File(contentsDir)); String excludedLibs = U.readPatterns("lib/excluded_jar_patterns.txt"); U.copyFlatten("lib", resourcesDir + "/lib", "**/*.jar", excludedLibs); U.copyFlatten("lib", resourcesDir + "/lib/swt", "**/swt*mac*.jar", null); U.copyFlatten("lib", resourcesDir + "/lib", "**/*.dylib", null); String jarName = U.removePrefix(tmpMainJar.getName(), "macosx_"); String dstMainJar = U.format("%s/lib/%s", resourcesDir, jarName); U.copyBinaryFile(tmpMainJar.getPath(), dstMainJar); String launcher = U.format("%s/MacOS/%s", contentsDir, appName); U.copyTextFile("dist/launchers/launcher-macosx-app.sh", launcher, LineSep.UNIX, "${app_name}", appName, "${main_class}", Main.class.getName()); makeExecutable("Cannot make the Mac OS X launcher shell script executable.", launcher); U.copyTextFile("dist/paths.txt", resourcesDir + "/misc/paths.txt", LineSep.WINDOWS); U.copyBinaryFile("build/tmp/licenses.zip", resourcesDir + "/misc/licenses.zip"); if (Util.IS_MAC_OS_X) { String dmgPath = U.format("build/%s-%s.dmg", appName, version); U.exec("hdiutil create -srcfolder %s %s", appDir, dmgPath); } } private static void updateManualVersionNumber(File helpDir) throws Exception { for (File dir : Util.listFiles(helpDir)) { File manFile = new File(dir, "VaticanFetcher_Manual.html"); String manPath = manFile.getPath(); String contents = U.read(manPath); contents = UtilGlobal.replace(manPath, contents, "${version}", version); U.write(contents, manPath); } } private static void runTests() { Util.println("Running tests..."); final List<String> classNames = new ArrayList<String>(); new FileWalker() { protected void handleFile(File file) { String name = file.getName(); if (!name.endsWith(".java")) return; name = Util.splitFilename(name)[0]; if (!name.startsWith("Test") && !name.endsWith("Test")) return; String path = file.getPath(); int start = "src/".length(); int end = path.length() - ".java".length(); path = path.substring(start, end); path = path.replace("/", ".").replace("\\", "."); if (path.equals(TestFiles.class.getName())) return; classNames.add(path); } }.run(new File("src")); Collections.sort(classNames); JUnitCore junit = new JUnitCore(); junit.addListener(new RunListener() { public void testFailure(Failure failure) throws Exception { Util.printErr(Strings.repeat(" ", 8) + "FAILED"); } }); for (String className : classNames) { /* * AppUtil.Const must be cleared before each test, otherwise one test class could load * AppUtil.Const and thereby hide AppUtil.Const loading failures in subsequent tests. */ AppUtil.Const.clear(); try { Class<?> clazz = Class.forName(className); Util.println(Strings.repeat(" ", 4) + className); junit.run(clazz); } catch (ClassNotFoundException e) { Util.printErr(e); } } AppUtil.Const.clear(); } }