001: /*******************************************************************************
002: * Copyright (c) 2005, 2007 IBM Corporation and others. All rights reserved.
003: * This program and the accompanying materials are made available under the
004: * terms of the Eclipse Public License v1.0 which accompanies this distribution,
005: * and is available at http://www.eclipse.org/legal/epl-v10.html
006: *
007: * Contributors: IBM Corporation - initial API and implementation
008: ******************************************************************************/package org.eclipse.pde.internal.build;
009:
010: import java.io.*;
011: import org.eclipse.pde.internal.swt.tools.IconExe;
012:
013: /**
014: *
015: */
016: public class BrandingIron implements IXMLConstants {
017: private static final String MARKER_NAME = "%EXECUTABLE_NAME%"; //$NON-NLS-1$
018: private static final String BUNDLE_NAME = "%BUNDLE_NAME%"; //$NON-NLS-1$
019: private static final String ICON_NAME = "%ICON_NAME%"; //$NON-NLS-1$
020: private static final String MARKER_KEY = "<key>CFBundleExecutable</key>"; //$NON-NLS-1$
021: private static final String BUNDLE_KEY = "<key>CFBundleName</key>"; //$NON-NLS-1$
022: private static final String ICON_KEY = "<key>CFBundleIconFile</key>"; //$NON-NLS-1$
023: private static final String STRING_START = "<string>"; //$NON-NLS-1$
024: private static final String STRING_END = "</string>"; //$NON-NLS-1$
025: private static final String XDOC_ICON = "-Xdock:icon=../Resources/Eclipse.icns"; //$NON-NLS-1$
026:
027: private String[] icons = null;
028: private String root;
029: private String name;
030: private String os = "win32"; //$NON-NLS-1$
031: private boolean brandIcons = true;
032:
033: public void setName(String value) {
034: name = value;
035: }
036:
037: public void setIcons(String value) {
038: icons = value.split(",\\s*"); //$NON-NLS-1$
039: if (icons[0].startsWith("${")) { //$NON-NLS-1$
040: if (icons.length > 1) {
041: String[] temp = new String[icons.length - 1];
042: System.arraycopy(icons, 1, temp, 0, temp.length);
043: icons = temp;
044: } else {
045: icons = null;
046: }
047: }
048: }
049:
050: public void setRoot(String value) {
051: root = value;
052: }
053:
054: public void brand() throws Exception {
055: // if the name property is not set it will be ${launcher.name} so just bail.
056: if (name.startsWith("${")) //$NON-NLS-1$
057: return;
058:
059: if (icons == null || icons[0].startsWith("${")) //$NON-NLS-1$
060: brandIcons = false;
061:
062: // if the root does not exists (happens in some packaging cases) or
063: // there is already a file with target name and we don't need to update its icons, don't do anything
064: String testName = os.equals("win32") ? name + ".exe" : name; //$NON-NLS-1$ //$NON-NLS-2$
065: if (!new File(root).exists()
066: || (!brandIcons && new File(root, testName).exists()))
067: return;
068:
069: if ("win32".equals(os)) //$NON-NLS-1$
070: brandWindows();
071: if ("linux".equals(os)) //$NON-NLS-1$
072: brandLinux();
073: if ("solaris".equals(os)) //$NON-NLS-1$
074: brandSolaris();
075: if ("macosx".equals(os)) //$NON-NLS-1$
076: brandMac();
077: if ("aix".equals(os)) //$NON-NLS-1$
078: brandAIX();
079: if ("hpux".equals(os)) //$NON-NLS-1$
080: brandHPUX();
081: }
082:
083: private void brandAIX() {
084: renameLauncher();
085: }
086:
087: private void brandHPUX() {
088: renameLauncher();
089: }
090:
091: private void brandLinux() throws Exception {
092: renameLauncher();
093: if (brandIcons)
094: copy(new File(icons[0]), new File(root, "icon.xpm")); //$NON-NLS-1$
095: }
096:
097: private void brandSolaris() throws Exception {
098: renameLauncher();
099: if (brandIcons == false)
100: return;
101:
102: for (int i = 0; i < icons.length; i++) {
103: String icon = icons[i];
104: if (icon.endsWith(".l.pm")) //$NON-NLS-1$
105: copy(new File(icon), new File(root, name + ".l.pm")); //$NON-NLS-1$
106: if (icon.endsWith(".m.pm")) //$NON-NLS-1$
107: copy(new File(icon), new File(root, name + ".m.pm")); //$NON-NLS-1$
108: if (icon.endsWith(".s.pm")) //$NON-NLS-1$
109: copy(new File(icon), new File(root, name + ".s.pm")); //$NON-NLS-1$
110: if (icon.endsWith(".t.pm")) //$NON-NLS-1$
111: copy(new File(icon), new File(root, name + ".t.pm")); //$NON-NLS-1$
112: }
113: }
114:
115: private void brandMac() throws Exception {
116: //Initially the files are in: <root>/Eclipse.app/
117: //and they must appear in <root>/MyAppName.app/
118: //Because java does not support the rename of a folder, files are copied.
119:
120: //Initialize the target folders
121: String target = root + '/' + name + ".app/Contents"; //$NON-NLS-1$
122: new File(target).mkdirs();
123: new File(target + "/MacOS").mkdirs(); //$NON-NLS-1$
124: new File(target + "/Resources").mkdirs(); //$NON-NLS-1$
125:
126: String initialRoot = root + "/Launcher.app/Contents"; //$NON-NLS-1$
127: if (!new File(initialRoot).exists())
128: initialRoot = root + "/Eclipse.app/Contents"; //$NON-NLS-1$
129: copyMacLauncher(initialRoot, target);
130: String iconName = ""; //$NON-NLS-1$
131: File splashApp = new File(initialRoot, "Resources/Splash.app"); //$NON-NLS-1$
132: if (brandIcons) {
133: File icon = new File(icons[0]);
134: iconName = icon.getName();
135: copy(icon,
136: new File(target + "/Resources/" + icon.getName())); //$NON-NLS-1$
137: new File(initialRoot + "/Resources/Eclipse.icns").delete(); //$NON-NLS-1$
138: if (!splashApp.exists())
139: new File(initialRoot + "/Resources/").delete(); //$NON-NLS-1$
140: }
141: copyMacIni(initialRoot, target, iconName);
142: modifyInfoPListFile(initialRoot, target, iconName);
143: if (splashApp.exists()) {
144: brandMacSplash(initialRoot, target, iconName);
145: }
146:
147: File rootFolder = new File(initialRoot);
148: rootFolder.delete();
149: if (rootFolder.exists()) {
150: //if the rootFolder still exists, its because there were other files that need to be moved over
151: moveContents(rootFolder, new File(target));
152: }
153: rootFolder.getParentFile().delete();
154: }
155:
156: /**
157: * Brand the splash.app Info.plist and link or copy the mac launcher.
158: * It is assumed that the mac launcher has been branded already.
159: * @param initialRoot
160: * @param target
161: * @param iconName
162: */
163: private void brandMacSplash(String initialRoot, String target,
164: String iconName) {
165: String splashContents = "/Resources/Splash.app/Contents"; //$NON-NLS-1$
166: modifyInfoPListFile(initialRoot + splashContents, target
167: + splashContents, iconName);
168:
169: //link the MacOS launcher for the splash app
170: int result = -1;
171: String osName = System.getProperty("os.name"); //$NON-NLS-1$
172: if (osName != null && !osName.startsWith("Windows")) { //$NON-NLS-1$
173: try {
174: String[] command = new String[] {
175: "ln", "-sf", "../../../MacOS/" + name, "MacOS/" + name }; //$NON-NLS-1$ //$NON-NLS-2$//$NON-NLS-3$//$NON-NLS-4$
176: File linkDir = new File(target, splashContents);
177: Process proc = Runtime.getRuntime().exec(command, null,
178: linkDir);
179: result = proc.waitFor();
180: } catch (IOException e) {
181: // ignore
182: } catch (InterruptedException e) {
183: // ignore
184: }
185: }
186:
187: if (result != 0) {
188: //ln failed, or we are on windows, just copy the executable instead
189: File macOSDir = new File(target, "MacOS"); //$NON-NLS-1$
190: File splashMacOSDir = new File(target, splashContents
191: + "/MacOS"); //$NON-NLS-1$
192: splashMacOSDir.mkdirs();
193: try {
194: File targetFile = new File(splashMacOSDir, name);
195: copy(new File(macOSDir, name), targetFile);
196: try {
197: Runtime
198: .getRuntime()
199: .exec(
200: new String[] {
201: "chmod", "755", targetFile.getAbsolutePath() }); //$NON-NLS-1$ //$NON-NLS-2$
202: } catch (IOException e) {
203: // ignore
204: }
205: } catch (IOException e) {
206: System.out
207: .println("Could not copy macosx splash launcher"); //$NON-NLS-1$
208: }
209: }
210: }
211:
212: private void moveContents(File source, File target) {
213: if (!source.exists())
214: return;
215:
216: try {
217: if (source.getCanonicalFile().equals(
218: target.getCanonicalFile()))
219: return;
220: } catch (IOException e) {
221: System.out.println("Could not copy macosx resources."); //$NON-NLS-1$
222: return;
223: }
224:
225: target.getParentFile().mkdirs();
226: if (source.isDirectory()) {
227: target.mkdirs();
228: File[] contents = source.listFiles();
229: for (int i = 0; i < contents.length; i++) {
230: File dest = new File(target, contents[i].getName());
231: if (contents[i].isFile())
232: contents[i].renameTo(dest);
233: else
234: moveContents(contents[i], dest);
235: }
236: source.delete();
237: } else {
238: source.renameTo(target);
239: }
240: }
241:
242: private void brandWindows() throws Exception {
243: File templateLauncher = new File(root, "launcher.exe"); //$NON-NLS-1$
244: if (!templateLauncher.exists())
245: templateLauncher = new File(root, "eclipse.exe"); //$NON-NLS-1$
246: if (brandIcons) {
247: String[] args = new String[icons.length + 1];
248: args[0] = templateLauncher.getAbsolutePath();
249: System.arraycopy(icons, 0, args, 1, icons.length);
250: IconExe.main(args);
251: }
252: templateLauncher.renameTo(new File(root, name + ".exe")); //$NON-NLS-1$
253: }
254:
255: private void renameLauncher() {
256: if (!new File(root, "launcher").renameTo(new File(root, name))) //$NON-NLS-1$
257: new File(root, "eclipse").renameTo(new File(root, name)); //$NON-NLS-1$
258: }
259:
260: private void copyMacLauncher(String initialRoot, String target) {
261: String targetLauncher = target + "/MacOS/"; //$NON-NLS-1$
262: File launcher = new File(initialRoot + "/MacOS/launcher"); //$NON-NLS-1$
263: File eclipseLauncher = new File(initialRoot + "/MacOS/eclipse"); //$NON-NLS-1$
264: if (!launcher.exists()) {
265: launcher = eclipseLauncher;
266: } else if (eclipseLauncher.exists()) {
267: //we may actually have both if exporting from the mac
268: eclipseLauncher.delete();
269: }
270: File targetFile = new File(targetLauncher, name);
271: try {
272: if (targetFile.getCanonicalFile().equals(
273: launcher.getCanonicalFile())) {
274: try {
275: //Force the executable bit on the exe because it has been lost when copying the file
276: Runtime
277: .getRuntime()
278: .exec(
279: new String[] {
280: "chmod", "755", targetFile.getAbsolutePath() }); //$NON-NLS-1$ //$NON-NLS-2$
281: } catch (IOException e) {
282: //ignore
283: }
284: return;
285: }
286: copy(launcher, targetFile);
287: } catch (IOException e) {
288: System.out.println("Could not copy macosx launcher"); //$NON-NLS-1$
289: return;
290: }
291: try {
292: //Force the executable bit on the exe because it has been lost when copying the file
293: Runtime
294: .getRuntime()
295: .exec(
296: new String[] {
297: "chmod", "755", targetFile.getAbsolutePath() }); //$NON-NLS-1$ //$NON-NLS-2$
298: } catch (IOException e) {
299: //ignore
300: }
301: launcher.delete();
302: launcher.getParentFile().delete();
303: }
304:
305: private void copyMacIni(String initialRoot, String target,
306: String iconName) {
307: File brandedIni = new File(initialRoot,
308: "/MacOS/" + name + ".ini"); //$NON-NLS-1$ //$NON-NLS-2$
309:
310: File ini = new File(initialRoot, "/MacOS/eclipse.ini"); //$NON-NLS-1$
311: if (!ini.exists() && !brandedIni.exists())
312: return;
313:
314: if (brandedIni.exists() && ini.exists()) {
315: //take the one that is already branded
316: ini.delete();
317: ini = brandedIni;
318: }
319:
320: StringBuffer buffer;
321: try {
322: buffer = readFile(ini);
323: ini.delete();
324: } catch (IOException e) {
325: System.out.println("Impossible to brand ini file"); //$NON-NLS-1$
326: return;
327: }
328:
329: if (iconName.length() > 0) {
330: int xdoc = scan(buffer, 0, XDOC_ICON);
331: if (xdoc != -1) {
332: String icns = XDOC_ICON.replaceFirst(
333: "Eclipse.icns", iconName); //$NON-NLS-1$
334: buffer.replace(xdoc, xdoc + XDOC_ICON.length(), icns);
335: }
336: }
337:
338: try {
339: File targetFile = new File(target,
340: "/MacOS/" + name + ".ini"); //$NON-NLS-1$//$NON-NLS-2$
341: transferStreams(new ByteArrayInputStream(buffer.toString()
342: .getBytes()), new FileOutputStream(targetFile));
343: } catch (FileNotFoundException e) {
344: System.out.println("Impossible to brand ini file"); //$NON-NLS-1$
345: return;
346: } catch (IOException e) {
347: System.out.println("Impossible to brand ini file"); //$NON-NLS-1$
348: return;
349: }
350: }
351:
352: private void modifyInfoPListFile(String initialRoot,
353: String targetRoot, String iconName) {
354: File infoPList = new File(initialRoot, "Info.plist"); //$NON-NLS-1$
355: StringBuffer buffer;
356: try {
357: buffer = readFile(infoPList);
358: } catch (IOException e) {
359: System.out.println("Impossible to brand info.plist file"); //$NON-NLS-1$
360: return;
361: }
362: int exePos = scan(buffer, 0, MARKER_NAME);
363: if (exePos != -1)
364: buffer.replace(exePos, exePos + MARKER_NAME.length(), name);
365: else {
366: exePos = scan(buffer, 0, MARKER_KEY);
367: if (exePos != -1) {
368: int start = scan(buffer, exePos + MARKER_KEY.length(),
369: STRING_START);
370: int end = scan(buffer, start + STRING_START.length(),
371: STRING_END);
372: if (start > -1 && end > start) {
373: buffer.replace(start + STRING_START.length(), end,
374: name);
375: }
376: }
377: }
378:
379: int bundlePos = scan(buffer, 0, BUNDLE_NAME);
380: if (bundlePos != -1)
381: buffer.replace(bundlePos, bundlePos + BUNDLE_NAME.length(),
382: name);
383: else {
384: exePos = scan(buffer, 0, BUNDLE_KEY);
385: if (exePos != -1) {
386: int start = scan(buffer, exePos + BUNDLE_KEY.length(),
387: STRING_START);
388: int end = scan(buffer, start + STRING_START.length(),
389: STRING_END);
390: if (start > -1 && end > start) {
391: buffer.replace(start + STRING_START.length(), end,
392: name);
393: }
394: }
395: }
396:
397: int iconPos = scan(buffer, 0, ICON_NAME);
398: if (iconPos != -1)
399: buffer.replace(iconPos, iconPos + ICON_NAME.length(),
400: iconName);
401: else {
402: exePos = scan(buffer, 0, ICON_KEY);
403: if (exePos != -1) {
404: int start = scan(buffer, exePos + ICON_KEY.length(),
405: STRING_START);
406: int end = scan(buffer, start + STRING_START.length(),
407: STRING_END);
408: if (start > -1 && end > start) {
409: buffer.replace(start + STRING_START.length(), end,
410: iconName);
411: }
412: }
413: }
414:
415: File target = null;
416: try {
417: target = new File(targetRoot, "Info.plist"); //$NON-NLS-1$
418: target.getParentFile().mkdirs();
419: transferStreams(new ByteArrayInputStream(buffer.toString()
420: .getBytes()), new FileOutputStream(target));
421: } catch (FileNotFoundException e) {
422: System.out.println("Impossible to brand info.plist file"); //$NON-NLS-1$
423: return;
424: } catch (IOException e) {
425: System.out.println("Impossible to brand info.plist file"); //$NON-NLS-1$
426: return;
427: }
428: try {
429: if (!infoPList.getCanonicalFile().equals(
430: target.getCanonicalFile()))
431: infoPList.delete();
432: } catch (IOException e) {
433: //ignore
434: }
435: }
436:
437: /**
438: * Transfers all available bytes from the given input stream to the given output stream.
439: * Regardless of failure, this method closes both streams.
440: * @throws IOException
441: */
442: public void copy(File source, File destination) throws IOException {
443: InputStream in = null;
444: OutputStream out = null;
445: try {
446: in = new BufferedInputStream(new FileInputStream(source));
447: out = new BufferedOutputStream(new FileOutputStream(
448: destination));
449: final byte[] buffer = new byte[8192];
450: while (true) {
451: int bytesRead = -1;
452: bytesRead = in.read(buffer);
453: if (bytesRead == -1)
454: break;
455: out.write(buffer, 0, bytesRead);
456: }
457: } finally {
458: try {
459: if (in != null)
460: in.close();
461: } finally {
462: if (out != null)
463: out.close();
464: }
465: }
466: }
467:
468: private int scan(StringBuffer buf, int start, String targetName) {
469: return scan(buf, start, new String[] { targetName });
470: }
471:
472: private int scan(StringBuffer buf, int start, String[] targets) {
473: for (int i = start; i < buf.length(); i++) {
474: for (int j = 0; j < targets.length; j++) {
475: if (i < buf.length() - targets[j].length()) {
476: String match = buf.substring(i, i
477: + targets[j].length());
478: if (targets[j].equalsIgnoreCase(match))
479: return i;
480: }
481: }
482: }
483: return -1;
484: }
485:
486: private StringBuffer readFile(File targetName) throws IOException {
487: InputStreamReader reader = new InputStreamReader(
488: new BufferedInputStream(new FileInputStream(targetName)));
489: StringBuffer result = new StringBuffer();
490: char[] buf = new char[4096];
491: int count;
492: try {
493: count = reader.read(buf, 0, buf.length);
494: while (count != -1) {
495: result.append(buf, 0, count);
496: count = reader.read(buf, 0, buf.length);
497: }
498: } finally {
499: try {
500: reader.close();
501: } catch (IOException e) {
502: // ignore exceptions here
503: }
504: }
505: return result;
506: }
507:
508: private void transferStreams(InputStream source,
509: OutputStream destination) throws IOException {
510: source = new BufferedInputStream(source);
511: destination = new BufferedOutputStream(destination);
512: try {
513: byte[] buffer = new byte[8192];
514: while (true) {
515: int bytesRead = -1;
516: if ((bytesRead = source.read(buffer)) == -1)
517: break;
518: destination.write(buffer, 0, bytesRead);
519: }
520: } finally {
521: try {
522: source.close();
523: } catch (IOException e) {
524: // ignore
525: }
526: try {
527: destination.close();
528: } catch (IOException e) {
529: // ignore
530: }
531: }
532: }
533:
534: public void setOS(String value) {
535: os = value;
536: }
537: }
|