This commit is contained in:
codeanticode
2013-09-19 10:05:39 -04:00
13 changed files with 1496 additions and 0 deletions

2
build/macosx/appbundler/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
bin

View File

@@ -0,0 +1,85 @@
appbundler
=============
A fork of the [Java Application Bundler](https://svn.java.net/svn/appbundler~svn)
with the following changes:
- The native binary is created as universal (32/64)
- Fixes [icon not showing bug](http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=7159381) in `JavaAppLauncher`
- Adds `LC_CTYPE` environment variable to the `Info.plist` file in order to fix an [issue with `File.exists()` in OpenJDK 7](http://java.net/jira/browse/MACOSX_PORT-165) **(Contributed by Steve Hannah)**
- Allows to specify the name of the executable instead of using the default `"JavaAppLauncher"` **(contributed by Karl von Randow)**
- Adds `classpathref` support to the `bundleapp` task
- Adds support for `JVMArchs` and `LSArchitecturePriority` keys
- Allows to specify a custom value for `CFBundleVersion`
- Allows specifying registered file extensions using `CFBundleDocumentTypes`
- Passes to the Java application a set of environment variables with the paths of
the OSX special folders and whether the application is running in the
sandbox (see below).
These are the environment variables passed to the JVM:
- `LibraryDirectory`
- `DocumentsDirectory`
- `CachesDirectory`
- `ApplicationSupportDirectory`
- `SandboxEnabled` (the String `true` or `false`)
Example:
<target name="bundle">
<taskdef name="bundleapp"
classpath="appbundler-1.0ea.jar"
classname="com.oracle.appbundler.AppBundlerTask"/>
<bundleapp
classpathref="runclasspathref"
outputdirectory="${dist}"
name="${bundle.name}"
displayname="${bundle.displayname}"
executableName="MyApp"
identifier="com.company.product"
shortversion="${version.public}"
version="${version.internal}"
icon="${bundle.icon}"
mainclassname="Main"
copyright="2012 Your Company"
applicationCategory="public.app-category.finance">
<runtime dir="${runtime}/Contents/Home"/>
<arch name="x86_64"/>
<arch name="i386"/>
<bundledocument extensions="png,jpg"
icon="${bundle.icon}"
name="Images"
role="editor">
</bundledocument>
<bundledocument extensions="pdf"
icon="${bundle.icon}"
name="PDF files"
role="viewer">
</bundledocument>
<bundledocument extensions="custom"
icon="${bundle.icon}"
name="Custom data"
role="editor"
isPackage="true">
</bundledocument>
<!-- Workaround since the icon parameter for bundleapp doesn't work -->
<option value="-Xdock:icon=Contents/Resources/${bundle.icon}"/>
<option value="-Dapple.laf.useScreenMenuBar=true"/>
<option value="-Dcom.apple.macos.use-file-dialog-packages=true"/>
<option value="-Dcom.apple.macos.useScreenMenuBar=true"/>
<option value="-Dcom.apple.mrj.application.apple.menu.about.name=${bundle.name}"/>
<option value="-Dcom.apple.smallTabs=true"/>
<option value="-Dfile.encoding=UTF-8"/>
<option value="-Xmx1024M"/>
</bundleapp>
</target>

View File

@@ -0,0 +1,3 @@
- Architectures should be automatically inferred from the embedded JVM
- It makes more sense to take as input a JRE instead of taking a JDK and strip
it until it becomes a JRE...

View File

@@ -0,0 +1,176 @@
<?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"/>
version=1.0ea
-->
<property name="arch64" value="-arch x86_64" />
<property name="arch32" value="-arch i386" />
<property name="archppc" value="-arch ppc" />
<property name="cc" value="gcc" />
<property name="folder.src" value="src"/>
<property name="folder.native" value="native"/>
<property name="folder.bin" value="bin"/>
<!-- <property name="folder.classes" value="${folder.bin}/classes"/>-->
<property name="folder.classes" value="${folder.bin}"/>
<property name="folder.res" value="res"/>
<!-- Compile target -->
<target name="compile">
<mkdir dir="${folder.classes}"/>
<javac destDir="${folder.classes}"
includejavaruntime="true"
includeantruntime="true"
deprecation="true"
debug="true"
encoding="UTF-8"
failonerror="true">
<src>
<dirset dir=".">
<include name="${folder.src}"/>
</dirset>
</src>
<compilerarg line="-Xlint:all"/>
<classpath>
<dirset dir="${basedir}" includes="**/${folder.classes}"/>
<fileset dir="." includes="lib/**/*.jar"/>
</classpath>
</javac>
<copy todir="${folder.classes}" includeEmptyDirs="false">
<fileset dir="${folder.src}">
<exclude name="**/*.java"/>
<exclude name="**/package.html"/>
</fileset>
</copy>
<exec executable="${cc}">
<arg line="${arch64} ${arch32}"/>
<arg value="-I"/>
<arg value="${env.JAVA_HOME}/include"/>
<arg value="-I"/>
<arg value="${env.JAVA_HOME}/include/darwin"/>
<arg value="-o"/>
<arg value="${folder.classes}/com/oracle/appbundler/JavaAppLauncher"/>
<arg value="-framework"/>
<arg value="Cocoa"/>
<arg value="-F"/>
<arg value="${env.JAVA_HOME}/../.."/>
<arg value="-isysroot"/>
<arg value="/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.7.sdk"/>
<arg value="-mmacosx-version-min=10.7"/>
<arg value="native/main.m"/>
</exec>
</target>
<!-- Clean target -->
<target name="clean">
<delete dir="${folder.bin}"/>
<delete dir="HelloWorld.app"/>
<delete dir="SwingSet2.app"/>
<delete dir="SwingSet2Plugin.app"/>
</target>
<!-- Package target -->
<target name="package" depends="compile">
<zip destfile="${folder.classes}/com/oracle/appbundler/res.zip">
<fileset dir="${folder.res}"/>
</zip>
<jar destfile="../${ant.project.name}.jar" 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="${folder.classes}"/>
<fileset dir="${folder.bin}">
<include name="JavaAppLauncher"/>
<include name="*.zip"/>
</fileset>
</jar>
</target>
<!-- Test targets -->
<target name="test" depends="package">
<taskdef name="bundleapp" classname="com.oracle.appbundler.AppBundlerTask"
classpath="${folder.bin}/${ant.project.name}.jar"/>
<bundleapp outputdirectory="."
name="Test"
displayname="Test"
identifier="com.oracle.javafx.swing.Test"
shortversion="1.0"
applicationCategory="public.app-category.developer-tools"
mainclassname="com/javafx/main/Main">
<runtime dir="${env.JAVA_HOME}"/>
<classpath file="${user.home}/bin/javafx-samples-2.2.0/SwingInterop.jar"/>
<option value="-Dapple.laf.useScreenMenuBar=true"/>
<argument value="foo=bar"/>
</bundleapp>
</target>
<target name="test-plugin" depends="package">
<taskdef name="bundleapp" classname="com.oracle.appbundler.AppBundlerTask"
classpath="${folder.bin}/${ant.project.name}.jar"/>
<bundleapp outputdirectory="."
name="TestPlugin"
displayname="Test (Plugin)"
identifier="com.oracle.javafx.swing.TestPlugin"
shortversion="1.0"
applicationCategory="public.app-category.developer-tools"
mainclassname="com/javafx/main/Main">
<classpath file="${user.home}/bin/javafx-samples-2.2.0/SwingInterop.jar"/>
<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="**/*.strings"/>
<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>

View File

@@ -0,0 +1,243 @@
/*
* 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 <dlfcn.h>
#include <jni.h>
#define JAVA_LAUNCH_ERROR "JavaLaunchError"
#define JVM_RUNTIME_KEY "JVMRuntime"
#define WORKING_DIR "WorkingDirectory"
#define JVM_MAIN_CLASS_NAME_KEY "JVMMainClassName"
#define JVM_OPTIONS_KEY "JVMOptions"
#define JVM_ARGUMENTS_KEY "JVMArguments"
#define JVM_RUN_PRIVILEGED "JVMRunPrivileged"
#define UNSPECIFIED_ERROR "An unknown error occurred."
#define APP_ROOT_PREFIX "$APP_ROOT"
#define LIBJLI_DYLIB "/Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Home/lib/jli/libjli.dylib"
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 main(int argc, char *argv[]) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
int result;
@try {
launch(argv[0]);
result = 0;
} @catch (NSException *exception) {
NSAlert *alert = [[NSAlert alloc] init];
[alert setAlertStyle:NSCriticalAlertStyle];
[alert setMessageText:[exception reason]];
[alert runModal];
result = 1;
}
[pool drain];
return result;
}
int launch(char *commandName) {
// Get the main bundle
NSBundle *mainBundle = [NSBundle mainBundle];
// Get the main bundle's info dictionary
NSDictionary *infoDictionary = [mainBundle infoDictionary];
// Set the working directory based on config, defaulting to the user's home directory
NSString *workingDir = [infoDictionary objectForKey:@WORKING_DIR];
if (workingDir != nil) {
workingDir = [workingDir stringByReplacingOccurrencesOfString:@APP_ROOT_PREFIX withString:[mainBundle bundlePath]];
} else {
workingDir = NSHomeDirectory();
}
chdir([workingDir UTF8String]);
// execute privileged
NSString *privileged = [infoDictionary objectForKey:@JVM_RUN_PRIVILEGED];
if ( privileged != nil && getuid() != 0 ) {
NSDictionary *error = [NSDictionary new];
NSString *script = [NSString stringWithFormat:@"do shell script \"\\\"%@\\\" > /dev/null 2>&1 &\" with administrator privileges", [NSString stringWithCString:commandName encoding:NSASCIIStringEncoding]];
NSAppleScript *appleScript = [[NSAppleScript new] initWithSource:script];
if ([appleScript executeAndReturnError:&error]) {
// This means we successfully elevated the application and can stop in here.
return;
}
}
// Locate the JLI_Launch() function
NSString *runtime = [infoDictionary objectForKey:@JVM_RUNTIME_KEY];
const char *libjliPath = NULL;
if (runtime != nil) {
NSString *runtimePath = [[[NSBundle mainBundle] builtInPlugInsPath] stringByAppendingPathComponent:runtime];
libjliPath = [[runtimePath stringByAppendingPathComponent:@"Contents/Home/jre/lib/jli/libjli.dylib"] fileSystemRepresentation];
} else {
libjliPath = LIBJLI_DYLIB;
}
void *libJLI = dlopen(libjliPath, RTLD_LAZY);
JLI_Launch_t jli_LaunchFxnPtr = NULL;
if (libJLI != NULL) {
jli_LaunchFxnPtr = dlsym(libJLI, "JLI_Launch");
}
if (jli_LaunchFxnPtr == NULL) {
[[NSException exceptionWithName:@JAVA_LAUNCH_ERROR
reason:NSLocalizedString(@"JRELoadError", @UNSPECIFIED_ERROR)
userInfo:nil] raise];
}
// Get the main class name
NSString *mainClassName = [infoDictionary objectForKey:@JVM_MAIN_CLASS_NAME_KEY];
if (mainClassName == nil) {
[[NSException exceptionWithName:@JAVA_LAUNCH_ERROR
reason:NSLocalizedString(@"MainClassNameRequired", @UNSPECIFIED_ERROR)
userInfo:nil] raise];
}
// Set the class path
NSString *mainBundlePath = [mainBundle bundlePath];
NSString *javaPath = [mainBundlePath stringByAppendingString:@"/Contents/Java"];
NSMutableString *classPath = [NSMutableString stringWithFormat:@"-Djava.class.path=%@/Classes", javaPath];
NSFileManager *defaultFileManager = [NSFileManager defaultManager];
NSArray *javaDirectoryContents = [defaultFileManager contentsOfDirectoryAtPath:javaPath error:nil];
if (javaDirectoryContents == nil) {
[[NSException exceptionWithName:@JAVA_LAUNCH_ERROR
reason:NSLocalizedString(@"JavaDirectoryNotFound", @UNSPECIFIED_ERROR)
userInfo:nil] raise];
}
for (NSString *file in javaDirectoryContents) {
if ([file hasSuffix:@".jar"]) {
[classPath appendFormat:@":%@/%@", javaPath, file];
}
}
// Set the library path
NSString *libraryPath = [NSString stringWithFormat:@"-Djava.library.path=%@/Contents/MacOS", mainBundlePath];
// Get the VM options
NSArray *options = [infoDictionary objectForKey:@JVM_OPTIONS_KEY];
if (options == nil) {
options = [NSArray array];
}
// Get the application arguments
NSArray *arguments = [infoDictionary objectForKey:@JVM_ARGUMENTS_KEY];
if (arguments == nil) {
arguments = [NSArray array];
}
// Set OSX special folders
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory,
NSUserDomainMask, YES);
NSString *basePath = [paths objectAtIndex:0];
NSString *libraryDirectory = [NSString stringWithFormat:@"-DLibraryDirectory=%@", basePath];
paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask, YES);
basePath = [paths objectAtIndex:0];
NSString *documentsDirectory = [NSString stringWithFormat:@"-DDocumentsDirectory=%@", basePath];
paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory,
NSUserDomainMask, YES);
basePath = [paths objectAtIndex:0];
NSString *applicationSupportDirectory = [NSString stringWithFormat:@"-DApplicationSupportDirectory=%@", basePath];
paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory,
NSUserDomainMask, YES);
basePath = [paths objectAtIndex:0];
NSString *cachesDirectory = [NSString stringWithFormat:@"-DCachesDirectory=%@", basePath];
NSString *sandboxEnabled = @"true";
if ([basePath rangeOfString:@"Containers"].location == NSNotFound) {
sandboxEnabled = @"false";
}
NSString *sandboxEnabledVar = [NSString stringWithFormat:@"-DSandboxEnabled=%@", sandboxEnabled];
// Initialize the arguments to JLI_Launch()
// +5 due to the special directories and the sandbox enabled property
int argc = 1 + [options count] + 2 + [arguments count] + 1 + 5;
char *argv[argc];
int i = 0;
argv[i++] = commandName;
argv[i++] = strdup([classPath UTF8String]);
argv[i++] = strdup([libraryPath UTF8String]);
argv[i++] = strdup([libraryDirectory UTF8String]);
argv[i++] = strdup([documentsDirectory UTF8String]);
argv[i++] = strdup([applicationSupportDirectory UTF8String]);
argv[i++] = strdup([cachesDirectory UTF8String]);
argv[i++] = strdup([sandboxEnabledVar UTF8String]);
for (NSString *option in options) {
option = [option stringByReplacingOccurrencesOfString:@APP_ROOT_PREFIX withString:[mainBundle bundlePath]];
argv[i++] = strdup([option UTF8String]);
}
argv[i++] = strdup([mainClassName UTF8String]);
for (NSString *argument in arguments) {
argument = [argument stringByReplacingOccurrencesOfString:@APP_ROOT_PREFIX withString:[mainBundle bundlePath]];
argv[i++] = strdup([argument UTF8String]);
}
// Invoke JLI_Launch()
return jli_LaunchFxnPtr(argc, argv,
0, NULL,
0, NULL,
"",
"",
"java",
"java",
FALSE,
FALSE,
FALSE,
0);
}

View File

@@ -0,0 +1,3 @@
"JRELoadError" = "Unable to load Java Runtime Environment.";
"MainClassNameRequired" = "Main class name is required.";
"JavaDirectoryNotFound" = "Unable to enumerate Java directory contents.";

View File

@@ -0,0 +1,731 @@
/*
* 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.BufferedOutputStream;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
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 java.util.Iterator;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
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;
import org.apache.tools.ant.types.Reference;
import org.apache.tools.ant.types.resources.FileResource;
/**
* 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 executableName = EXECUTABLE_NAME;
private String shortVersion = "1.0";
private String version = "1.0";
private String signature = "????";
private String copyright = "";
private String privileged = null;
private String workingDirectory = null;
private String applicationCategory = null;
private boolean highResolutionCapable = true;
// JVM info properties
private String mainClassName = null;
private FileSet runtime = null;
private ArrayList<FileSet> classPath = new ArrayList<>();
private ArrayList<FileSet> libraryPath = new ArrayList<>();
private ArrayList<String> options = new ArrayList<>();
private ArrayList<String> arguments = new ArrayList<>();
private ArrayList<String> architectures = new ArrayList<>();
private ArrayList<BundleDocument> bundleDocuments = new ArrayList<>();
private Reference classPathRef;
private static final String EXECUTABLE_NAME = "JavaAppLauncher";
private static final String DEFAULT_ICON_NAME = "GenericApp.icns";
private static final String OS_TYPE_CODE = "APPL";
private static final String PLIST_DTD = "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">";
private static final String PLIST_TAG = "plist";
private static final String PLIST_VERSION_ATTRIBUTE = "version";
private static final String DICT_TAG = "dict";
private static final String KEY_TAG = "key";
private static final String ARRAY_TAG = "array";
private static final String STRING_TAG = "string";
private static final int BUFFER_SIZE = 2048;
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 setExecutableName(String executable) {
this.executableName = executable;
}
public void setShortVersion(String shortVersion) {
this.shortVersion = shortVersion;
}
public void setVersion(String version) {
this.version = version;
}
public void setSignature(String signature) {
this.signature = signature;
}
public void setCopyright(String copyright) {
this.copyright = copyright;
}
public void setPrivileged(String privileged) {
this.privileged = privileged;
}
public void setWorkingDirectory(String workingDirectory) {
this.workingDirectory = workingDirectory;
}
public void setApplicationCategory(String applicationCategory) {
this.applicationCategory = applicationCategory;
}
public void setHighResolutionCapable(boolean highResolutionCapable) {
this.highResolutionCapable = highResolutionCapable;
}
public void setMainClassName(String mainClassName) {
this.mainClassName = mainClassName;
}
public void addConfiguredRuntime(FileSet runtime) throws BuildException {
if (this.runtime != null) {
throw new BuildException("Runtime already specified.");
}
this.runtime = runtime;
runtime.appendIncludes(new String[] {
"jre/",
});
runtime.appendExcludes(new String[] {
"bin/",
"jre/bin/",
"jre/lib/deploy/",
"jre/lib/deploy.jar",
"jre/lib/javaws.jar",
"jre/lib/libdeploy.dylib",
"jre/lib/libnpjp2.dylib",
"jre/lib/plugin.jar",
"jre/lib/security/javaws.policy"
});
}
public void setClasspathRef(Reference ref) {
this.classPathRef = ref;
}
public void addConfiguredClassPath(FileSet classPath) {
this.classPath.add(classPath);
}
public void addConfiguredLibraryPath(FileSet libraryPath) {
this.libraryPath.add(libraryPath);
}
public void addConfiguredBundleDocument(BundleDocument document) {
this.bundleDocuments.add(document);
}
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);
}
public void addConfiguredArch(Architecture architecture) throws BuildException {
String name = architecture.getName();
if (name == null) {
throw new BuildException("Name is required.");
}
architectures.add(name);
}
@Override
public void execute() throws BuildException {
// Validate required properties
if (outputDirectory == null) {
throw new IllegalStateException("Output directory is required.");
}
if (!outputDirectory.exists()) {
throw new IllegalStateException("Output directory does not exist.");
}
if (!outputDirectory.isDirectory()) {
throw new IllegalStateException("Invalid output 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 (mainClassName == null) {
throw new IllegalStateException("Main class name is required.");
}
// Create the app bundle
try {
System.out.println("Creating app bundle: " + name);
// Create directory structure
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, "Java");
javaDirectory.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, executableName);
copy(getClass().getResource(EXECUTABLE_NAME), executableFile);
executableFile.setExecutable(true, false);
// Copy localized resources to Resources folder
copyResources(resourcesDirectory);
// Copy runtime to PlugIns folder
copyRuntime(plugInsDirectory);
// Copy class path entries to Java folder
copyClassPathEntries(javaDirectory);
// Copy class path ref entries to Java folder
copyClassPathRefEntries(javaDirectory);
// Copy library path entries to MacOS folder
copyLibraryPathEntries(macOSDirectory);
// Copy icon to Resources folder
copyIcon(resourcesDirectory);
} catch (IOException exception) {
throw new BuildException(exception);
}
}
private void copyResources(File resourcesDirectory) throws IOException {
// Unzip res.zip into resources directory
InputStream inputStream = getClass().getResourceAsStream("res.zip");
ZipInputStream zipInputStream = new ZipInputStream(inputStream);
try {
ZipEntry zipEntry = zipInputStream.getNextEntry();
while (zipEntry != null) {
File file = new File(resourcesDirectory, zipEntry.getName());
if (zipEntry.isDirectory()) {
file.mkdir();
} else {
OutputStream outputStream = new BufferedOutputStream(new FileOutputStream(file), BUFFER_SIZE);
try {
int b = zipInputStream.read();
while (b != -1) {
outputStream.write(b);
b = zipInputStream.read();
}
outputStream.flush();
} finally {
outputStream.close();
}
}
zipEntry = zipInputStream.getNextEntry();
}
} finally {
zipInputStream.close();
}
}
private void copyRuntime(File plugInsDirectory) throws IOException {
if (runtime != null) {
File runtimeHomeDirectory = runtime.getDir();
File runtimeContentsDirectory = runtimeHomeDirectory.getParentFile();
File runtimeDirectory = runtimeContentsDirectory.getParentFile();
// Create root plug-in directory
File pluginDirectory = new File(plugInsDirectory, runtimeDirectory.getName());
pluginDirectory.mkdir();
// Create Contents directory
File pluginContentsDirectory = new File(pluginDirectory, runtimeContentsDirectory.getName());
pluginContentsDirectory.mkdir();
// Copy MacOS directory
File runtimeMacOSDirectory = new File(runtimeContentsDirectory, "MacOS");
copy(runtimeMacOSDirectory, new File(pluginContentsDirectory, runtimeMacOSDirectory.getName()));
// Copy Info.plist file
File runtimeInfoPlistFile = new File(runtimeContentsDirectory, "Info.plist");
copy(runtimeInfoPlistFile, new File(pluginContentsDirectory, runtimeInfoPlistFile.getName()));
// Copy included contents of Home directory
File pluginHomeDirectory = new File(pluginContentsDirectory, runtimeHomeDirectory.getName());
DirectoryScanner directoryScanner = runtime.getDirectoryScanner(getProject());
String[] includedFiles = directoryScanner.getIncludedFiles();
for (int i = 0; i < includedFiles.length; i++) {
String includedFile = includedFiles[i];
File source = new File(runtimeHomeDirectory, includedFile);
File destination = new File(pluginHomeDirectory, includedFile);
copy(source, destination);
}
}
}
private void copyClassPathRefEntries(File javaDirectory) throws IOException {
if(classPathRef != null) {
org.apache.tools.ant.types.Path classpath =
(org.apache.tools.ant.types.Path) classPathRef.getReferencedObject(getProject());
Iterator<FileResource> iter = (Iterator<FileResource>)(Object)classpath.iterator();
while(iter.hasNext()) {
FileResource resource = iter.next();
File source = resource.getFile();
File destination = new File(javaDirectory, source.getName());
copy(source, destination);
}
}
}
private void copyClassPathEntries(File javaDirectory) throws IOException {
for (FileSet fileSet : classPath) {
File classPathDirectory = fileSet.getDir();
DirectoryScanner directoryScanner = fileSet.getDirectoryScanner(getProject());
String[] includedFiles = directoryScanner.getIncludedFiles();
for (int i = 0; i < includedFiles.length; i++) {
String includedFile = includedFiles[i];
File source = new File(classPathDirectory, includedFile);
File destination = new File(javaDirectory, new File(includedFile).getName());
copy(source, destination);
}
}
}
private void copyLibraryPathEntries(File macOSDirectory) throws IOException {
for (FileSet fileSet : libraryPath) {
File libraryPathDirectory = fileSet.getDir();
DirectoryScanner directoryScanner = fileSet.getDirectoryScanner(getProject());
String[] includedFiles = directoryScanner.getIncludedFiles();
for (int i = 0; i < includedFiles.length; i++) {
String includedFile = includedFiles[i];
File source = new File(libraryPathDirectory, includedFile);
File destination = new File(macOSDirectory, new File(includedFile).getName());
copy(source, destination);
}
}
}
private void copyIcon(File resourcesDirectory) throws IOException {
if (icon == null) {
copy(getClass().getResource(DEFAULT_ICON_NAME), new File(resourcesDirectory, DEFAULT_ICON_NAME));
} else {
copy(icon, new File(resourcesDirectory, icon.getName()));
}
}
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", executableName);
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, "CFBundleVersion", version);
writeProperty(xout, "CFBundleSignature", signature);
writeProperty(xout, "NSHumanReadableCopyright", copyright);
if (applicationCategory != null) {
writeProperty(xout, "LSApplicationCategoryType", applicationCategory);
}
if (highResolutionCapable) {
writeKey(xout, "NSHighResolutionCapable");
writeBoolean(xout, true);
xout.writeCharacters("\n");
}
// Write runtime
if (runtime != null) {
writeProperty(xout, "JVMRuntime", runtime.getDir().getParentFile().getParentFile().getName());
}
if ( privileged != null ) {
writeProperty(xout, "JVMRunPrivileged", privileged);
}
if ( workingDirectory != null ) {
writeProperty(xout, "WorkingDirectory", workingDirectory);
}
// Write main class name
writeProperty(xout, "JVMMainClassName", mainClassName);
// Write CFBundleDocument entries
writeKey(xout, "CFBundleDocumentTypes");
xout.writeStartElement(ARRAY_TAG);
xout.writeCharacters("\n");
for(BundleDocument bundleDocument: bundleDocuments) {
xout.writeStartElement(DICT_TAG);
xout.writeCharacters("\n");
writeKey(xout, "CFBundleTypeExtensions");
xout.writeStartElement(ARRAY_TAG);
xout.writeCharacters("\n");
for(String extension : bundleDocument.getExtensions()) {
writeString(xout, extension);
}
xout.writeEndElement();
xout.writeCharacters("\n");
if(bundleDocument.hasIcon()) {
writeKey(xout, "CFBundleTypeIconFile");
writeString(xout, bundleDocument.getIcon());
}
writeKey(xout, "CFBundleTypeName");
writeString(xout, bundleDocument.getName());
writeKey(xout, "CFBundleTypeRole");
writeString(xout, bundleDocument.getRole());
writeKey(xout, "LSTypeIsPackage");
writeBoolean(xout, bundleDocument.isPackage());
xout.writeEndElement();
xout.writeCharacters("\n");
}
xout.writeEndElement();
xout.writeCharacters("\n");
// Write architectures
writeKey(xout, "LSArchitecturePriority");
xout.writeStartElement(ARRAY_TAG);
xout.writeCharacters("\n");
for (String architecture : architectures) {
writeString(xout, architecture);
}
xout.writeEndElement();
xout.writeCharacters("\n");
// Write Environment
writeKey(xout, "LSEnvironment");
xout.writeStartElement(DICT_TAG);
xout.writeCharacters("\n");
writeKey(xout, "LC_CTYPE");
writeString(xout, "UTF-8");
xout.writeEndElement();
xout.writeCharacters("\n");
// Write options
writeKey(xout, "JVMOptions");
xout.writeStartElement(ARRAY_TAG);
xout.writeCharacters("\n");
for (String option : options) {
writeString(xout, option);
}
xout.writeEndElement();
xout.writeCharacters("\n");
// Write arguments
writeKey(xout, "JVMArguments");
xout.writeStartElement(ARRAY_TAG);
xout.writeCharacters("\n");
for (String argument : arguments) {
writeString(xout, argument);
}
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 writeBoolean(XMLStreamWriter xout, boolean value) throws XMLStreamException {
xout.writeEmptyElement(value ? "true" : "false");
}
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();
destination.getParentFile().mkdirs();
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));
}
}
}
}

View File

@@ -0,0 +1,45 @@
/*
* Copyright 2012, The Infinite Kind 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. The Infinite Kind designates this
* particular file as subject to the "Classpath" exception as provided
* by The Infinite Kind 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.
*
*/
package com.oracle.appbundler;
/**
* Class representing an architecture that will be written in the Info.plist file
* to indicate which architectures the binary support.
*/
public class Architecture {
private String name = null;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return name;
}
}

View File

@@ -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;
}
}

