Java tutorial
package org.codehaus.tycho.eclipsepackaging; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.BufferedWriter; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.StringWriter; import java.util.Arrays; import java.util.Enumeration; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Properties; import java.util.Set; import java.util.StringTokenizer; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.project.MavenProject; import org.codehaus.plexus.archiver.ArchiverException; import org.codehaus.plexus.archiver.util.ArchiveEntryUtils; import org.codehaus.plexus.archiver.zip.ZipArchiver; import org.codehaus.plexus.component.repository.exception.ComponentLookupException; import org.codehaus.plexus.util.DirectoryScanner; import org.codehaus.plexus.util.FileUtils; import org.codehaus.plexus.util.IOUtil; import org.codehaus.plexus.util.SelectorUtils; import org.codehaus.plexus.util.io.RawInputStreamFacade; import org.codehaus.tycho.ArtifactDependencyVisitor; import org.codehaus.tycho.ArtifactDependencyWalker; import org.codehaus.tycho.ArtifactDescription; import org.codehaus.tycho.PluginDescription; import org.codehaus.tycho.TargetEnvironment; import org.codehaus.tycho.TargetPlatformConfiguration; import org.codehaus.tycho.TychoConstants; import org.codehaus.tycho.TychoProject; import org.codehaus.tycho.model.BundleConfiguration; import org.codehaus.tycho.model.ProductConfiguration; import org.codehaus.tycho.osgitools.BundleReader; import org.codehaus.tycho.utils.PlatformPropertiesUtils; import org.eclipse.pde.internal.swt.tools.IconExe; /** * @goal product-export */ public class ProductExportMojo extends AbstractTychoPackagingMojo { /** key in the project context to easily access the environments. Contains List<TargetEnvironment> */ public static String PRODUCT_EXPORT_ENVIRONMENTS = TychoConstants.CTX_BASENAME + "/ProductExportMojo/environments"; /** key in the project context to easily access the expandedProductConfigurationFile */ public static String PRODUCT_EXPORT_EXPANDED_PRODUCT_CONFIGURATION_FILE = TychoConstants.CTX_BASENAME + "/ProductExportMojo/expandedProductConfigurationFile"; /** key in the project context to easily access if we build a single environment or not. Contains a Boolean. */ public static String PRODUCT_EXPORT_SEPARATE_ENVIRONMENTS = TychoConstants.CTX_BASENAME + "/ProductExportMojo/separateEnvironments"; /** key in the project context to easily access if we produce a p2 enable product. Contains a Boolean. */ public static String PRODUCT_EXPORT_ENABLE_P2 = TychoConstants.CTX_BASENAME + "/ProductExportMojo/enableP2"; /** * The product configuration, a .product file. This file manages all aspects of a product definition from its * constituent plug-ins to configuration files to branding. * * @parameter expression="${productConfiguration}" default-value="${project.basedir}/${project.artifactId}.product" */ private File productConfigurationFile; /** * @parameter expression="${productConfiguration}/../p2.inf" */ private File p2inf; /** * Location of generated .product file with all versions replaced with their expanded values. * * @parameter expression="${project.build.directory}/${project.artifactId}.product" */ private File expandedProductFile; /** * Parsed product configuration file */ private ProductConfiguration productConfiguration; /** * @parameter * @deprecated use target-platform-configuration <environments/> element */ private TargetEnvironment[] environments; /** expression="${tycho.product.enableP2}" * @parameter default-value="true" */ private boolean enableP2; /** * @parameter default-value="false" */ private boolean includeSources; /** * If true (the default), produce separate directory structure for each supported runtime environment. * * @parameter default-value="true" */ private boolean separateEnvironments = true; /** * @parameter expression="${tycho.product.createArchive}" default-value="true" */ private boolean createProductArchive; /** * If true, all included features and bundles will be packed. If false (the default), all features will be unpacked * and bundles will honour unpack value of <plugin/> element. * * @parameter default-value="false" */ private boolean forcePackedDependencies; /** * @component */ private BundleReader manifestReader; public void execute() throws MojoExecutionException, MojoFailureException { if (!productConfigurationFile.exists()) { throw new MojoExecutionException( "Product configuration file not found " + productConfigurationFile.getAbsolutePath()); } try { getLog().debug("Parsing productConfiguration"); productConfiguration = ProductConfiguration.read(productConfigurationFile); } catch (IOException e) { throw new MojoExecutionException("Error reading product configuration file", e); } // build results will vary from system to system without explicit target environment configuration if (productConfiguration.includeLaunchers() && getTargetPlatformConfiguration().isImplicitTargetEnvironment() && environments == null) { throw new MojoFailureException( "Product includes native launcher but no target environment was specified"); } if (separateEnvironments) { List<TargetEnvironment> environments = getEnvironments(); for (TargetEnvironment environment : getEnvironments()) { File target = getTarget(environment); File targetEclipse = new File(target, "eclipse"); targetEclipse.mkdirs(); generateDotEclipseProduct(targetEclipse); includeRootFiles(environment, targetEclipse); ProductAssembler assembler = new ProductAssembler(session, manifestReader, targetEclipse, environment); assembler.setIncludeSources(includeSources); getDependencyWalker(environment).walk(assembler); generateConfigIni(environment, targetEclipse); generateLauncherIni(environment, targetEclipse); if (productConfiguration.includeLaunchers()) { copyExecutable(environment, targetEclipse); } //in the "expanded" product file specify the config.ini file //otherwise p2's ProductPublisher does not read the config.ini //file and does not generate the bundles.info. //see org.eclipse.equinox.p2.publisher.eclipse#createDataLoader try { productConfiguration.setConfigIni( targetEclipse.getCanonicalPath() + "/configuration/config.ini", environment.getWs()); } catch (IOException ioe) { productConfiguration.setConfigIni(targetEclipse.getAbsolutePath() + "/configuration/config.ini", environment.getWs()); } } } else { File target = getTarget(null); File targetEclipse = new File(target, "eclipse"); targetEclipse.mkdirs(); generateDotEclipseProduct(targetEclipse); generateConfigIni(null, targetEclipse); for (TargetEnvironment environment : getEnvironments()) { includeRootFiles(environment, targetEclipse); } ProductAssembler assembler = new ProductAssembler(session, manifestReader, targetEclipse, null); assembler.setIncludeSources(includeSources); if (forcePackedDependencies) { assembler.setUnpackFeatures(false); assembler.setUnpackPlugins(false); } getDependencyWalker().walk(assembler); if (productConfiguration.includeLaunchers()) { for (TargetEnvironment environment : getEnvironments()) { copyExecutable(environment, targetEclipse); } } } // String version = getTychoProjectFacet().getArtifactKey( project ).getVersion(); // String productVersion = VersioningHelper.getExpandedVersion( project, version ); // productConfiguration.setVersion( productVersion.toString() ); try { ProductConfiguration.write(productConfiguration, expandedProductFile); if (p2inf.canRead()) { FileUtils.copyFile(p2inf, new File(expandedProductFile.getParentFile(), p2inf.getName())); } } catch (IOException e) { throw new MojoExecutionException("Error writing expanded product configuration file", e); } project.setContextValue(PRODUCT_EXPORT_ENVIRONMENTS, getEnvironments()); project.setContextValue(PRODUCT_EXPORT_ENABLE_P2, enableP2); project.setContextValue(PRODUCT_EXPORT_EXPANDED_PRODUCT_CONFIGURATION_FILE, expandedProductFile); project.setContextValue(PRODUCT_EXPORT_SEPARATE_ENVIRONMENTS, separateEnvironments); } private ArtifactDependencyWalker getDependencyWalker(TargetEnvironment environment) { return getTychoProjectFacet(TychoProject.ECLIPSE_APPLICATION).getDependencyWalker(project, environment); } private List<TargetEnvironment> getEnvironments() { if (environments != null) { getLog().warn( "maven-osgi-packaging-plugin <environments/> is deprecated. use target-platform-configuration <environments/>."); return Arrays.asList(environments); } return getTargetPlatformConfiguration().getEnvironments(); } protected TargetPlatformConfiguration getTargetPlatformConfiguration() { TargetPlatformConfiguration configuration = (TargetPlatformConfiguration) project .getContextValue(TychoConstants.CTX_TARGET_PLATFORM_CONFIGURATION); if (configuration == null) { throw new IllegalStateException( "Project build target platform configuration has not been initialized properly."); } return configuration; } private File getTarget(TargetEnvironment environment) { return getTarget(environment, separateEnvironments, project); } static File getTarget(TargetEnvironment environment, boolean separateEnvironments, MavenProject project) { File target; if (separateEnvironments) { target = new File(project.getBuild().getDirectory(), toString(environment)); } else { target = new File(project.getBuild().getDirectory(), "product"); } target.mkdirs(); return target; } static String toString(TargetEnvironment environment) { StringBuilder sb = new StringBuilder(); sb.append(environment.getOs()).append('.').append(environment.getWs()).append('.') .append(environment.getArch()); if (environment.getNl() != null) { sb.append('.').append(environment.getNl()); } return sb.toString(); } /** * Root files are files that must be packaged with an Eclipse install but are not features or plug-ins. These files * are added to the root or to a specified sub folder of the build. * * <pre> * root= * root.<confi>= * root.folder.<subfolder>= * root.<config>.folder.<subfolder>= * </pre> * * Not supported are the properties root.permissions and root.link. * * @see http://help.eclipse.org/ganymede/index.jsp?topic=/org.eclipse.pde.doc.user/tasks/pde_rootfiles.htm * @throws MojoExecutionException */ private void includeRootFiles(TargetEnvironment environment, File target) throws MojoExecutionException { Properties properties = project.getProperties(); String generatedBuildProperties = properties.getProperty("generatedBuildProperties"); getLog().debug("includeRootFiles from " + generatedBuildProperties); if (generatedBuildProperties != null) { Properties rootProperties = new Properties(); try { rootProperties.load(new FileInputStream(new File(project.getBasedir(), generatedBuildProperties))); if (!rootProperties.isEmpty()) { String config = getConfig(environment); String root = "root"; String rootConfig = "root." + config; String rootFolder = "root.folder."; String rootConfigFolder = "root." + config + ".folder."; Set<Entry<Object, Object>> entrySet = rootProperties.entrySet(); for (Iterator iterator = entrySet.iterator(); iterator.hasNext();) { Entry<String, String> entry = (Entry<String, String>) iterator.next(); String key = entry.getKey().trim(); // root= if (root.equals(key)) { handleRootEntry(target, entry.getValue(), null); } // root.xxx= else if (rootConfig.equals(key)) { handleRootEntry(target, entry.getValue(), null); } // root.folder.yyy= else if (key.startsWith(rootFolder)) { String subFolder = entry.getKey().substring((rootFolder.length())); handleRootEntry(target, entry.getValue(), subFolder); } // root.xxx.folder.yyy= else if (key.startsWith(rootConfigFolder)) { String subFolder = entry.getKey().substring((rootConfigFolder.length())); handleRootEntry(target, entry.getValue(), subFolder); } else { getLog().debug("ignoring property " + entry.getKey() + "=" + entry.getValue()); } } } } catch (FileNotFoundException e) { throw new MojoExecutionException("Error including root files for product", e); } catch (IOException e) { throw new MojoExecutionException("Error including root files for product", e); } } } /** * @param rootFileEntry files and directories seperated by semicolons, the syntax is: * <ul> * <li>for a relative file: file:license.html,...</li> * <li>for a absolute file: absolute:file:/eclipse/about.html,...</li> * <li>for a relative folder: rootfiles,...</li> * <li>for a absolute folder: absolute:/eclipse/rootfiles,...</li> * </ul> * @param subFolder the sub folder to which the root file entries are copied to * @throws MojoExecutionException */ private void handleRootEntry(File target, String rootFileEntries, String subFolder) throws MojoExecutionException { StringTokenizer t = new StringTokenizer(rootFileEntries, ","); File destination = target; if (subFolder != null) { destination = new File(target, subFolder); } while (t.hasMoreTokens()) { String rootFileEntry = t.nextToken(); String fileName = rootFileEntry.trim(); boolean isAbsolute = false; if (fileName.startsWith("absolute:")) { isAbsolute = true; fileName = fileName.substring("absolute:".length()); } if (fileName.startsWith("file")) { fileName = fileName.substring("file:".length()); } File source = null; if (!isAbsolute) { source = new File(project.getBasedir(), fileName); } else { source = new File(fileName); } try { if (source.isFile()) { FileUtils.copyFileToDirectory(source, destination); } else if (source.isDirectory()) { FileUtils.copyDirectoryStructure(source, destination); } else { getLog().warn("Skipping root entry " + rootFileEntry); } } catch (IOException e) { throw new MojoExecutionException("Coult not copy root entry " + fileName, e); } } } private String getConfig(TargetEnvironment environment) { String os = environment.getOs(); String ws = environment.getWs(); String arch = environment.getArch(); StringBuffer config = new StringBuffer(os).append(".").append(ws).append(".").append(arch); return config.toString(); } private void createProductArchive(File target, String classifier) throws MojoExecutionException { ZipArchiver zipper; try { zipper = (ZipArchiver) plexus.lookup(ZipArchiver.ROLE, "zip"); } catch (ComponentLookupException e) { throw new MojoExecutionException("Unable to resolve ZipArchiver", e); } StringBuilder filename = new StringBuilder(project.getBuild().getFinalName()); if (separateEnvironments) { filename.append('-').append(classifier); } filename.append(".zip"); File destFile = new File(project.getBuild().getDirectory(), filename.toString()); try { zipper.addDirectory(target); zipper.setDestFile(destFile); zipper.createArchive(); } catch (Exception e) { throw new MojoExecutionException("Error packing product", e); } if (separateEnvironments) { projectHelper.attachArtifact(project, destFile, classifier); } else { // main artifact project.getArtifact().setFile(destFile); } } private void generateDotEclipseProduct(File target) throws MojoExecutionException { getLog().debug("Generating .eclipseproduct"); Properties props = new Properties(); setPropertyIfNotNull(props, "version", productConfiguration.getVersion()); setPropertyIfNotNull(props, "name", productConfiguration.getName()); setPropertyIfNotNull(props, "id", productConfiguration.getId()); File eclipseproduct = new File(target, ".eclipseproduct"); try { FileOutputStream fos = new FileOutputStream(eclipseproduct); props.store(fos, "Eclipse Product File"); fos.close(); } catch (IOException e) { throw new MojoExecutionException("Error creating .eclipseproduct file.", e); } } /** * Transforms the launcherArgs element in the product file into a $launcher.ini file. * @param environment * @param target * @throws MojoExecutionException * @throws MojoFailureException */ private void generateLauncherIni(TargetEnvironment environment, File target) throws MojoExecutionException, MojoFailureException { String os = environment == null ? null : environment.getOs(); String launcherName = productConfiguration.getLauncher() != null ? productConfiguration.getLauncher().getName() : null; if (launcherName == null) { return; } String allProgArgs = productConfiguration.getLauncherArgsForProgram(null); String osProgArgs = os != null ? productConfiguration.getLauncherArgsForProgram(os) : null; String allVmArgs = productConfiguration.getLauncherArgsForVM(null); String osVmArgs = os != null ? productConfiguration.getLauncherArgsForVM(os) : null; if (allProgArgs != null || osProgArgs != null || allVmArgs != null || osVmArgs != null) { File launcherIni = new File(target, launcherName + ".ini"); BufferedWriter writer = null; try { if (!launcherIni.exists()) launcherIni.createNewFile(); boolean osIsWin = os != null && os.indexOf("win") != -1; writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(launcherIni), "UTF-8")); //TODO: output the other arguments that we can live without? -startup and --startup-library ? writeArgLine(allProgArgs, writer, osIsWin); writeArgLine(osProgArgs, writer, osIsWin); if (allVmArgs != null || osVmArgs != null) writeArgLine("-vmargs", writer, osIsWin); writeArgLine(allVmArgs, writer, osIsWin); writeArgLine(osVmArgs, writer, osIsWin); writer.flush(); } catch (IOException e) { throw new MojoExecutionException("Unable to output the " + launcherIni.getName() + " file", e); } finally { if (writer != null) IOUtil.close(writer); } } } private void writeArgLine(String line, BufferedWriter writer, boolean osIsWin) throws IOException { if (line == null || line.length() == 0) { return; } writer.append(line); writer.append(osIsWin ? "\n\r" : "\n"); } /** * Generate a new default config.ini file or use the custom one specified in the environment. * (TYCHO-422). * @param environment The current environment or null for a non environment specific packaging. * @param target The eclipse folder assembled. */ private void generateConfigIni(TargetEnvironment environment, File target) throws MojoExecutionException, MojoFailureException { String os = environment == null ? null : environment.getOs(); String customConfigIni = os != null ? productConfiguration.getConfigIni(os) : null; if (customConfigIni != null && customConfigIni.length() > 0) { getLog().debug("Using " + customConfigIni); if (customConfigIni.charAt(0) == '/') { //relative path to the project. customConfigIni = customConfigIni.substring(1); } File customIniF = new File(project.getBasedir(), customConfigIni); if (!customIniF.exists()) { throw new MojoFailureException("Unable to locate the custom config.ini for " + os + " that should be located at " + customIniF.getAbsolutePath()); } File configsFolder = new File(target, "configuration"); configsFolder.mkdirs(); File destConfigIni = new File(configsFolder, "config.ini"); try { FileUtils.copyFile(customIniF, destConfigIni); } catch (IOException e) { throw new MojoFailureException("Unable to copy the custom config.ini for " + os + " that located at " + customIniF.getAbsolutePath(), e); } } getLog().debug("Generating config.ini"); Properties props = new Properties(); String id = productConfiguration.getProduct(); if (id != null) { String splash = id.split("\\.")[0]; setPropertyIfNotNull(props, "osgi.splashPath", "platform:/base/plugins/" + splash); } //when there is no application //add the proper parameters for this runtime application: if (productConfiguration.getApplication() == null) { setPropertyIfNotNull(props, "eclipse.ignoreApp", "true"); setPropertyIfNotNull(props, "osgi.noShutdown", "true"); } setPropertyIfNotNull(props, "eclipse.product", id); // TODO check if there are any other levels setPropertyIfNotNull(props, "osgi.bundles.defaultStartLevel", "4"); File configsFolder = new File(target, "configuration"); configsFolder.mkdirs(); generateOSGiBundles(props, environment, configsFolder); File configIni = new File(configsFolder, "config.ini"); try { FileOutputStream fos = new FileOutputStream(configIni); props.store(fos, "Product Runtime Configuration File"); fos.close(); } catch (IOException e) { throw new MojoExecutionException("Error creating .eclipseproduct file.", e); } } /** * Semi-smart generation of the osgi.bundle property for the config.ini file. * Looks into the configurations element of the product file where the plugins to start are listed. * <ol> * <li>if the parameter forceConfigIniOsgiBundlesListAll is set then for to * generate or not generate the whole list. otherwise: </li> * <li>If there is no such thing at all (for example tycho-runtime product file) then osgi.bundle is the list * of all bundles packaged in the product with auto started if preset: * org.eclipse.equinox.common, org.eclipse.update.configurator, org.eclipse.core.runtime, * org.eclipse.equinox.simpleconfigurator, org.eclipse.equinox.ds</li> * <li>If there is a list of bundles to autostart and org.eclipse.update.configurator belongs to them * or org.eclipse.equinox.simpleconfigurator is one of them, then don't list all the p2 bundles.</li> * </ol> */ private void generateOSGiBundles(Properties props, TargetEnvironment environment, File configurationFolder) throws MojoFailureException, MojoExecutionException { Map<String, BundleConfiguration> bundlesToStart = productConfiguration.getPluginConfiguration(); Map<String, PluginDescription> bundles = new LinkedHashMap<String, PluginDescription>( getBundles(environment)); boolean autoListAllBundles = enableP2 ? false : true; if (bundlesToStart == null || bundlesToStart.isEmpty()) { autoListAllBundles = true; bundlesToStart = new HashMap<String, BundleConfiguration>(); // This is the well known set of bundles for Eclipse based RCP application for 3.3 till 3.6 without p2 // if we see the p2's simpleconfigurator then we add it and that makes it work for p2 (should) // here is the doc of what the PDEBuild does: // http://help.eclipse.org/galileo/index.jsp?topic=/org.eclipse.pde.doc.user/tasks/pde_p2_configuringproducts.htm bundlesToStart.put("org.eclipse.equinox.common", // new BundleConfiguration("org.eclipse.equinox.common", 2, true)); if (bundles.containsKey("org.eclipse.update.configurator")) { bundlesToStart.put("org.eclipse.update.configurator", // new BundleConfiguration("org.eclipse.update.configurator", 4, true)); autoListAllBundles = false; } if (bundles.containsKey("org.eclipse.core.runtime")) { bundlesToStart.put("org.eclipse.core.runtime", // new BundleConfiguration("org.eclipse.core.runtime", -1, true)); } if (bundles.containsKey("org.eclipse.equinox.ds")) { bundlesToStart.put("org.eclipse.equinox.ds", // new BundleConfiguration("org.eclipse.equinox.ds", 1, true)); } if (bundles.containsKey("org.eclipse.equinox.simpleconfigurator")) { //for now we choose to not consider that simpleconfigurator is actually in use bundlesToStart.put("org.eclipse.equinox.simpleconfigurator", // new BundleConfiguration("org.eclipse.equinox.simpleconfigurator", 1, true)); } } if (!autoListAllBundles) { //only list the bundles explicitly declared in the product file's configuration element: Map<String, PluginDescription> actuallyListedBundles = new LinkedHashMap<String, PluginDescription>(); for (BundleConfiguration bundleConf : bundlesToStart.values()) { PluginDescription pluginDesc = bundles.get(bundleConf.getId()); if (pluginDesc == null) { throw new MojoFailureException("No plugin " + bundleConf.getId() + " is packaged by the product " + productConfiguration.getId() + " although it is listed in the plugins to start."); } actuallyListedBundles.put(pluginDesc.getKey().getId(), pluginDesc); } bundles = actuallyListedBundles; } StringBuilder osgiBundles = new StringBuilder(); for (PluginDescription plugin : bundles.values()) { String bundleId = plugin.getKey().getId(); // reverse engineering discovered // this plugin is not present on config.ini, and if so nothing // starts if ("org.eclipse.osgi".equals(bundleId)) { continue; } if (osgiBundles.length() > 0) { osgiBundles.append(','); } osgiBundles.append(bundleId); BundleConfiguration startup = bundlesToStart.get(bundleId); if (startup != null) { osgiBundles.append('@'); if (startup.getStartLevel() > 0) { osgiBundles.append(startup.getStartLevel()); } if (startup.isAutoStart()) { if (startup.getStartLevel() > 0) { osgiBundles.append(':'); } osgiBundles.append("start"); } } } setPropertyIfNotNull(props, "osgi.bundles", osgiBundles.toString()); } private Map<String, PluginDescription> getBundles(TargetEnvironment environment) { final Map<String, PluginDescription> bundles = new LinkedHashMap<String, PluginDescription>(); getDependencyWalker(environment).walk(new ArtifactDependencyVisitor() { @Override public void visitPlugin(PluginDescription plugin) { bundles.put(plugin.getKey().getId(), plugin); } }); return bundles; } private void copyExecutable(TargetEnvironment environment, File target) throws MojoExecutionException, MojoFailureException { getLog().debug("Creating launcher"); ArtifactDescription artifact = getTargetPlatform().getArtifact(TychoProject.ECLIPSE_FEATURE, "org.eclipse.equinox.executable", null); if (artifact == null) { throw new MojoExecutionException("Native launcher is not found for " + environment.toString()); } File location = artifact.getLocation(); String os = environment.getOs(); String ws = environment.getWs(); String arch = environment.getArch(); try { String launcherRelPath = "bin/" + ws + "/" + os + "/" + arch + "/"; String excludes = "**/eclipsec*"; if (location.isDirectory()) { copyDirectory(new File(location, launcherRelPath), target, excludes); } else { unzipDirectory(location, launcherRelPath, target, excludes); } } catch (IOException e) { throw new MojoExecutionException("Unable to copy launcher executable", e); } File launcher = getLauncher(environment, target); // make launcher executable try { getLog().debug("running chmod"); ArchiveEntryUtils.chmod(launcher, 0755, null); } catch (ArchiverException e) { throw new MojoExecutionException("Unable to make launcher being executable", e); } File osxEclipseApp = null; // Rename launcher if (productConfiguration.getLauncher() != null && productConfiguration.getLauncher().getName() != null) { String launcherName = productConfiguration.getLauncher().getName(); String newName = launcherName; // win32 has extensions if (PlatformPropertiesUtils.OS_WIN32.equals(os)) { String extension = FileUtils.getExtension(launcher.getAbsolutePath()); newName = launcherName + "." + extension; } else if (PlatformPropertiesUtils.OS_MACOSX.equals(os)) { // the launcher is renamed to "eclipse", because // this is the value of the CFBundleExecutable // property within the Info.plist file. // see http://jira.codehaus.org/browse/MNGECLIPSE-1087 newName = "eclipse"; } getLog().debug("Renaming launcher to " + newName); File newLauncher = new File(launcher.getParentFile(), newName); if (!launcher.renameTo(newLauncher)) { throw new MojoExecutionException("Could not rename native launcher to " + newName); } launcher = newLauncher; // macosx: the *.app directory is renamed to the // product configuration launcher name // see http://jira.codehaus.org/browse/MNGECLIPSE-1087 if (PlatformPropertiesUtils.OS_MACOSX.equals(os)) { newName = launcherName + ".app"; getLog().debug("Renaming Eclipse.app to " + newName); File eclipseApp = new File(target, "Eclipse.app"); osxEclipseApp = new File(eclipseApp.getParentFile(), newName); eclipseApp.renameTo(osxEclipseApp); // ToDo: the "Info.plist" file must be patched, so that the // property "CFBundleName" has the value of the // launcherName variable } } // icons if (productConfiguration.getLauncher() != null) { if (PlatformPropertiesUtils.OS_WIN32.equals(os)) { getLog().debug("win32 icons"); List<String> icons = productConfiguration.getW32Icons(); if (icons != null) { getLog().debug(icons.toString()); try { String[] args = new String[icons.size() + 1]; args[0] = launcher.getAbsolutePath(); int pos = 1; for (String string : icons) { args[pos] = string; pos++; } IconExe.main(args); } catch (Exception e) { throw new MojoExecutionException("Unable to replace icons", e); } } else { getLog().debug("icons is null"); } } else if (PlatformPropertiesUtils.OS_LINUX.equals(os)) { String icon = productConfiguration.getLinuxIcon(); if (icon != null) { try { File sourceXPM = new File(project.getBasedir(), removeFirstSegment(icon)); File targetXPM = new File(launcher.getParentFile(), "icon.xpm"); FileUtils.copyFile(sourceXPM, targetXPM); } catch (IOException e) { throw new MojoExecutionException("Unable to create ico.xpm", e); } } } else if (PlatformPropertiesUtils.OS_MACOSX.equals(os)) { String icon = productConfiguration.getMacIcon(); if (icon != null) { try { if (osxEclipseApp == null) { osxEclipseApp = new File(target, "Eclipse.app"); } File source = new File(project.getBasedir(), removeFirstSegment(icon)); File targetFolder = new File(osxEclipseApp, "/Resources/" + source.getName()); FileUtils.copyFile(source, targetFolder); // Modify eclipse.ini File iniFile = new File(osxEclipseApp + "/Contents/MacOS/eclipse.ini"); if (iniFile.exists() && iniFile.canWrite()) { StringBuffer buf = readFileToString(iniFile); int pos = buf.indexOf("Eclipse.icns"); buf.replace(pos, pos + 12, source.getName()); writeStringToFile(iniFile, buf.toString()); } } catch (Exception e) { throw new MojoExecutionException("Unable to create macosx icon", e); } } } } } private void writeStringToFile(File iniFile, String string) throws IOException { OutputStream os = new BufferedOutputStream(new FileOutputStream(iniFile)); try { IOUtil.copy(string, os); } finally { IOUtil.close(os); } } private StringBuffer readFileToString(File iniFile) throws IOException { InputStream is = new BufferedInputStream(new FileInputStream(iniFile)); try { StringWriter buffer = new StringWriter(); IOUtil.copy(is, buffer, "UTF-8"); return buffer.getBuffer(); } finally { IOUtil.close(is); } } private void unzipDirectory(File source, String sourceRelPath, File target, String excludes) throws IOException { ZipFile zip = new ZipFile(source); try { Enumeration<? extends ZipEntry> entries = zip.entries(); while (entries.hasMoreElements()) { ZipEntry entry = entries.nextElement(); if (entry.isDirectory()) { continue; } String name = entry.getName(); if (name.startsWith(sourceRelPath) && !SelectorUtils.matchPath(excludes, name)) { File targetFile = new File(target, name.substring(sourceRelPath.length())); targetFile.getParentFile().mkdirs(); FileUtils.copyStreamToFile(new RawInputStreamFacade(zip.getInputStream(entry)), targetFile); } } } finally { zip.close(); } } private void copyDirectory(File source, File target, String excludes) throws IOException { DirectoryScanner ds = new DirectoryScanner(); ds.setBasedir(source); ds.setExcludes(new String[] { excludes }); ds.scan(); for (String relPath : ds.getIncludedFiles()) { File targetFile = new File(target, relPath); targetFile.getParentFile().mkdirs(); FileUtils.copyFile(new File(source, relPath), targetFile); } } private String removeFirstSegment(String path) { int idx = path.indexOf('/'); if (idx < 0) { return null; } if (idx == 0) { idx = path.indexOf('/', 1); } if (idx < 0) { return null; } return path.substring(idx); } private File getLauncher(TargetEnvironment environment, File target) throws MojoExecutionException { String os = environment.getOs(); if (PlatformPropertiesUtils.OS_WIN32.equals(os)) { return new File(target, "launcher.exe"); } if (PlatformPropertiesUtils.OS_LINUX.equals(os) || PlatformPropertiesUtils.OS_SOLARIS.equals(os) || PlatformPropertiesUtils.OS_HPUX.equals(os) || PlatformPropertiesUtils.OS_AIX.equals(os)) { return new File(target, "launcher"); } if (PlatformPropertiesUtils.OS_MACOSX.equals(os)) { // TODO need to check this at macos return new File(target, "Eclipse.app/Contents/MacOS/launcher"); } throw new MojoExecutionException("Unexpected OS: " + os); } private void setPropertyIfNotNull(Properties properties, String key, String value) { if (value != null) { properties.setProperty(key, value); } } }