changeset 5072:8196ef701ac1

7134730: Create Ant task for generating Mac OS X app bundles Summary: provides infrastructure for building app bundles using ant Reviewed-by: ksrini, swingler Contributed-by: greg.x.brown@oracle.com
author ksrini
date Wed, 15 Feb 2012 14:21:13 -0800
parents a188c3094dba
children 7c50ad220359
files src/macosx/bundle/appbundler/native/main.m src/macosx/bundle/appbundler/src/com/oracle/appbundler/AppBundlerTask.java src/macosx/bundle/appbundler/src/com/oracle/appbundler/Argument.java src/macosx/bundle/appbundler/src/com/oracle/appbundler/GenericApp.icns src/macosx/bundle/appbundler/src/com/oracle/appbundler/Option.java src/macosx/bundle/appbundler/test/Test.java src/macosx/bundle/build.properties src/macosx/bundle/build.xml
diffstat 8 files changed, 1000 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/macosx/bundle/appbundler/native/main.m	Wed Feb 15 14:21:13 2012 -0800
@@ -0,0 +1,187 @@
+/*
+ * Copyright 2012, Oracle and/or its affiliates. All rights reserved.
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#import <Cocoa/Cocoa.h>
+#include <jni.h>
+
+#define JAVA_LAUNCH_ERROR "JavaLaunchError"
+
+#define JAVA_VM_KEY "JavaVM"
+#define RUNTIME_KEY "Runtime"
+#define MAIN_CLASS_NAME_KEY "MainClassName"
+#define OPTIONS_KEY "Options"
+#define ARGUMENTS_KEY "Arguments"
+
+// TODO Remove these; they are defined by the makefile
+#define FULL_VERSION "1.7.0"
+#define DOT_VERSION "1.7.0"
+#define DEFAULT_POLICY 0
+
+typedef int (JNICALL *JLI_Launch_t)(int argc, char ** argv,
+                                    int jargc, const char** jargv,
+                                    int appclassc, const char** appclassv,
+                                    const char* fullversion,
+                                    const char* dotversion,
+                                    const char* pname,
+                                    const char* lname,
+                                    jboolean javaargs,
+                                    jboolean cpwildcard,
+                                    jboolean javaw,
+                                    jint ergo);
+
+int launch(char *);
+int jli_launch(char *, NSURL *, NSString *, NSString *, NSString *, NSArray *, NSArray *);
+
+int main(int argc, char *argv[]) {
+    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+    int result;
+    @try {
+        launch(argv[0]);
+        result = 0;
+    } @catch (NSException *exception) {
+        NSLog(@"%@: %@", exception, [exception callStackSymbols]);
+        result = 1;
+    }
+
+    [pool drain];
+
+    return result;
+}
+
+int launch(char *commandName) {
+    // Get the main bundle
+    NSBundle *mainBundle = [NSBundle mainBundle];
+    NSDictionary *infoDictionary = [mainBundle infoDictionary];
+
+    NSString *javaVMPath = [[mainBundle bundlePath] stringByAppendingString:@"/Contents/JavaVM"];
+
+    // Get the Java dictionary
+    NSDictionary *javaVMDictionary = [infoDictionary objectForKey:@JAVA_VM_KEY];
+    if (javaVMDictionary == nil) {
+        [NSException raise:@JAVA_LAUNCH_ERROR format:@"%@ is required.", @JAVA_VM_KEY];
+    }
+
+    // Get the runtime bundle URL
+    NSString *runtime = [javaVMDictionary objectForKey:@RUNTIME_KEY];
+
+    // TODO If unspecified, use default runtime location
+
+    NSURL *runtimeBundleURL = [[mainBundle builtInPlugInsURL] URLByAppendingPathComponent:runtime];
+
+    // Get the main class name
+    NSString *mainClassName = [javaVMDictionary objectForKey:@MAIN_CLASS_NAME_KEY];
+    if (mainClassName == nil) {
+        [NSException raise:@JAVA_LAUNCH_ERROR format:@"%@ is required.", @MAIN_CLASS_NAME_KEY];
+    }
+
+    // Set the class path
+    NSString *classPathFormat = @"-Djava.class.path=%@/Classes";
+    NSMutableString *classPath = [[NSString stringWithFormat:classPathFormat, javaVMPath] mutableCopy];
+
+    NSFileManager *defaultFileManager = [NSFileManager defaultManager];
+    NSArray *javaDirectoryContents = [defaultFileManager contentsOfDirectoryAtPath:javaVMPath error:nil];
+    if (javaDirectoryContents == nil) {
+        [NSException raise:@JAVA_LAUNCH_ERROR format:@"Could not enumerate Java directory contents."];
+    }
+
+    for (NSString *file in javaDirectoryContents) {
+        if ([file hasSuffix:@".jar"]) {
+            [classPath appendFormat:@":%@/%@", javaVMPath, file];
+        }
+    }
+
+    // Set the library path
+    NSString *libraryPathFormat = @"-Djava.library.path=%@";
+    NSString *libraryPath = [NSString stringWithFormat:libraryPathFormat, javaVMPath];
+
+    // Get the VM options
+    NSArray *options = [javaVMDictionary objectForKey:@OPTIONS_KEY];
+    if (options == nil) {
+        options = [NSArray array];
+    }
+
+    // Get the application arguments
+    NSArray *arguments = [javaVMDictionary objectForKey:@ARGUMENTS_KEY];
+    if (arguments == nil) {
+        arguments = [NSArray array];
+    }
+
+    return jli_launch(commandName, runtimeBundleURL,
+                      mainClassName, classPath, libraryPath,
+                      options, arguments);
+}
+
+int jli_launch(char *commandName, NSURL *runtimeBundleURL,
+               NSString *mainClassName, NSString *classPath, NSString *libraryPath,
+               NSArray *options, NSArray *arguments) {
+    // Load the runtime bundle
+    CFBundleRef runtimeBundle = CFBundleCreate(NULL, (CFURLRef)runtimeBundleURL);
+
+    NSError *bundleLoadError = nil;
+    Boolean runtimeBundleLoaded = CFBundleLoadExecutableAndReturnError(runtimeBundle, (CFErrorRef *)&bundleLoadError);
+    if (bundleLoadError != nil || !runtimeBundleLoaded) {
+        [NSException raise:@JAVA_LAUNCH_ERROR format:@"Could not load JRE from %@.", bundleLoadError];
+    }
+
+    // Get the JLI_Launch() function pointer
+    JLI_Launch_t JLI_LaunchFxnPtr = CFBundleGetFunctionPointerForName(runtimeBundle, CFSTR("JLI_Launch"));
+    if (JLI_LaunchFxnPtr == NULL) {
+        [NSException raise:@JAVA_LAUNCH_ERROR format:@"Could not get function pointer for JLI_Launch."];
+    }
+
+    // Initialize the arguments to JLI_Launch()
+    int argc = 1 + [options count] + 2 + [arguments count] + 1;
+    char *argv[argc];
+
+    int i = 0;
+    argv[i++] = commandName;
+    argv[i++] = strdup([classPath UTF8String]);
+    argv[i++] = strdup([libraryPath UTF8String]);
+
+    for (NSString *option in options) {
+        argv[i++] = strdup([option UTF8String]);
+    }
+
+    argv[i++] = strdup([mainClassName UTF8String]);
+
+    for (NSString *argument in arguments) {
+        argv[i++] = strdup([argument UTF8String]);
+    }
+
+    // Invoke JLI_Launch()
+    return JLI_LaunchFxnPtr(argc, argv,
+                            0, NULL,
+                            0, NULL,
+                            FULL_VERSION,
+                            DOT_VERSION,
+                            "java",
+                            "java",
+                            FALSE,
+                            FALSE,
+                            FALSE,
+                            DEFAULT_POLICY);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/macosx/bundle/appbundler/src/com/oracle/appbundler/AppBundlerTask.java	Wed Feb 15 14:21:13 2012 -0800
@@ -0,0 +1,485 @@
+/*
+ * Copyright 2012, Oracle and/or its affiliates. All rights reserved.
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.oracle.appbundler;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Writer;
+import java.net.URL;
+import java.nio.file.Files;
+import java.nio.file.LinkOption;
+import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
+import java.util.ArrayList;
+
+import javax.xml.stream.XMLOutputFactory;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.FileSet;
+
+/**
+ * App bundler Ant task.
+ */
+public class AppBundlerTask extends Task {
+    // Output folder for generated bundle
+    private File outputDirectory = null;
+
+    // General bundle properties
+    private String name = null;
+    private String displayName = null;
+    private String identifier = null;
+    private File icon = null;
+
+    private String shortVersion = "1.0";
+    private String signature = "????";
+    private String copyright = "";
+
+    // JVM info properties
+    private File runtime = null;
+    private String mainClassName = null;
+    private ArrayList<File> classPath = new ArrayList<>();
+    private ArrayList<File> nativeLibraries = new ArrayList<>();
+    private ArrayList<String> options = new ArrayList<>();
+    private ArrayList<String> arguments = new ArrayList<>();
+
+    public static final String EXECUTABLE_NAME = "JavaAppLauncher";
+    public static final String DEFAULT_ICON_NAME = "GenericApp.icns";
+    public static final String OS_TYPE_CODE = "APPL";
+    public static final String CLASS_EXTENSION = ".class";
+
+    public static final String PLIST_DTD = "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">";
+    public static final String PLIST_TAG = "plist";
+    public static final String PLIST_VERSION_ATTRIBUTE = "version";
+    public static final String DICT_TAG = "dict";
+    public static final String KEY_TAG = "key";
+    public static final String ARRAY_TAG = "array";
+    public static final String STRING_TAG = "string";
+
+    public static final int BUFFER_SIZE = 1024;
+
+    public void setOutputDirectory(File outputDirectory) {
+        this.outputDirectory = outputDirectory;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public void setDisplayName(String displayName) {
+        this.displayName = displayName;
+    }
+
+    public void setIdentifier(String identifier) {
+        this.identifier = identifier;
+    }
+
+    public void setIcon(File icon) {
+        this.icon = icon;
+    }
+
+    public void setShortVersion(String shortVersion) {
+        this.shortVersion = shortVersion;
+    }
+
+    public void setSignature(String signature) {
+        this.signature = signature;
+    }
+
+    public void setCopyright(String copyright) {
+        this.copyright = copyright;
+    }
+
+    public File getRuntime() {
+        return runtime;
+    }
+
+    public void setRuntime(File runtime) {
+        this.runtime = runtime;
+    }
+
+    public void setMainClassName(String mainClassName) {
+        this.mainClassName = mainClassName;
+    }
+
+    public void addConfiguredClassPath(FileSet classPath) {
+        File parent = classPath.getDir();
+
+        DirectoryScanner directoryScanner = classPath.getDirectoryScanner(getProject());
+        String[] includedFiles = directoryScanner.getIncludedFiles();
+
+        for (int i = 0; i < includedFiles.length; i++) {
+            this.classPath.add(new File(parent, includedFiles[i]));
+        }
+    }
+
+    public void addNativeLibrary(File nativeLibrary) throws BuildException {
+        if (nativeLibrary.isDirectory()) {
+            throw new BuildException("Native library cannot be a directory.");
+        }
+
+        nativeLibraries.add(nativeLibrary);
+    }
+
+    public void addConfiguredOption(Option option) throws BuildException {
+        String value = option.getValue();
+
+        if (value == null) {
+            throw new BuildException("Value is required.");
+        }
+
+        options.add(value);
+    }
+
+    public void addConfiguredArgument(Argument argument) throws BuildException {
+        String value = argument.getValue();
+
+        if (value == null) {
+            throw new BuildException("Value is required.");
+        }
+
+        arguments.add(value);
+    }
+
+    @Override
+    public void execute() throws BuildException {
+        // Validate required properties
+        if (outputDirectory == null) {
+            throw new IllegalStateException("Destination directory is required.");
+        }
+
+        if (!outputDirectory.exists()) {
+            throw new IllegalStateException("Destination directory does not exist.");
+        }
+
+        if (!outputDirectory.isDirectory()) {
+            throw new IllegalStateException("Invalid destination directory.");
+        }
+
+        if (name == null) {
+            throw new IllegalStateException("Name is required.");
+        }
+
+        if (displayName == null) {
+            throw new IllegalStateException("Display name is required.");
+        }
+
+        if (identifier == null) {
+            throw new IllegalStateException("Identifier is required.");
+        }
+
+        if (icon != null) {
+            if (!icon.exists()) {
+                throw new IllegalStateException("Icon does not exist.");
+            }
+
+            if (icon.isDirectory()) {
+                throw new IllegalStateException("Invalid icon.");
+            }
+        }
+
+        if (shortVersion == null) {
+            throw new IllegalStateException("Short version is required.");
+        }
+
+        if (signature == null) {
+            throw new IllegalStateException("Signature is required.");
+        }
+
+        if (signature.length() != 4) {
+            throw new IllegalStateException("Invalid signature.");
+        }
+
+        if (copyright == null) {
+            throw new IllegalStateException("Copyright is required.");
+        }
+
+        if (runtime != null) {
+            if (!runtime.exists()) {
+                throw new IllegalStateException("Runtime does not exist.");
+            }
+
+            if (!runtime.isDirectory()) {
+                throw new IllegalStateException("Invalid runtime.");
+            }
+        }
+
+        if (mainClassName == null) {
+            throw new IllegalStateException("Main class name is required.");
+        }
+
+        if (classPath.isEmpty()) {
+            throw new IllegalStateException("Class path is required.");
+        }
+
+        // Create directory structure
+        try {
+            System.out.println("Creating app bundle: " + name);
+
+            File rootDirectory = new File(outputDirectory, name + ".app");
+            delete(rootDirectory);
+            rootDirectory.mkdir();
+
+            File contentsDirectory = new File(rootDirectory, "Contents");
+            contentsDirectory.mkdir();
+
+            File macOSDirectory = new File(contentsDirectory, "MacOS");
+            macOSDirectory.mkdir();
+
+            File javaDirectory = new File(contentsDirectory, "JavaVM");
+            javaDirectory.mkdir();
+
+            File classesDirectory = new File(javaDirectory, "Classes");
+            classesDirectory.mkdir();
+
+            File plugInsDirectory = new File(contentsDirectory, "PlugIns");
+            plugInsDirectory.mkdir();
+
+            File resourcesDirectory = new File(contentsDirectory, "Resources");
+            resourcesDirectory.mkdir();
+
+            // Generate Info.plist
+            File infoPlistFile = new File(contentsDirectory, "Info.plist");
+            infoPlistFile.createNewFile();
+            writeInfoPlist(infoPlistFile);
+
+            // Generate PkgInfo
+            File pkgInfoFile = new File(contentsDirectory, "PkgInfo");
+            pkgInfoFile.createNewFile();
+            writePkgInfo(pkgInfoFile);
+
+            // Copy executable to MacOS folder
+            File executableFile = new File(macOSDirectory, EXECUTABLE_NAME);
+            copy(getClass().getResource(EXECUTABLE_NAME), executableFile);
+
+            executableFile.setExecutable(true);
+
+            // Copy runtime to PlugIns folder (if specified)
+            if (runtime != null) {
+                copy(runtime, new File(plugInsDirectory, runtime.getName()));
+            }
+
+            // Copy class path entries to Java folder
+            for (File entry : classPath) {
+                String name = entry.getName();
+
+                if (entry.isDirectory() || name.endsWith(CLASS_EXTENSION)) {
+                    copy(entry, new File(classesDirectory, name));
+                } else {
+                    copy(entry, new File(javaDirectory, name));
+                }
+            }
+
+            // Copy native libraries to Java folder
+            for (File nativeLibrary : nativeLibraries) {
+                copy(nativeLibrary, new File(javaDirectory, nativeLibrary.getName()));
+            }
+
+            // Copy icon to Resources folder
+            if (icon == null) {
+                copy(getClass().getResource(DEFAULT_ICON_NAME), new File(resourcesDirectory,
+                    DEFAULT_ICON_NAME));
+            } else {
+                copy(icon, new File(resourcesDirectory, icon.getName()));
+            }
+        } catch (IOException exception) {
+            throw new BuildException(exception);
+        }
+    }
+
+    private void writeInfoPlist(File file) throws IOException {
+        Writer out = new BufferedWriter(new FileWriter(file));
+        XMLOutputFactory output = XMLOutputFactory.newInstance();
+
+        try {
+            XMLStreamWriter xout = output.createXMLStreamWriter(out);
+
+            // Write XML declaration
+            xout.writeStartDocument();
+            xout.writeCharacters("\n");
+
+            // Write plist DTD declaration
+            xout.writeDTD(PLIST_DTD);
+            xout.writeCharacters("\n");
+
+            // Begin root element
+            xout.writeStartElement(PLIST_TAG);
+            xout.writeAttribute(PLIST_VERSION_ATTRIBUTE, "1.0");
+            xout.writeCharacters("\n");
+
+            // Begin root dictionary
+            xout.writeStartElement(DICT_TAG);
+            xout.writeCharacters("\n");
+
+            // Write bundle properties
+            writeProperty(xout, "CFBundleDevelopmentRegion", "English");
+            writeProperty(xout, "CFBundleExecutable", EXECUTABLE_NAME);
+            writeProperty(xout, "CFBundleIconFile", (icon == null) ? DEFAULT_ICON_NAME : icon.getName());
+            writeProperty(xout, "CFBundleIdentifier", identifier);
+            writeProperty(xout, "CFBundleDisplayName", displayName);
+            writeProperty(xout, "CFBundleInfoDictionaryVersion", "6.0");
+            writeProperty(xout, "CFBundleName", name);
+            writeProperty(xout, "CFBundlePackageType", OS_TYPE_CODE);
+            writeProperty(xout, "CFBundleShortVersionString", shortVersion);
+            writeProperty(xout, "CFBundleSignature", signature);
+            writeProperty(xout, "CFBundleVersion", "1");
+            writeProperty(xout, "NSHumanReadableCopyright", copyright);
+
+            // Start Java properties
+            writeKey(xout, "JavaVM");
+            xout.writeStartElement(DICT_TAG);
+
+            // Write runtime
+            writeProperty(xout, "Runtime", runtime.getName());
+
+            // Write main class name
+            writeProperty(xout, "MainClassName", mainClassName);
+
+            // Write options
+            writeKey(xout, "Options");
+
+            xout.writeStartElement(ARRAY_TAG);
+            xout.writeCharacters("\n");
+
+            for (String option : options) {
+                writeString(xout, option);
+            }
+
+            xout.writeEndElement();
+            xout.writeCharacters("\n");
+
+            // Write arguments
+            writeKey(xout, "Arguments");
+
+            xout.writeStartElement(ARRAY_TAG);
+            xout.writeCharacters("\n");
+
+            for (String argument : arguments) {
+                writeString(xout, argument);
+            }
+
+            xout.writeEndElement();
+            xout.writeCharacters("\n");
+
+            // End Java properties
+            xout.writeEndElement();
+            xout.writeCharacters("\n");
+
+            // End root dictionary
+            xout.writeEndElement();
+            xout.writeCharacters("\n");
+
+            // End root element
+            xout.writeEndElement();
+            xout.writeCharacters("\n");
+
+            // Close document
+            xout.writeEndDocument();
+            xout.writeCharacters("\n");
+
+            out.flush();
+        } catch (XMLStreamException exception) {
+            throw new IOException(exception);
+        } finally {
+            out.close();
+        }
+    }
+
+    private void writeKey(XMLStreamWriter xout, String key) throws XMLStreamException {
+        xout.writeStartElement(KEY_TAG);
+        xout.writeCharacters(key);
+        xout.writeEndElement();
+        xout.writeCharacters("\n");
+    }
+
+    private void writeString(XMLStreamWriter xout, String value) throws XMLStreamException {
+        xout.writeStartElement(STRING_TAG);
+        xout.writeCharacters(value);
+        xout.writeEndElement();
+        xout.writeCharacters("\n");
+    }
+
+    private void writeProperty(XMLStreamWriter xout, String key, String value) throws XMLStreamException {
+        writeKey(xout, key);
+        writeString(xout, value);
+    }
+
+    private void writePkgInfo(File file) throws IOException {
+        Writer out = new BufferedWriter(new FileWriter(file));
+
+        try {
+            out.write(OS_TYPE_CODE + signature);
+            out.flush();
+        } finally {
+            out.close();
+        }
+    }
+
+    private static void delete(File file) throws IOException {
+        Path filePath = file.toPath();
+
+        if (Files.exists(filePath, LinkOption.NOFOLLOW_LINKS)) {
+            if (Files.isDirectory(filePath, LinkOption.NOFOLLOW_LINKS)) {
+                File[] files = file.listFiles();
+
+                for (int i = 0; i < files.length; i++) {
+                    delete(files[i]);
+                }
+            }
+
+            Files.delete(filePath);
+        }
+    }
+
+    private static void copy(URL location, File file) throws IOException {
+        try (InputStream in = location.openStream()) {
+            Files.copy(in, file.toPath(), StandardCopyOption.REPLACE_EXISTING);
+        }
+    }
+
+    private static void copy(File source, File destination) throws IOException {
+        Path sourcePath = source.toPath();
+        Path destinationPath = destination.toPath();
+
+        Files.copy(sourcePath, destinationPath, StandardCopyOption.REPLACE_EXISTING, LinkOption.NOFOLLOW_LINKS);
+
+        if (Files.isDirectory(sourcePath, LinkOption.NOFOLLOW_LINKS)) {
+            String[] files = source.list();
+
+            for (int i = 0; i < files.length; i++) {
+                String file = files[i];
+                copy(new File(source, file), new File(destination, file));
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/macosx/bundle/appbundler/src/com/oracle/appbundler/Argument.java	Wed Feb 15 14:21:13 2012 -0800
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2012, Oracle and/or its affiliates. All rights reserved.
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.oracle.appbundler;
+
+/**
+ * Class representing an argument that will be passed to the Java application
+ * at startup.
+ */
+public class Argument {
+    private String value = null;
+
+    public String getValue() {
+        return value;
+    }
+
+    public void setValue(String value) {
+        this.value = value;
+    }
+
+    @Override
+    public String toString() {
+        return value;
+    }
+}
Binary file src/macosx/bundle/appbundler/src/com/oracle/appbundler/GenericApp.icns has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/macosx/bundle/appbundler/src/com/oracle/appbundler/Option.java	Wed Feb 15 14:21:13 2012 -0800
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2012, Oracle and/or its affiliates. All rights reserved.
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.oracle.appbundler;
+
+/**
+ * Class representing an option that will be passed to the JVM at startup.
+ */
+public class Option {
+    private String value = null;
+
+    public String getValue() {
+        return value;
+    }
+
+    public void setValue(String value) {
+        this.value = value;
+    }
+
+    @Override
+    public String toString() {
+        return value;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/macosx/bundle/appbundler/test/Test.java	Wed Feb 15 14:21:13 2012 -0800
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2012, Oracle and/or its affiliates. All rights reserved.
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+public class Test {
+    public static void main(String[] args) {
+        System.out.println("Hello, World!");
+
+        for (int i = 0; i < args.length; i++) {
+            System.out.println("arg[" + i + "] = " + args[i]);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/macosx/bundle/build.properties	Wed Feb 15 14:21:13 2012 -0800
@@ -0,0 +1,24 @@
+# Copyright 2012, Oracle and/or its affiliates. All rights reserved.
+#
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# This code is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License version 2 only, as
+# published by the Free Software Foundation.  Oracle designates this
+# particular file as subject to the "Classpath" exception as provided
+# by Oracle in the LICENSE file that accompanied this code.
+#
+# This code is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+# version 2 for more details (a copy is included in the LICENSE file that
+# accompanied this code).
+#
+# You should have received a copy of the GNU General Public License version
+# 2 along with this work; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+# or visit www.oracle.com if you need additional information or have any
+# questions.
+version=1.0
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/macosx/bundle/build.xml	Wed Feb 15 14:21:13 2012 -0800
@@ -0,0 +1,174 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Copyright 2012, Oracle and/or its affiliates. All rights reserved.
+
+DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+
+This code is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License version 2 only, as
+published by the Free Software Foundation.  Oracle designates this
+particular file as subject to the "Classpath" exception as provided
+by Oracle in the LICENSE file that accompanied this code.
+
+This code is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+version 2 for more details (a copy is included in the LICENSE file that
+accompanied this code).
+
+You should have received a copy of the GNU General Public License version
+2 along with this work; if not, write to the Free Software Foundation,
+Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+or visit www.oracle.com if you need additional information or have any
+questions.
+-->
+
+<project name="appbundler" default="package">
+    <property environment="env"/>
+
+    <property file="build.properties"/>
+
+    <property name="folder.src" value="src"/>
+    <property name="folder.test" value="test"/>
+    <property name="folder.native" value="native"/>
+    <property name="folder.bin" value="bin"/>
+    <property name="folder.classes" value="${folder.bin}/classes"/>
+
+    <!-- Compile target -->
+    <target name="compile">
+        <mkdir dir="${ant.project.name}/${folder.classes}"/>
+        <javac destDir="${ant.project.name}/${folder.classes}"
+            includejavaruntime="true"
+            includeantruntime="true"
+            deprecation="true"
+            debug="true"
+            encoding="UTF-8"
+            failonerror="true">
+            <src>
+                <dirset dir="${ant.project.name}">
+                    <include name="${folder.src}"/>
+                    <include name="${folder.test}"/>
+                </dirset>
+            </src>
+            <compilerarg line="-Xlint:all"/>
+            <classpath>
+                <dirset dir="${basedir}" includes="**/${folder.classes}"/>
+                <fileset dir="${ant.project.name}" includes="lib/**/*.jar"/>
+            </classpath>
+        </javac>
+        <copy todir="${ant.project.name}/${folder.classes}" includeEmptyDirs="false">
+            <fileset dir="${ant.project.name}/${folder.src}">
+                <exclude name="**/*.java"/>
+                <exclude name="**/package.html"/>
+            </fileset>
+            <fileset dir="${ant.project.name}/${folder.test}" erroronmissingdir="false">
+                <exclude name="**/*.java"/>
+                <exclude name="**/package.html"/>
+            </fileset>
+        </copy>
+
+        <!-- TODO Move this to a makefile so we can reference the defines required by JLI_Launch() -->
+        <exec executable="gcc">
+            <arg value="-I"/>
+            <arg value="${env.JAVA_HOME}/include"/>
+            <arg value="-I"/>
+            <arg value="${env.JAVA_HOME}/include/darwin"/>
+            <arg value="-o"/>
+            <arg value="${ant.project.name}/${folder.classes}/com/oracle/appbundler/JavaAppLauncher"/>
+            <arg value="-framework"/>
+            <arg value="Cocoa"/>
+            <arg value="-F"/>
+            <arg value="${env.JAVA_HOME}/../.."/>
+            <arg value="-m64"/>
+            <arg value="-std=c99"/>
+            <arg value="appbundler/native/main.m"/>
+        </exec>
+    </target>
+
+    <!-- Clean target -->
+    <target name="clean">
+        <delete dir="${ant.project.name}/${folder.classes}"/>
+        <delete file="${ant.project.name}/${folder.bin}/${ant.project.name}-${version}.jar"/>
+        <delete dir="HelloWorld.app"/>
+        <delete dir="SwingSet2.app"/>
+    </target>
+
+    <!-- Package target -->
+    <target name="package" depends="compile">
+        <property name="destfile" value="${ant.project.name}/${folder.bin}/${ant.project.name}-${version}.jar"/>
+
+        <delete file="${destfile}"/>
+        <jar destfile="${destfile}" index="true">
+            <manifest>
+                <attribute name="Implementation-Vendor-Id" value="com.oracle"/>
+                <attribute name="Implementation-Vendor" value="Oracle"/>
+                <attribute name="Implementation-Title" value="App Bundler Ant Task"/>
+                <attribute name="Implementation-Version" value="${version}"/>
+            </manifest>
+            <fileset dir="${ant.project.name}/${folder.classes}">
+                <exclude name="**/${folder.test}/**"/>
+            </fileset>
+            <fileset dir="${ant.project.name}/${folder.bin}">
+                <include name="JavaAppLauncher"/>
+            </fileset>
+        </jar>
+    </target>
+
+    <!-- Test targets -->
+    <target name="test-hello" depends="package">
+        <taskdef name="bundleapp" classname="com.oracle.appbundler.AppBundlerTask"
+            classpath="${ant.project.name}/${folder.bin}/${ant.project.name}-${version}.jar"/>
+
+        <bundleapp outputdirectory="."
+            name="HelloWorld"
+            displayname="Hello World Test"
+            identifier="com.oracle.appbundler.Test"
+            shortversion="1.0"
+            runtime="${env.JAVA_HOME}/../.."
+            mainclassname="Test">
+            <classpath dir="${ant.project.name}/${folder.classes}" includes="Test.class"/>
+            <option value="-Xms32M"/>
+            <option value="-Xmx256M"/>
+            <argument value="foo=bar"/>
+        </bundleapp>
+    </target>
+
+    <target name="test-swingset" depends="package">
+        <taskdef name="bundleapp" classname="com.oracle.appbundler.AppBundlerTask"
+            classpath="${ant.project.name}/${folder.bin}/${ant.project.name}-${version}.jar"/>
+
+        <bundleapp outputdirectory="."
+            name="SwingSet2"
+            displayname="SwingSet 2"
+            identifier="com.oracle.javax.swing.SwingSet2"
+            shortversion="1.0"
+            runtime="${env.JAVA_HOME}/../.."
+            mainclassname="SwingSet2">
+            <classpath file="/Library/Java/Demos/JFC/SwingSet2/SwingSet2.jar"/>
+            <option value="-Xms32M"/>
+            <option value="-Xmx256M"/>
+            <option value="-Dapple.laf.useScreenMenuBar=true"/>
+            <argument value="foo=bar"/>
+        </bundleapp>
+    </target>
+
+    <!-- Trim whitespace target -->
+    <target name="trim-whitespace">
+        <fileset id="trimfiles" dir=".">
+            <include name="**/*.h"/>
+            <include name="**/*.html"/>
+            <include name="**/*.java"/>
+            <include name="**/*.m"/>
+            <include name="**/*.properties"/>
+            <include name="**/*.xml"/>
+        </fileset>
+        <replaceregexp match="[\t]" replace="    " flags="gm" byline="true">
+            <fileset refid="trimfiles"/>
+        </replaceregexp>
+        <replaceregexp match="[\t ]+$" replace="" flags="gm" byline="true">
+            <fileset refid="trimfiles"/>
+        </replaceregexp>
+    </target>
+</project>