View File

@@ -0,0 +1,111 @@
/*
* Copyright 2012, The Infinite Kind 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. The Infinite Kind designates this
* particular file as subject to the "Classpath" exception as provided
* by The Infinite Kind 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.
*
*/
package com.oracle.appbundler;
import java.io.File;
import org.apache.tools.ant.BuildException;
/**
* Represent a CFBundleDocument.
*/
public class BundleDocument {
private String name = "editor";
private String role = "";
private String icon = null;
private String[] extensions;
private boolean isPackage = false;
private String capitalizeFirst(String string) {
char[] stringArray = string.toCharArray();
stringArray[0] = Character.toUpperCase(stringArray[0]);
return new String(stringArray);
}
public void setExtensions(String extensionsList) {
if(extensionsList == null) {
throw new BuildException("Extensions can't be null");
}
extensions = extensionsList.split(",");
for (String extension : extensions) {
extension.trim().toLowerCase();
}
}
public void setIcon(String icon) {
this.icon = icon;
}
public void setName(String name) {
this.name = name;
}
public void setRole(String role) {
this.role = capitalizeFirst(role);
}
public void setIsPackage(String isPackageString) {
if(isPackageString.trim().equalsIgnoreCase("true")) {
this.isPackage = true;
} else {
this.isPackage = false;
}
}
public String getIcon() {
return icon;
}
public String getName() {
return name;
}
public String getRole() {
return role;
}
public String[] getExtensions() {
return extensions;
}
public boolean hasIcon() {
return icon != null;
}
public boolean isPackage() {
return isPackage;
}
@Override
public String toString() {
StringBuilder s = new StringBuilder(getName());
s.append(" ").append(getRole()).append(" ").append(getIcon()). append(" ");
for(String extension : extensions) {
s.append(extension).append(" ");
}
return s.toString();
}
}

View File

@@ -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;
}
}

View File

@@ -3,6 +3,8 @@ X background color for present mode has no effect
X https://github.com/processing/processing/issues/2071
X https://github.com/processing/processing/pull/2072
_ Sort out blending differences with P2D/P3D
_ https://github.com/processing/processing/issues/1844
high
_ loadPixels doesn't set alpha value for pixels on Java2D