From dca42f90bd8c33b797e885017ef318a85664f207 Mon Sep 17 00:00:00 2001 From: NathanSweet Date: Mon, 11 Nov 2013 10:37:50 +0100 Subject: [PATCH] Removed extra root directory. --- scar/.classpath => .classpath | 26 +- .gitignore | 7 + scar/.project => .project | 34 +- {scar/dist => dist}/scar | 0 {scar/dist => dist}/scar.bat | 2 +- {scar/lib => lib}/JLzma.jar | Bin {scar/lib => lib}/commons-net-ftp-2.0.jar | Bin {scar/lib => lib}/jsch-0.1.49.jar | Bin {scar/lib => lib}/minlog-1.2.jar | Bin {scar/lib => lib}/wildcard-1.03.jar | Bin {scar/lib => lib}/yamlbeans-1.05.jar | Bin scar/project.yaml => project.yaml | 10 +- .../com/esotericsoftware/scar/Arguments.java | 180 +- .../com/esotericsoftware/scar/Build.java | 886 ++--- .../com/esotericsoftware/scar/Project.java | 700 ++-- .../com/esotericsoftware/scar/Scar.java | 2900 ++++++++--------- 16 files changed, 2376 insertions(+), 2369 deletions(-) rename scar/.classpath => .classpath (98%) create mode 100644 .gitignore rename scar/.project => .project (95%) rename {scar/dist => dist}/scar (100%) rename {scar/dist => dist}/scar.bat (96%) rename {scar/lib => lib}/JLzma.jar (100%) rename {scar/lib => lib}/commons-net-ftp-2.0.jar (100%) rename {scar/lib => lib}/jsch-0.1.49.jar (100%) rename {scar/lib => lib}/minlog-1.2.jar (100%) rename {scar/lib => lib}/wildcard-1.03.jar (100%) rename {scar/lib => lib}/yamlbeans-1.05.jar (100%) rename scar/project.yaml => project.yaml (95%) rename {scar/src => src}/com/esotericsoftware/scar/Arguments.java (96%) rename {scar/src => src}/com/esotericsoftware/scar/Build.java (97%) rename {scar/src => src}/com/esotericsoftware/scar/Project.java (96%) rename {scar/src => src}/com/esotericsoftware/scar/Scar.java (97%) diff --git a/scar/.classpath b/.classpath similarity index 98% rename from scar/.classpath rename to .classpath index 1e891f0..fc0ea92 100644 --- a/scar/.classpath +++ b/.classpath @@ -1,13 +1,13 @@ - - - - - - - - - - - - - + + + + + + + + + + + + + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d5b2315 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +# Temporary Files +*~ +.*.swp +.DS_STORE + +bin/ +target/ diff --git a/scar/.project b/.project similarity index 95% rename from scar/.project rename to .project index bf7904f..723864f 100644 --- a/scar/.project +++ b/.project @@ -1,17 +1,17 @@ - - - scar - - - - - - org.eclipse.jdt.core.javabuilder - - - - - - org.eclipse.jdt.core.javanature - - + + + scar + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/scar/dist/scar b/dist/scar similarity index 100% rename from scar/dist/scar rename to dist/scar diff --git a/scar/dist/scar.bat b/dist/scar.bat similarity index 96% rename from scar/dist/scar.bat rename to dist/scar.bat index 17d8e2a..543b8f5 100644 --- a/scar/dist/scar.bat +++ b/dist/scar.bat @@ -1 +1 @@ -java -jar "%~dp0scar.jar" +java -jar "%~dp0scar.jar" diff --git a/scar/lib/JLzma.jar b/lib/JLzma.jar similarity index 100% rename from scar/lib/JLzma.jar rename to lib/JLzma.jar diff --git a/scar/lib/commons-net-ftp-2.0.jar b/lib/commons-net-ftp-2.0.jar similarity index 100% rename from scar/lib/commons-net-ftp-2.0.jar rename to lib/commons-net-ftp-2.0.jar diff --git a/scar/lib/jsch-0.1.49.jar b/lib/jsch-0.1.49.jar similarity index 100% rename from scar/lib/jsch-0.1.49.jar rename to lib/jsch-0.1.49.jar diff --git a/scar/lib/minlog-1.2.jar b/lib/minlog-1.2.jar similarity index 100% rename from scar/lib/minlog-1.2.jar rename to lib/minlog-1.2.jar diff --git a/scar/lib/wildcard-1.03.jar b/lib/wildcard-1.03.jar similarity index 100% rename from scar/lib/wildcard-1.03.jar rename to lib/wildcard-1.03.jar diff --git a/scar/lib/yamlbeans-1.05.jar b/lib/yamlbeans-1.05.jar similarity index 100% rename from scar/lib/yamlbeans-1.05.jar rename to lib/yamlbeans-1.05.jar diff --git a/scar/project.yaml b/project.yaml similarity index 95% rename from scar/project.yaml rename to project.yaml index dc5ea8f..9573597 100644 --- a/scar/project.yaml +++ b/project.yaml @@ -1,5 +1,5 @@ -main: com.esotericsoftware.scar.Build -version: 1.09 ---- -Build.build(project); -Build.oneJAR(project); +main: com.esotericsoftware.scar.Build +version: 1.09 +--- +Build.build(project); +Build.oneJAR(project); diff --git a/scar/src/com/esotericsoftware/scar/Arguments.java b/src/com/esotericsoftware/scar/Arguments.java similarity index 96% rename from scar/src/com/esotericsoftware/scar/Arguments.java rename to src/com/esotericsoftware/scar/Arguments.java index 292a777..47e96d1 100644 --- a/scar/src/com/esotericsoftware/scar/Arguments.java +++ b/src/com/esotericsoftware/scar/Arguments.java @@ -1,90 +1,90 @@ - -package com.esotericsoftware.scar; - -import java.util.LinkedHashMap; - -/** - * Stores command line arguments as name/value pairs. Arguments containing an equals sign are considered a name/value pair. All - * other arguments are stored as a name/value pair with a null value. - */ -public class Arguments { - private final LinkedHashMap parameters = new LinkedHashMap(); - - public Arguments () { - } - - public Arguments (String[] args) { - this(args, 0); - } - - public Arguments (String[] args, int startIndex) { - for (int i = startIndex; i < args.length; i++) { - String[] nameValuePair = args[i].split("=", 2); - if (nameValuePair.length == 2) - parameters.put(nameValuePair[0], nameValuePair[1]); - else - parameters.put(args[i], null); - } - } - - /** - * Returns true if the argument was specified. - */ - public boolean has (String name) { - if (name == null) throw new IllegalArgumentException("name cannot be null."); - return parameters.containsKey(name); - } - - /** - * Returns the value of the argument with the specified name, or null if the argument was specified without a value or was not - * specified. - */ - public String get (String name) { - if (name == null) throw new IllegalArgumentException("name cannot be null."); - return parameters.get(name); - } - - /** - * Returns the value of the argument with the specified name, or the specified default value if the argument was specified - * without a value or was not specified. - */ - public String get (String name, String defaultValue) { - String value = parameters.get(name); - if (value == null) return defaultValue; - return value; - } - - public void set (String name) { - set(name, null); - } - - public void set (String name, String value) { - if (name == null) throw new IllegalArgumentException("name cannot be null."); - parameters.put(name, value); - } - - public int count () { - return parameters.size(); - } - - public String remove (String name) { - if (name == null) throw new IllegalArgumentException("name cannot be null."); - return parameters.remove(name); - } - - public String toString () { - StringBuffer buffer = new StringBuffer(100); - for (String param : parameters.keySet()) { - if (buffer.length() > 1) buffer.append(' '); - String value = get(param); - if (value == null) - buffer.append(param); - else { - buffer.append(param); - buffer.append('='); - buffer.append(value); - } - } - return buffer.toString(); - } -} + +package com.esotericsoftware.scar; + +import java.util.LinkedHashMap; + +/** + * Stores command line arguments as name/value pairs. Arguments containing an equals sign are considered a name/value pair. All + * other arguments are stored as a name/value pair with a null value. + */ +public class Arguments { + private final LinkedHashMap parameters = new LinkedHashMap(); + + public Arguments () { + } + + public Arguments (String[] args) { + this(args, 0); + } + + public Arguments (String[] args, int startIndex) { + for (int i = startIndex; i < args.length; i++) { + String[] nameValuePair = args[i].split("=", 2); + if (nameValuePair.length == 2) + parameters.put(nameValuePair[0], nameValuePair[1]); + else + parameters.put(args[i], null); + } + } + + /** + * Returns true if the argument was specified. + */ + public boolean has (String name) { + if (name == null) throw new IllegalArgumentException("name cannot be null."); + return parameters.containsKey(name); + } + + /** + * Returns the value of the argument with the specified name, or null if the argument was specified without a value or was not + * specified. + */ + public String get (String name) { + if (name == null) throw new IllegalArgumentException("name cannot be null."); + return parameters.get(name); + } + + /** + * Returns the value of the argument with the specified name, or the specified default value if the argument was specified + * without a value or was not specified. + */ + public String get (String name, String defaultValue) { + String value = parameters.get(name); + if (value == null) return defaultValue; + return value; + } + + public void set (String name) { + set(name, null); + } + + public void set (String name, String value) { + if (name == null) throw new IllegalArgumentException("name cannot be null."); + parameters.put(name, value); + } + + public int count () { + return parameters.size(); + } + + public String remove (String name) { + if (name == null) throw new IllegalArgumentException("name cannot be null."); + return parameters.remove(name); + } + + public String toString () { + StringBuffer buffer = new StringBuffer(100); + for (String param : parameters.keySet()) { + if (buffer.length() > 1) buffer.append(' '); + String value = get(param); + if (value == null) + buffer.append(param); + else { + buffer.append(param); + buffer.append('='); + buffer.append(value); + } + } + return buffer.toString(); + } +} diff --git a/scar/src/com/esotericsoftware/scar/Build.java b/src/com/esotericsoftware/scar/Build.java similarity index 97% rename from scar/src/com/esotericsoftware/scar/Build.java rename to src/com/esotericsoftware/scar/Build.java index 6e06ff2..3eb215c 100644 --- a/scar/src/com/esotericsoftware/scar/Build.java +++ b/src/com/esotericsoftware/scar/Build.java @@ -1,443 +1,443 @@ - -package com.esotericsoftware.scar; - -import java.io.File; -import java.io.IOException; -import java.net.URL; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; - -import com.esotericsoftware.wildcard.Paths; - -import static com.esotericsoftware.minlog.Log.*; -import static com.esotericsoftware.scar.Scar.*; - -public class Build extends Project { - /** List of project names that have been built. {@link Build#buildDependencies(Project)} will skip any projects with a matching - * name. */ - static public final List builtProjects = new ArrayList(); - - /** Loads the specified project with default values and loads any other projects needed for the "include" property. - * @param path Path to a YAML project file, or a directory containing a "project.yaml" file. */ - static public Project project (String path) throws IOException { - if (path == null) throw new IllegalArgumentException("path cannot be null."); - - Project defaults = new Project(); - - File file = new File(canonical(path)); - if (file.isDirectory()) { - String name = file.getName(); - defaults.set("name", name); - defaults.set("target", file.getParent() + "/target/" + name + "/"); - } else { - String name = file.getParentFile().getName(); - defaults.set("name", name); - defaults.set("target", file.getParentFile().getParent() + "/target/" + name + "/"); - } - - ArrayList libs = new ArrayList(); - libs.add("lib|**/*.jar"); - libs.add("libs|**/*.jar"); - defaults.set("classpath", libs); - - defaults.set("dist", "dist"); - - ArrayList source = new ArrayList(); - source.add("src|**/*.java"); - source.add("src/main/java|**/*.java"); - defaults.set("source", source); - - ArrayList resources = new ArrayList(); - resources.add("assets"); - resources.add("resources"); - resources.add("src/main/resources"); - defaults.set("resources", resources); - - Project project = project(path, defaults); - - // Remove dependency if a JAR of the same name is on the classpath. - Paths classpath = project.getPaths("classpath"); - classpath.add(dependencyClasspaths(project, classpath, false, false)); - for (String dependency : project.getList("dependencies")) { - String dependencyName = project(project.path(dependency)).get("name"); - for (String classpathFile : classpath) { - String name = fileWithoutExtension(classpathFile); - int dashIndex = name.lastIndexOf('-'); - if (dashIndex != -1) name = name.substring(0, dashIndex); - if (name.equals(dependencyName)) { - if (DEBUG) { - debug(project.toString(), "Ignoring dependency: " + dependencyName + " (already on classpath: " + classpathFile - + ")"); - } - project.remove("dependencies", dependency); - break; - } - } - } - - return project; - } - - /** Loads the specified project with the specified defaults and loads any other projects needed for the "include" property. - * @param path Path to a YAML project file, or a directory containing a "project.yaml" file. */ - static public Project project (String path, Project defaults) throws IOException { - if (path == null) throw new IllegalArgumentException("path cannot be null."); - if (defaults == null) throw new IllegalArgumentException("defaults cannot be null."); - - Project actualProject = new Project(path); - - Project project = new Project(); - project.replace(defaults); - - File parent = new File(actualProject.getDirectory()).getParentFile(); - while (parent != null) { - File includeFile = new File(parent, "include.yaml"); - if (includeFile.exists()) { - try { - project.replace(project(includeFile.getAbsolutePath(), defaults)); - } catch (RuntimeException ex) { - throw new RuntimeException("Error loading included project: " + includeFile.getAbsolutePath(), ex); - } - } - parent = parent.getParentFile(); - } - - for (String include : actualProject.getList("include")) { - try { - project.replace(project(actualProject.path(include), defaults)); - } catch (RuntimeException ex) { - throw new RuntimeException("Error loading included project: " + actualProject.path(include), ex); - } - } - project.replace(actualProject); - return project; - } - - /** Deletes the "target" directory and all files and directories under it. */ - static public void clean (Project project) { - if (project == null) throw new IllegalArgumentException("project cannot be null."); - - if (INFO) info(project.toString(), "Clean"); - if (TRACE) trace(project.toString(), "Deleting: " + project.path("$target$")); - paths(project.path("$target$")).delete(); - } - - /** Computes the classpath for the specified project and all its dependency projects, recursively. */ - static public Paths classpath (Project project, boolean errorIfDepenenciesNotBuilt) throws IOException { - if (project == null) throw new IllegalArgumentException("project cannot be null."); - - Paths classpath = project.getPaths("classpath"); - classpath.add(dependencyClasspaths(project, classpath, true, errorIfDepenenciesNotBuilt)); - return classpath; - } - - /** Computes the classpath for all the dependencies of the specified project, recursively. */ - static private Paths dependencyClasspaths (Project project, Paths paths, boolean includeDependencyJAR, - boolean errorIfDepenenciesNotBuilt) throws IOException { - for (String dependency : project.getList("dependencies")) { - Project dependencyProject = project(project.path(dependency)); - String dependencyTarget = dependencyProject.path("$target$/"); - if (errorIfDepenenciesNotBuilt && !fileExists(dependencyTarget)) - throw new RuntimeException("Dependency has not been built: " + dependency + "\nAbsolute dependency path: " - + canonical(dependency) + "\nMissing dependency target: " + canonical(dependencyTarget)); - if (includeDependencyJAR) paths.glob(dependencyTarget, "*.jar"); - paths.add(classpath(dependencyProject, errorIfDepenenciesNotBuilt)); - } - return paths; - } - - /** Collects the source files using the "source" property and compiles them into a "classes" directory under the target - * directory. It uses "classpath" and "dependencies" to find the libraries required to compile the source. - *

- * Note: Each dependency project is not built automatically. Each needs to be built before the dependent project. - * @return The path to the "classes" directory. */ - static public String compile (Project project) throws IOException { - if (project == null) throw new IllegalArgumentException("project cannot be null."); - - Paths classpath = classpath(project, true); - Paths source = project.getPaths("source"); - - if (INFO) info(project.toString(), "Compile"); - if (DEBUG) { - debug(project.toString(), "Source: " + source.count() + " files"); - debug(project.toString(), "Classpath: " + classpath); - } - - String classesDir = mkdir(project.path("$target$/classes/")); - - Scar.compile(source, classpath, classesDir); - return classesDir; - } - - /** Collects the class files from the "classes" directory and all the resource files using the "resources" property and encodes - * them into a JAR file. - * - * If the resources don't contain a META-INF/MANIFEST.MF file, one is generated. If the project has a main property, the - * generated manifest will include "Main-Class" and "Class-Path" entries to allow the main class to be run with "java -jar". - * @return The path to the created JAR file. */ - static public String jar (Project project) throws IOException { - if (project == null) throw new IllegalArgumentException("project cannot be null."); - - if (INFO) info(project.toString(), "JAR"); - - String jarDir = mkdir(project.path("$target$/jar/")); - - String classesDir = project.path("$target$/classes/"); - paths(classesDir, "**/*.class").copyTo(jarDir); - project.getPaths("resources").copyTo(jarDir); - - String jarFile; - if (project.has("version")) - jarFile = project.path("$target$/$name$-$version$.jar"); - else - jarFile = project.path("$target$/$name$.jar"); - - Scar.jar(jarFile, jarDir, project.get("main"), classpath(project, true)); - return jarFile; - } - - /** Collects the distribution files using the "dist" property, the project's JAR file, and everything on the project's classpath - * (including dependency project classpaths) and places them into a "dist" directory under the "target" directory. This is also - * done for depenency projects, recursively. This is everything the application needs to be run from JAR files. - * @return The path to the "dist" directory. */ - static public String dist (Project project) throws IOException { - if (project == null) throw new IllegalArgumentException("project cannot be null."); - - if (INFO) info(project.toString(), "Dist"); - - String distDir = mkdir(project.path("$target$/dist/")); - classpath(project, true).copyTo(distDir); - Paths distPaths = project.getPaths("dist"); - dependencyDistPaths(project, distPaths); - distPaths.copyTo(distDir); - paths(project.path("$target$"), "*.jar").copyTo(distDir); - return distDir; - } - - static private Paths dependencyDistPaths (Project project, Paths paths) throws IOException { - if (project == null) throw new IllegalArgumentException("project cannot be null."); - - for (String dependency : project.getList("dependencies")) { - Project dependencyProject = project(project.path(dependency)); - String dependencyTarget = dependencyProject.path("$target$/"); - if (!fileExists(dependencyTarget)) throw new RuntimeException("Dependency has not been built: " + dependency); - paths.glob(dependencyTarget + "dist", "!*/**.jar"); - paths.add(dependencyDistPaths(dependencyProject, paths)); - } - return paths; - } - - /** Copies all the JAR and JNLP files from the "dist" directory to a "jws" directory under the "target" directory. It then uses - * the specified keystore to sign each JAR. If the "pack" parameter is true, it also compresses each JAR using pack200 and - * GZIP. */ - static public void jws (Project project, boolean pack, String keystoreFile, String alias, String password) throws IOException { - if (project == null) throw new IllegalArgumentException("project cannot be null."); - if (keystoreFile == null) throw new IllegalArgumentException("keystoreFile cannot be null."); - if (alias == null) throw new IllegalArgumentException("alias cannot be null."); - if (password == null) throw new IllegalArgumentException("password cannot be null."); - if (password.length() < 6) throw new IllegalArgumentException("password must be 6 or more characters."); - - if (INFO) info(project.toString(), "JWS"); - - String jwsDir = mkdir(project.path("$target$/jws/")); - String distDir = project.path("$target$/dist/"); - Scar.jws(distDir, jwsDir, pack, keystoreFile, alias, password); - } - - /** Generates ".htaccess" and "type map" VAR files in the "jws" directory. These files allow Apache to serve both pack200/GZIP - * JARs and regular JARs, based on capability of the client requesting the JAR. */ - static public void jwsHtaccess (Project project) throws IOException { - if (project == null) throw new IllegalArgumentException("project cannot be null."); - - if (INFO) info(project.toString(), "JWS htaccess"); - - Scar.jwsHtaccess(mkdir(project.path("$target$/jws/"))); - } - - /** Generates a JNLP file in the "jws" directory. JARs in the "jws" directory are included in the JNLP. JARs containing "native" - * and "win", "mac", "linux", or "solaris" are properly included in the native section of the JNLP. The "main" property is used - * for the main class in the JNLP. - * @param splashImage Can be null. */ - static public void jnlp (Project project, String url, String company, String title, String splashImage) throws IOException { - if (project == null) throw new IllegalArgumentException("project cannot be null."); - if (url == null) throw new IllegalArgumentException("url cannot be null."); - if (!url.startsWith("http")) throw new RuntimeException("Invalid url: " + url); - if (company == null) throw new IllegalArgumentException("company cannot be null."); - if (title == null) throw new IllegalArgumentException("title cannot be null."); - - if (DEBUG) - debug(project.toString(), "JNLP (" + url + ", " + company + ", " + title + ", " + splashImage + ")"); - else if (INFO) // - info(project.toString(), "JNLP"); - - if (!project.has("main")) throw new RuntimeException("Unable to generate JNLP: project has no main class"); - - String projectJarName; - if (project.has("version")) - projectJarName = project.format("$name$-$version$.jar"); - else - projectJarName = project.format("$name$.jar"); - - String jwsDir = mkdir(project.path("$target$/jws/")); - Scar.jnlp(jwsDir, project.get("main"), projectJarName, url, title, company, splashImage); - } - - static public String lwjglApplet (Project project, String keystoreFile, String alias, String password) throws IOException { - if (project == null) throw new IllegalArgumentException("project cannot be null."); - - if (INFO) info(project.toString(), "LWJGL applet"); - - String distDir = project.path("$target$/dist/"); - String appletDir = mkdir(project.path("$target$/applet-lwjgl/")); - - Scar.lwjglApplet(distDir, appletDir, keystoreFile, alias, password); - - return appletDir; - } - - static public String lwjglAppletHtml (Project project) throws IOException { - if (project == null) throw new IllegalArgumentException("project cannot be null."); - - if (INFO) info(project.toString(), "LWJGL applet HTML"); - - String appletDir = mkdir(project.path("$target$/applet-lwjgl/")); - - Scar.lwjglAppletHtml(appletDir, project.get("main")); - - return appletDir; - } - - /** Unzips all JARs in the "dist" directory and creates a single JAR containing those files in the "dist/onejar" directory. The - * manifest from the project's JAR is used. Putting everything into a single JAR makes it harder to see what libraries are - * being used, but makes it easier for end users to distribute the application. - *

- * Note: Files with the same path in different JARs will be overwritten. Files in the project's JAR will never be overwritten, - * but may overwrite other files. - * @param excludeJARs The names of any JARs to exclude. */ - static public void oneJAR (Project project, String... excludeJARs) throws IOException { - if (project == null) throw new IllegalArgumentException("project cannot be null."); - - if (INFO) info(project.toString(), "One JAR"); - - String onejarDir = mkdir(project.path("$target$/onejar/")); - String distDir = project.path("$target$/dist/"); - String projectJarName; - if (project.has("version")) - projectJarName = project.format("$name$-$version$.jar"); - else - projectJarName = project.format("$name$.jar"); - - ArrayList processedJARs = new ArrayList(); - outer: - for (String jarFile : paths(distDir, "*.jar", "!" + projectJarName)) { - String jarName = fileName(jarFile); - for (String exclude : excludeJARs) - if (jarName.equals(exclude)) continue outer; - unzip(jarFile, onejarDir); - processedJARs.add(jarFile); - } - unzip(distDir + projectJarName, onejarDir); - - String onejarFile; - if (project.has("version")) - onejarFile = project.path("$target$/dist/onejar/$name$-$version$-all.jar"); - else - onejarFile = project.path("$target$/dist/onejar/$name$-all.jar"); - mkdir(parent(onejarFile)); - - if (project.has("main")) new File(onejarDir, "META-INF/MANIFEST.MF").delete(); - - Scar.jar(onejarFile, onejarDir, project.get("main"), classpath(project, true)); - } - - /** Calls {@link #build(Project)} for each dependency project in the specified project. */ - static public void buildDependencies (Project project) throws IOException { - if (project == null) throw new IllegalArgumentException("project cannot be null."); - - for (String dependency : project.getList("dependencies")) { - Project dependencyProject = project(project.path(dependency)); - - if (builtProjects.contains(dependencyProject.get("name"))) { - if (DEBUG) debug(project.toString(), "Dependency project already built: " + dependencyProject); - continue; - } - - String jarFile; - if (dependencyProject.has("version")) - jarFile = dependencyProject.path("$target$/$name$-$version$.jar"); - else - jarFile = dependencyProject.path("$target$/$name$.jar"); - - if (DEBUG) debug("Building dependency: " + dependencyProject); - if (!executeDocument(dependencyProject)) build(dependencyProject); - } - } - - /** Calls {@link #project(String)} and then {@link #build(Project)}. */ - static public void build (String path) throws IOException { - build(project(path)); - } - - /** Executes the buildDependencies, clean, compile, jar, and dist utility metshods. */ - static public void build (Project project) throws IOException { - if (project == null) throw new IllegalArgumentException("project cannot be null."); - - buildDependencies(project); - - if (INFO) info(project.toString(), "Target: " + project.path("$target$")); - - clean(project); - try { - Thread.sleep(100); - } catch (InterruptedException ignored) { - } - compile(project); - try { - Thread.sleep(100); - } catch (InterruptedException ignored) { - } - jar(project); - dist(project); - - if (paths(project.path("$target$")).filesOnly().isEmpty()) { - if (WARN) warn(project.toString(), "Empty target folder."); - delete(project.path("$target$")); - } - - builtProjects.add(project.get("name")); - } - - /** Executes Java code in the specified project's document, if any. - * @return true if code was executed. */ - static public boolean executeDocument (Project project) throws IOException { - String code = project.getDocument(); - if (code == null || code.trim().isEmpty()) return false; - HashMap parameters = new HashMap(); - parameters.put("project", project); - try { - Scar.executeCode(code, parameters, project); - } catch (RuntimeException ex) { - throw new RuntimeException("Error executing code for project: " + project, ex); - } - return true; - } - - static public void main (String[] args) throws IOException { - Scar.args = new Arguments(args); - - if (Scar.args.has("trace")) - TRACE(); - else if (Scar.args.has("debug")) - DEBUG(); - else if (Scar.args.has("info")) - INFO(); - else if (Scar.args.has("warn")) - WARN(); - else if (Scar.args.has("error")) // - ERROR(); - - Project project = project(Scar.args.get("file", ".")); - if (!executeDocument(project)) build(project); - } -} + +package com.esotericsoftware.scar; + +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import com.esotericsoftware.wildcard.Paths; + +import static com.esotericsoftware.minlog.Log.*; +import static com.esotericsoftware.scar.Scar.*; + +public class Build extends Project { + /** List of project names that have been built. {@link Build#buildDependencies(Project)} will skip any projects with a matching + * name. */ + static public final List builtProjects = new ArrayList(); + + /** Loads the specified project with default values and loads any other projects needed for the "include" property. + * @param path Path to a YAML project file, or a directory containing a "project.yaml" file. */ + static public Project project (String path) throws IOException { + if (path == null) throw new IllegalArgumentException("path cannot be null."); + + Project defaults = new Project(); + + File file = new File(canonical(path)); + if (file.isDirectory()) { + String name = file.getName(); + defaults.set("name", name); + defaults.set("target", file.getParent() + "/target/" + name + "/"); + } else { + String name = file.getParentFile().getName(); + defaults.set("name", name); + defaults.set("target", file.getParentFile().getParent() + "/target/" + name + "/"); + } + + ArrayList libs = new ArrayList(); + libs.add("lib|**/*.jar"); + libs.add("libs|**/*.jar"); + defaults.set("classpath", libs); + + defaults.set("dist", "dist"); + + ArrayList source = new ArrayList(); + source.add("src|**/*.java"); + source.add("src/main/java|**/*.java"); + defaults.set("source", source); + + ArrayList resources = new ArrayList(); + resources.add("assets"); + resources.add("resources"); + resources.add("src/main/resources"); + defaults.set("resources", resources); + + Project project = project(path, defaults); + + // Remove dependency if a JAR of the same name is on the classpath. + Paths classpath = project.getPaths("classpath"); + classpath.add(dependencyClasspaths(project, classpath, false, false)); + for (String dependency : project.getList("dependencies")) { + String dependencyName = project(project.path(dependency)).get("name"); + for (String classpathFile : classpath) { + String name = fileWithoutExtension(classpathFile); + int dashIndex = name.lastIndexOf('-'); + if (dashIndex != -1) name = name.substring(0, dashIndex); + if (name.equals(dependencyName)) { + if (DEBUG) { + debug(project.toString(), "Ignoring dependency: " + dependencyName + " (already on classpath: " + classpathFile + + ")"); + } + project.remove("dependencies", dependency); + break; + } + } + } + + return project; + } + + /** Loads the specified project with the specified defaults and loads any other projects needed for the "include" property. + * @param path Path to a YAML project file, or a directory containing a "project.yaml" file. */ + static public Project project (String path, Project defaults) throws IOException { + if (path == null) throw new IllegalArgumentException("path cannot be null."); + if (defaults == null) throw new IllegalArgumentException("defaults cannot be null."); + + Project actualProject = new Project(path); + + Project project = new Project(); + project.replace(defaults); + + File parent = new File(actualProject.getDirectory()).getParentFile(); + while (parent != null) { + File includeFile = new File(parent, "include.yaml"); + if (includeFile.exists()) { + try { + project.replace(project(includeFile.getAbsolutePath(), defaults)); + } catch (RuntimeException ex) { + throw new RuntimeException("Error loading included project: " + includeFile.getAbsolutePath(), ex); + } + } + parent = parent.getParentFile(); + } + + for (String include : actualProject.getList("include")) { + try { + project.replace(project(actualProject.path(include), defaults)); + } catch (RuntimeException ex) { + throw new RuntimeException("Error loading included project: " + actualProject.path(include), ex); + } + } + project.replace(actualProject); + return project; + } + + /** Deletes the "target" directory and all files and directories under it. */ + static public void clean (Project project) { + if (project == null) throw new IllegalArgumentException("project cannot be null."); + + if (INFO) info(project.toString(), "Clean"); + if (TRACE) trace(project.toString(), "Deleting: " + project.path("$target$")); + paths(project.path("$target$")).delete(); + } + + /** Computes the classpath for the specified project and all its dependency projects, recursively. */ + static public Paths classpath (Project project, boolean errorIfDepenenciesNotBuilt) throws IOException { + if (project == null) throw new IllegalArgumentException("project cannot be null."); + + Paths classpath = project.getPaths("classpath"); + classpath.add(dependencyClasspaths(project, classpath, true, errorIfDepenenciesNotBuilt)); + return classpath; + } + + /** Computes the classpath for all the dependencies of the specified project, recursively. */ + static private Paths dependencyClasspaths (Project project, Paths paths, boolean includeDependencyJAR, + boolean errorIfDepenenciesNotBuilt) throws IOException { + for (String dependency : project.getList("dependencies")) { + Project dependencyProject = project(project.path(dependency)); + String dependencyTarget = dependencyProject.path("$target$/"); + if (errorIfDepenenciesNotBuilt && !fileExists(dependencyTarget)) + throw new RuntimeException("Dependency has not been built: " + dependency + "\nAbsolute dependency path: " + + canonical(dependency) + "\nMissing dependency target: " + canonical(dependencyTarget)); + if (includeDependencyJAR) paths.glob(dependencyTarget, "*.jar"); + paths.add(classpath(dependencyProject, errorIfDepenenciesNotBuilt)); + } + return paths; + } + + /** Collects the source files using the "source" property and compiles them into a "classes" directory under the target + * directory. It uses "classpath" and "dependencies" to find the libraries required to compile the source. + *

+ * Note: Each dependency project is not built automatically. Each needs to be built before the dependent project. + * @return The path to the "classes" directory. */ + static public String compile (Project project) throws IOException { + if (project == null) throw new IllegalArgumentException("project cannot be null."); + + Paths classpath = classpath(project, true); + Paths source = project.getPaths("source"); + + if (INFO) info(project.toString(), "Compile"); + if (DEBUG) { + debug(project.toString(), "Source: " + source.count() + " files"); + debug(project.toString(), "Classpath: " + classpath); + } + + String classesDir = mkdir(project.path("$target$/classes/")); + + Scar.compile(source, classpath, classesDir); + return classesDir; + } + + /** Collects the class files from the "classes" directory and all the resource files using the "resources" property and encodes + * them into a JAR file. + * + * If the resources don't contain a META-INF/MANIFEST.MF file, one is generated. If the project has a main property, the + * generated manifest will include "Main-Class" and "Class-Path" entries to allow the main class to be run with "java -jar". + * @return The path to the created JAR file. */ + static public String jar (Project project) throws IOException { + if (project == null) throw new IllegalArgumentException("project cannot be null."); + + if (INFO) info(project.toString(), "JAR"); + + String jarDir = mkdir(project.path("$target$/jar/")); + + String classesDir = project.path("$target$/classes/"); + paths(classesDir, "**/*.class").copyTo(jarDir); + project.getPaths("resources").copyTo(jarDir); + + String jarFile; + if (project.has("version")) + jarFile = project.path("$target$/$name$-$version$.jar"); + else + jarFile = project.path("$target$/$name$.jar"); + + Scar.jar(jarFile, jarDir, project.get("main"), classpath(project, true)); + return jarFile; + } + + /** Collects the distribution files using the "dist" property, the project's JAR file, and everything on the project's classpath + * (including dependency project classpaths) and places them into a "dist" directory under the "target" directory. This is also + * done for depenency projects, recursively. This is everything the application needs to be run from JAR files. + * @return The path to the "dist" directory. */ + static public String dist (Project project) throws IOException { + if (project == null) throw new IllegalArgumentException("project cannot be null."); + + if (INFO) info(project.toString(), "Dist"); + + String distDir = mkdir(project.path("$target$/dist/")); + classpath(project, true).copyTo(distDir); + Paths distPaths = project.getPaths("dist"); + dependencyDistPaths(project, distPaths); + distPaths.copyTo(distDir); + paths(project.path("$target$"), "*.jar").copyTo(distDir); + return distDir; + } + + static private Paths dependencyDistPaths (Project project, Paths paths) throws IOException { + if (project == null) throw new IllegalArgumentException("project cannot be null."); + + for (String dependency : project.getList("dependencies")) { + Project dependencyProject = project(project.path(dependency)); + String dependencyTarget = dependencyProject.path("$target$/"); + if (!fileExists(dependencyTarget)) throw new RuntimeException("Dependency has not been built: " + dependency); + paths.glob(dependencyTarget + "dist", "!*/**.jar"); + paths.add(dependencyDistPaths(dependencyProject, paths)); + } + return paths; + } + + /** Copies all the JAR and JNLP files from the "dist" directory to a "jws" directory under the "target" directory. It then uses + * the specified keystore to sign each JAR. If the "pack" parameter is true, it also compresses each JAR using pack200 and + * GZIP. */ + static public void jws (Project project, boolean pack, String keystoreFile, String alias, String password) throws IOException { + if (project == null) throw new IllegalArgumentException("project cannot be null."); + if (keystoreFile == null) throw new IllegalArgumentException("keystoreFile cannot be null."); + if (alias == null) throw new IllegalArgumentException("alias cannot be null."); + if (password == null) throw new IllegalArgumentException("password cannot be null."); + if (password.length() < 6) throw new IllegalArgumentException("password must be 6 or more characters."); + + if (INFO) info(project.toString(), "JWS"); + + String jwsDir = mkdir(project.path("$target$/jws/")); + String distDir = project.path("$target$/dist/"); + Scar.jws(distDir, jwsDir, pack, keystoreFile, alias, password); + } + + /** Generates ".htaccess" and "type map" VAR files in the "jws" directory. These files allow Apache to serve both pack200/GZIP + * JARs and regular JARs, based on capability of the client requesting the JAR. */ + static public void jwsHtaccess (Project project) throws IOException { + if (project == null) throw new IllegalArgumentException("project cannot be null."); + + if (INFO) info(project.toString(), "JWS htaccess"); + + Scar.jwsHtaccess(mkdir(project.path("$target$/jws/"))); + } + + /** Generates a JNLP file in the "jws" directory. JARs in the "jws" directory are included in the JNLP. JARs containing "native" + * and "win", "mac", "linux", or "solaris" are properly included in the native section of the JNLP. The "main" property is used + * for the main class in the JNLP. + * @param splashImage Can be null. */ + static public void jnlp (Project project, String url, String company, String title, String splashImage) throws IOException { + if (project == null) throw new IllegalArgumentException("project cannot be null."); + if (url == null) throw new IllegalArgumentException("url cannot be null."); + if (!url.startsWith("http")) throw new RuntimeException("Invalid url: " + url); + if (company == null) throw new IllegalArgumentException("company cannot be null."); + if (title == null) throw new IllegalArgumentException("title cannot be null."); + + if (DEBUG) + debug(project.toString(), "JNLP (" + url + ", " + company + ", " + title + ", " + splashImage + ")"); + else if (INFO) // + info(project.toString(), "JNLP"); + + if (!project.has("main")) throw new RuntimeException("Unable to generate JNLP: project has no main class"); + + String projectJarName; + if (project.has("version")) + projectJarName = project.format("$name$-$version$.jar"); + else + projectJarName = project.format("$name$.jar"); + + String jwsDir = mkdir(project.path("$target$/jws/")); + Scar.jnlp(jwsDir, project.get("main"), projectJarName, url, title, company, splashImage); + } + + static public String lwjglApplet (Project project, String keystoreFile, String alias, String password) throws IOException { + if (project == null) throw new IllegalArgumentException("project cannot be null."); + + if (INFO) info(project.toString(), "LWJGL applet"); + + String distDir = project.path("$target$/dist/"); + String appletDir = mkdir(project.path("$target$/applet-lwjgl/")); + + Scar.lwjglApplet(distDir, appletDir, keystoreFile, alias, password); + + return appletDir; + } + + static public String lwjglAppletHtml (Project project) throws IOException { + if (project == null) throw new IllegalArgumentException("project cannot be null."); + + if (INFO) info(project.toString(), "LWJGL applet HTML"); + + String appletDir = mkdir(project.path("$target$/applet-lwjgl/")); + + Scar.lwjglAppletHtml(appletDir, project.get("main")); + + return appletDir; + } + + /** Unzips all JARs in the "dist" directory and creates a single JAR containing those files in the "dist/onejar" directory. The + * manifest from the project's JAR is used. Putting everything into a single JAR makes it harder to see what libraries are + * being used, but makes it easier for end users to distribute the application. + *

+ * Note: Files with the same path in different JARs will be overwritten. Files in the project's JAR will never be overwritten, + * but may overwrite other files. + * @param excludeJARs The names of any JARs to exclude. */ + static public void oneJAR (Project project, String... excludeJARs) throws IOException { + if (project == null) throw new IllegalArgumentException("project cannot be null."); + + if (INFO) info(project.toString(), "One JAR"); + + String onejarDir = mkdir(project.path("$target$/onejar/")); + String distDir = project.path("$target$/dist/"); + String projectJarName; + if (project.has("version")) + projectJarName = project.format("$name$-$version$.jar"); + else + projectJarName = project.format("$name$.jar"); + + ArrayList processedJARs = new ArrayList(); + outer: + for (String jarFile : paths(distDir, "*.jar", "!" + projectJarName)) { + String jarName = fileName(jarFile); + for (String exclude : excludeJARs) + if (jarName.equals(exclude)) continue outer; + unzip(jarFile, onejarDir); + processedJARs.add(jarFile); + } + unzip(distDir + projectJarName, onejarDir); + + String onejarFile; + if (project.has("version")) + onejarFile = project.path("$target$/dist/onejar/$name$-$version$-all.jar"); + else + onejarFile = project.path("$target$/dist/onejar/$name$-all.jar"); + mkdir(parent(onejarFile)); + + if (project.has("main")) new File(onejarDir, "META-INF/MANIFEST.MF").delete(); + + Scar.jar(onejarFile, onejarDir, project.get("main"), classpath(project, true)); + } + + /** Calls {@link #build(Project)} for each dependency project in the specified project. */ + static public void buildDependencies (Project project) throws IOException { + if (project == null) throw new IllegalArgumentException("project cannot be null."); + + for (String dependency : project.getList("dependencies")) { + Project dependencyProject = project(project.path(dependency)); + + if (builtProjects.contains(dependencyProject.get("name"))) { + if (DEBUG) debug(project.toString(), "Dependency project already built: " + dependencyProject); + continue; + } + + String jarFile; + if (dependencyProject.has("version")) + jarFile = dependencyProject.path("$target$/$name$-$version$.jar"); + else + jarFile = dependencyProject.path("$target$/$name$.jar"); + + if (DEBUG) debug("Building dependency: " + dependencyProject); + if (!executeDocument(dependencyProject)) build(dependencyProject); + } + } + + /** Calls {@link #project(String)} and then {@link #build(Project)}. */ + static public void build (String path) throws IOException { + build(project(path)); + } + + /** Executes the buildDependencies, clean, compile, jar, and dist utility metshods. */ + static public void build (Project project) throws IOException { + if (project == null) throw new IllegalArgumentException("project cannot be null."); + + buildDependencies(project); + + if (INFO) info(project.toString(), "Target: " + project.path("$target$")); + + clean(project); + try { + Thread.sleep(100); + } catch (InterruptedException ignored) { + } + compile(project); + try { + Thread.sleep(100); + } catch (InterruptedException ignored) { + } + jar(project); + dist(project); + + if (paths(project.path("$target$")).filesOnly().isEmpty()) { + if (WARN) warn(project.toString(), "Empty target folder."); + delete(project.path("$target$")); + } + + builtProjects.add(project.get("name")); + } + + /** Executes Java code in the specified project's document, if any. + * @return true if code was executed. */ + static public boolean executeDocument (Project project) throws IOException { + String code = project.getDocument(); + if (code == null || code.trim().isEmpty()) return false; + HashMap parameters = new HashMap(); + parameters.put("project", project); + try { + Scar.executeCode(code, parameters, project); + } catch (RuntimeException ex) { + throw new RuntimeException("Error executing code for project: " + project, ex); + } + return true; + } + + static public void main (String[] args) throws IOException { + Scar.args = new Arguments(args); + + if (Scar.args.has("trace")) + TRACE(); + else if (Scar.args.has("debug")) + DEBUG(); + else if (Scar.args.has("info")) + INFO(); + else if (Scar.args.has("warn")) + WARN(); + else if (Scar.args.has("error")) // + ERROR(); + + Project project = project(Scar.args.get("file", ".")); + if (!executeDocument(project)) build(project); + } +} diff --git a/scar/src/com/esotericsoftware/scar/Project.java b/src/com/esotericsoftware/scar/Project.java similarity index 96% rename from scar/src/com/esotericsoftware/scar/Project.java rename to src/com/esotericsoftware/scar/Project.java index f337f2f..9542582 100644 --- a/scar/src/com/esotericsoftware/scar/Project.java +++ b/src/com/esotericsoftware/scar/Project.java @@ -1,350 +1,350 @@ - -package com.esotericsoftware.scar; - -import java.io.BufferedReader; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.io.FileReader; -import java.io.FileWriter; -import java.io.IOException; -import java.io.OutputStream; -import java.io.StringReader; -import java.net.MalformedURLException; -import java.net.URI; -import java.net.URL; -import java.net.URLClassLoader; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.jar.Attributes; -import java.util.jar.Manifest; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import javax.tools.Diagnostic; -import javax.tools.DiagnosticCollector; -import javax.tools.FileObject; -import javax.tools.ForwardingJavaFileManager; -import javax.tools.JavaCompiler; -import javax.tools.JavaFileObject; -import javax.tools.JavaFileObject.Kind; -import javax.tools.SimpleJavaFileObject; -import javax.tools.ToolProvider; - -import com.esotericsoftware.wildcard.Paths; -import com.esotericsoftware.yamlbeans.YamlException; -import com.esotericsoftware.yamlbeans.YamlReader; -import com.esotericsoftware.yamlbeans.parser.Parser.ParserException; -import com.esotericsoftware.yamlbeans.tokenizer.Tokenizer.TokenizerException; - -import static com.esotericsoftware.minlog.Log.*; -import static com.esotericsoftware.scar.Scar.*; - -/** Generic data structure that contains information needed to perform tasks. */ -public class Project { - static private Pattern formatPattern = Pattern.compile("([^\\$]*)\\$([^\\$]+)\\$([^\\$]*)"); - - String dir; - private HashMap data = new HashMap(); - private String document; - - /** Creates an empty project, without any default properties. */ - public Project () { - } - - /** Creates an empty project, without any default properties, and then loads the specified YAML files. */ - public Project (String path, String... paths) throws IOException { - if (paths == null) throw new IllegalArgumentException("paths cannot be null."); - - load(path); - for (String mergePath : paths) - replace(new Project(mergePath)); - } - - /** Clears the data in this project and replaces it with the contents of the specified YAML file. The project directory is set - * to the directory containing the YAML file. - * @param path Path to a YAML project file, or a directory containing a "project.yaml" file. */ - public void load (String path) throws IOException { - File file = new File(path); - if (!file.exists() && !path.endsWith(".yaml")) { - path += ".yaml"; - file = new File(path); - } - if (!file.exists()) throw new IllegalArgumentException("Project not found: " + file.getAbsolutePath()); - if (file.isDirectory()) { - file = new File(file, "project.yaml"); - if (file.exists()) - load(file.getPath()); - else - dir = Scar.canonical(path); - return; - } - dir = new File(Scar.canonical(path)).getParent().replace('\\', '/'); - - BufferedReader fileReader = new BufferedReader(new FileReader(path)); - try { - StringBuffer buffer = new StringBuffer(2048); - while (true) { - String line = fileReader.readLine(); - if (line == null || line.trim().equals("---")) break; - buffer.append(line); - buffer.append('\n'); - } - - YamlReader yamlReader = new YamlReader(new StringReader(buffer.toString())) { - protected Object readValue (Class type, Class elementType, Class defaultType) throws YamlException, ParserException, - TokenizerException { - Object value = super.readValue(type, elementType, defaultType); - if (value instanceof String) value = ((String)value).replaceAll("\\$dir\\$", dir); - return value; - } - }; - try { - data = yamlReader.read(HashMap.class); - yamlReader.close(); - } catch (YamlException ex) { - throw new IOException("Error reading YAML file: " + new File(path).getAbsolutePath(), ex); - } - if (data == null) data = new HashMap(); - - buffer.setLength(0); - while (true) { - String line = fileReader.readLine(); - if (line == null) break; - buffer.append(line); - buffer.append('\n'); - } - document = buffer.toString(); - } finally { - fileReader.close(); - } - } - - /** Replaces the data in this project with the contents of the specified YAML file. If the specified project has data with the - * same key as this project, the value is overwritten. Keys in this project that are not in the specified project are not - * affected. */ - public void replace (Project project) throws IOException { - if (project == null) throw new IllegalArgumentException("project cannot be null."); - data.putAll(project.data); - document = project.document; - dir = project.dir; - } - - public boolean has (Object key) { - if (key == null) throw new IllegalArgumentException("key cannot be null."); - return data.get(key) != null; - } - - public Object getObject (Object key) { - return getObject(key, null); - } - - public Object getObject (Object key, Object defaultValue) { - if (key == null) throw new IllegalArgumentException("key cannot be null."); - Object object = data.get(key); - if (object == null) return defaultValue; - return object; - } - - public String get (Object key) { - return get(key, null); - } - - public String get (Object key, String defaultValue) { - Object value = getObject(key); - if (value == null) return defaultValue; - if (!(value instanceof String)) return value.toString(); - return (String)value; - } - - public int getInt (Object key) { - return getInt(key, 0); - } - - public int getInt (Object key, int defaultValue) { - Object value = getObject(key); - if (value == null) return defaultValue; - if (value instanceof String) value = Integer.parseInt((String)value); - return (Integer)value; - } - - public float getFloat (Object key) { - return getFloat(key, 0); - } - - public float getFloat (Object key, float defaultValue) { - Object value = getObject(key); - if (value == null) return defaultValue; - if (value instanceof String) value = Float.parseFloat((String)value); - return (Float)value; - } - - public boolean getBoolean (Object key) { - return getBoolean(key, false); - } - - public boolean getBoolean (Object key, boolean defaultValue) { - Object value = getObject(key); - if (value == null) return defaultValue; - if (value instanceof String) value = Boolean.parseBoolean((String)value); - return (Boolean)value; - } - - /** Returns a list of strings under the specified key. If the key is a single value, it is placed in a list and returned. If the - * key does not exist, an empty list is returned. */ - public List getList (Object key, String... defaultValues) { - Object object = getObject(key); - if (!(object instanceof List)) { - List list = new ArrayList(); - if (object != null) list.add((String)object); - return list; - } - if (object != null) return (List)object; - if (defaultValues == null) return null; - List list = new ArrayList(); - for (Object value : defaultValues) - list.add((String)value); - return list; - } - - /** Returns a list of objects under the specified key. If the key is a single value, it is placed in a list and returned. If the - * key does not exist, an empty list is returned. */ - public List getObjectList (Object key, Object... defaultValues) { - Object object = getObject(key); - if (!(object instanceof List)) { - List list = new ArrayList(); - if (object != null) list.add(object); - return list; - } - if (object != null) return (List)object; - if (defaultValues == null) return null; - List list = new ArrayList(); - for (Object value : defaultValues) - list.add(value); - return list; - } - - public Map getMap (Object key, String... defaultValues) { - Map map = (Map)getObject(key); - if (map == null) { - if (defaultValues == null) return null; - map = new HashMap(); - for (int i = 0, n = defaultValues.length; i < n;) { - Object defaultKey = defaultValues[i++]; - Object defaultValue = i < n ? defaultValues[i++] : null; - map.put((String)key, (String)defaultValue); - } - } - return map; - } - - public Map getObjectMap (Object key, Object... defaultValues) { - Map map = (Map)getObject(key); - if (map == null) { - if (defaultValues == null) return null; - map = new HashMap(); - for (int i = 0, n = defaultValues.length; i < n;) { - Object defaultKey = defaultValues[i++]; - Object defaultValue = i < n ? defaultValues[i++] : null; - map.put(defaultKey, defaultValue); - } - } - return map; - } - - /** Uses the strings under the specified key to {@link Paths#glob(String, String...) glob} paths. */ - public Paths getPaths (String key) { - Paths paths = new Paths(); - Object object = data.get(key); - if (object instanceof List) { - for (Object dirPattern : (List)object) - paths.glob(path((String)dirPattern)); - } else if (object instanceof String) { - paths.glob(path((String)object)); - } - return paths; - } - - /** Returns the specified path if it is an absolute path, otherwise returns the path relative to this project's directory. */ - public String path (String path) { - path = format(path); - if (dir == null) return path; - int pipeIndex = path.indexOf('|'); - if (pipeIndex > -1) { - // Handle wildcard search patterns. - return path(path.substring(0, pipeIndex)) + path.substring(pipeIndex); - } - if (new File(path).isAbsolute()) return path; - return dir + "/" + path; - } - - public void set (Object key, Object object) { - if (key == null) throw new IllegalArgumentException("key cannot be null."); - data.put(key, object); - } - - public void setDirectory (String dir) { - this.dir = Scar.canonical(dir); - } - - public String getDirectory () { - return dir; - } - - public String getDocument () { - return document; - } - - public void setDocument (String document) { - this.document = document; - } - - public void remove (Object key) { - data.remove(key); - } - - /** Removes an item from a list or map. If the data under the specified key is a list, the entry equal to the specified value is - * removed. If the data under the specified key is a map, the entry with the key specified by value is removed. */ - public void remove (Object key, Object value) { - Object object = data.get(key); - if (object instanceof Map) - ((Map)object).remove(object); - else if (object instanceof List) - ((List)object).remove(object); - else - data.remove(key); - } - - /** Replaces property names surrounded by curly braces with the value from this project. */ - public String format (String text) { - Matcher matcher = formatPattern.matcher(text); - StringBuilder buffer = new StringBuilder(128); - while (matcher.find()) { - buffer.append(matcher.group(1)); - String name = matcher.group(2); - Object value = data.get(name); - if (value instanceof String) - buffer.append(format((String)value)); - else if (value != null) - buffer.append(value); - else - buffer.append(name); - buffer.append(matcher.group(3)); - } - if (buffer.length() == 0) return text; - return buffer.toString(); - } - - public void clear () { - data.clear(); - } - - public String toString () { - if (has("name")) return get("name"); - return data.toString(); - } -} + +package com.esotericsoftware.scar; + +import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.io.OutputStream; +import java.io.StringReader; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.jar.Attributes; +import java.util.jar.Manifest; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.tools.Diagnostic; +import javax.tools.DiagnosticCollector; +import javax.tools.FileObject; +import javax.tools.ForwardingJavaFileManager; +import javax.tools.JavaCompiler; +import javax.tools.JavaFileObject; +import javax.tools.JavaFileObject.Kind; +import javax.tools.SimpleJavaFileObject; +import javax.tools.ToolProvider; + +import com.esotericsoftware.wildcard.Paths; +import com.esotericsoftware.yamlbeans.YamlException; +import com.esotericsoftware.yamlbeans.YamlReader; +import com.esotericsoftware.yamlbeans.parser.Parser.ParserException; +import com.esotericsoftware.yamlbeans.tokenizer.Tokenizer.TokenizerException; + +import static com.esotericsoftware.minlog.Log.*; +import static com.esotericsoftware.scar.Scar.*; + +/** Generic data structure that contains information needed to perform tasks. */ +public class Project { + static private Pattern formatPattern = Pattern.compile("([^\\$]*)\\$([^\\$]+)\\$([^\\$]*)"); + + String dir; + private HashMap data = new HashMap(); + private String document; + + /** Creates an empty project, without any default properties. */ + public Project () { + } + + /** Creates an empty project, without any default properties, and then loads the specified YAML files. */ + public Project (String path, String... paths) throws IOException { + if (paths == null) throw new IllegalArgumentException("paths cannot be null."); + + load(path); + for (String mergePath : paths) + replace(new Project(mergePath)); + } + + /** Clears the data in this project and replaces it with the contents of the specified YAML file. The project directory is set + * to the directory containing the YAML file. + * @param path Path to a YAML project file, or a directory containing a "project.yaml" file. */ + public void load (String path) throws IOException { + File file = new File(path); + if (!file.exists() && !path.endsWith(".yaml")) { + path += ".yaml"; + file = new File(path); + } + if (!file.exists()) throw new IllegalArgumentException("Project not found: " + file.getAbsolutePath()); + if (file.isDirectory()) { + file = new File(file, "project.yaml"); + if (file.exists()) + load(file.getPath()); + else + dir = Scar.canonical(path); + return; + } + dir = new File(Scar.canonical(path)).getParent().replace('\\', '/'); + + BufferedReader fileReader = new BufferedReader(new FileReader(path)); + try { + StringBuffer buffer = new StringBuffer(2048); + while (true) { + String line = fileReader.readLine(); + if (line == null || line.trim().equals("---")) break; + buffer.append(line); + buffer.append('\n'); + } + + YamlReader yamlReader = new YamlReader(new StringReader(buffer.toString())) { + protected Object readValue (Class type, Class elementType, Class defaultType) throws YamlException, ParserException, + TokenizerException { + Object value = super.readValue(type, elementType, defaultType); + if (value instanceof String) value = ((String)value).replaceAll("\\$dir\\$", dir); + return value; + } + }; + try { + data = yamlReader.read(HashMap.class); + yamlReader.close(); + } catch (YamlException ex) { + throw new IOException("Error reading YAML file: " + new File(path).getAbsolutePath(), ex); + } + if (data == null) data = new HashMap(); + + buffer.setLength(0); + while (true) { + String line = fileReader.readLine(); + if (line == null) break; + buffer.append(line); + buffer.append('\n'); + } + document = buffer.toString(); + } finally { + fileReader.close(); + } + } + + /** Replaces the data in this project with the contents of the specified YAML file. If the specified project has data with the + * same key as this project, the value is overwritten. Keys in this project that are not in the specified project are not + * affected. */ + public void replace (Project project) throws IOException { + if (project == null) throw new IllegalArgumentException("project cannot be null."); + data.putAll(project.data); + document = project.document; + dir = project.dir; + } + + public boolean has (Object key) { + if (key == null) throw new IllegalArgumentException("key cannot be null."); + return data.get(key) != null; + } + + public Object getObject (Object key) { + return getObject(key, null); + } + + public Object getObject (Object key, Object defaultValue) { + if (key == null) throw new IllegalArgumentException("key cannot be null."); + Object object = data.get(key); + if (object == null) return defaultValue; + return object; + } + + public String get (Object key) { + return get(key, null); + } + + public String get (Object key, String defaultValue) { + Object value = getObject(key); + if (value == null) return defaultValue; + if (!(value instanceof String)) return value.toString(); + return (String)value; + } + + public int getInt (Object key) { + return getInt(key, 0); + } + + public int getInt (Object key, int defaultValue) { + Object value = getObject(key); + if (value == null) return defaultValue; + if (value instanceof String) value = Integer.parseInt((String)value); + return (Integer)value; + } + + public float getFloat (Object key) { + return getFloat(key, 0); + } + + public float getFloat (Object key, float defaultValue) { + Object value = getObject(key); + if (value == null) return defaultValue; + if (value instanceof String) value = Float.parseFloat((String)value); + return (Float)value; + } + + public boolean getBoolean (Object key) { + return getBoolean(key, false); + } + + public boolean getBoolean (Object key, boolean defaultValue) { + Object value = getObject(key); + if (value == null) return defaultValue; + if (value instanceof String) value = Boolean.parseBoolean((String)value); + return (Boolean)value; + } + + /** Returns a list of strings under the specified key. If the key is a single value, it is placed in a list and returned. If the + * key does not exist, an empty list is returned. */ + public List getList (Object key, String... defaultValues) { + Object object = getObject(key); + if (!(object instanceof List)) { + List list = new ArrayList(); + if (object != null) list.add((String)object); + return list; + } + if (object != null) return (List)object; + if (defaultValues == null) return null; + List list = new ArrayList(); + for (Object value : defaultValues) + list.add((String)value); + return list; + } + + /** Returns a list of objects under the specified key. If the key is a single value, it is placed in a list and returned. If the + * key does not exist, an empty list is returned. */ + public List getObjectList (Object key, Object... defaultValues) { + Object object = getObject(key); + if (!(object instanceof List)) { + List list = new ArrayList(); + if (object != null) list.add(object); + return list; + } + if (object != null) return (List)object; + if (defaultValues == null) return null; + List list = new ArrayList(); + for (Object value : defaultValues) + list.add(value); + return list; + } + + public Map getMap (Object key, String... defaultValues) { + Map map = (Map)getObject(key); + if (map == null) { + if (defaultValues == null) return null; + map = new HashMap(); + for (int i = 0, n = defaultValues.length; i < n;) { + Object defaultKey = defaultValues[i++]; + Object defaultValue = i < n ? defaultValues[i++] : null; + map.put((String)key, (String)defaultValue); + } + } + return map; + } + + public Map getObjectMap (Object key, Object... defaultValues) { + Map map = (Map)getObject(key); + if (map == null) { + if (defaultValues == null) return null; + map = new HashMap(); + for (int i = 0, n = defaultValues.length; i < n;) { + Object defaultKey = defaultValues[i++]; + Object defaultValue = i < n ? defaultValues[i++] : null; + map.put(defaultKey, defaultValue); + } + } + return map; + } + + /** Uses the strings under the specified key to {@link Paths#glob(String, String...) glob} paths. */ + public Paths getPaths (String key) { + Paths paths = new Paths(); + Object object = data.get(key); + if (object instanceof List) { + for (Object dirPattern : (List)object) + paths.glob(path((String)dirPattern)); + } else if (object instanceof String) { + paths.glob(path((String)object)); + } + return paths; + } + + /** Returns the specified path if it is an absolute path, otherwise returns the path relative to this project's directory. */ + public String path (String path) { + path = format(path); + if (dir == null) return path; + int pipeIndex = path.indexOf('|'); + if (pipeIndex > -1) { + // Handle wildcard search patterns. + return path(path.substring(0, pipeIndex)) + path.substring(pipeIndex); + } + if (new File(path).isAbsolute()) return path; + return dir + "/" + path; + } + + public void set (Object key, Object object) { + if (key == null) throw new IllegalArgumentException("key cannot be null."); + data.put(key, object); + } + + public void setDirectory (String dir) { + this.dir = Scar.canonical(dir); + } + + public String getDirectory () { + return dir; + } + + public String getDocument () { + return document; + } + + public void setDocument (String document) { + this.document = document; + } + + public void remove (Object key) { + data.remove(key); + } + + /** Removes an item from a list or map. If the data under the specified key is a list, the entry equal to the specified value is + * removed. If the data under the specified key is a map, the entry with the key specified by value is removed. */ + public void remove (Object key, Object value) { + Object object = data.get(key); + if (object instanceof Map) + ((Map)object).remove(object); + else if (object instanceof List) + ((List)object).remove(object); + else + data.remove(key); + } + + /** Replaces property names surrounded by curly braces with the value from this project. */ + public String format (String text) { + Matcher matcher = formatPattern.matcher(text); + StringBuilder buffer = new StringBuilder(128); + while (matcher.find()) { + buffer.append(matcher.group(1)); + String name = matcher.group(2); + Object value = data.get(name); + if (value instanceof String) + buffer.append(format((String)value)); + else if (value != null) + buffer.append(value); + else + buffer.append(name); + buffer.append(matcher.group(3)); + } + if (buffer.length() == 0) return text; + return buffer.toString(); + } + + public void clear () { + data.clear(); + } + + public String toString () { + if (has("name")) return get("name"); + return data.toString(); + } +} diff --git a/scar/src/com/esotericsoftware/scar/Scar.java b/src/com/esotericsoftware/scar/Scar.java similarity index 97% rename from scar/src/com/esotericsoftware/scar/Scar.java rename to src/com/esotericsoftware/scar/Scar.java index 767ad8b..38836d5 100644 --- a/scar/src/com/esotericsoftware/scar/Scar.java +++ b/src/com/esotericsoftware/scar/Scar.java @@ -1,1450 +1,1450 @@ - -package com.esotericsoftware.scar; - -import static com.esotericsoftware.minlog.Log.*; - -import SevenZip.LzmaAlone; - -import com.esotericsoftware.wildcard.Paths; - -import java.io.BufferedInputStream; -import java.io.BufferedOutputStream; -import java.io.BufferedReader; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.FileReader; -import java.io.FileWriter; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.io.StringReader; -import java.io.Writer; -import java.net.InetAddress; -import java.net.MalformedURLException; -import java.net.URI; -import java.net.URL; -import java.net.URLClassLoader; -import java.nio.channels.FileChannel; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map.Entry; -import java.util.jar.Attributes; -import java.util.jar.JarEntry; -import java.util.jar.JarInputStream; -import java.util.jar.JarOutputStream; -import java.util.jar.Manifest; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.util.zip.Deflater; -import java.util.zip.GZIPInputStream; -import java.util.zip.GZIPOutputStream; -import java.util.zip.ZipEntry; -import java.util.zip.ZipInputStream; - -import javax.tools.Diagnostic; -import javax.tools.DiagnosticCollector; -import javax.tools.FileObject; -import javax.tools.ForwardingJavaFileManager; -import javax.tools.JavaCompiler; -import javax.tools.JavaFileObject; -import javax.tools.JavaFileObject.Kind; -import javax.tools.SimpleJavaFileObject; -import javax.tools.ToolProvider; - -import org.apache.commons.net.ftp.FTPClient; - -import com.jcraft.jsch.ChannelExec; -import com.jcraft.jsch.ChannelSftp; -import com.jcraft.jsch.JSch; -import com.jcraft.jsch.Session; -import com.jcraft.jsch.SftpProgressMonitor; - -// BOZO - Add javadocs method. - -/** Provides utility methods for common Java build tasks. */ -public class Scar { - /** The Scar installation directory. The value comes from the SCAR_HOME environment variable, if it exists. Alternatively, the - * "scar.home" System property can be defined. */ - static public final String SCAR_HOME; - static { - if (System.getProperty("scar.home") != null) - SCAR_HOME = System.getProperty("scar.home"); - else - SCAR_HOME = System.getenv("SCAR_HOME"); - } - - /** The command line arguments Scar was started with. Empty if Scar was started with no arguments or Scar was not started from - * the command line. */ - static public Arguments args = new Arguments(); - - /** The Java installation directory. */ - static public final String JAVA_HOME = System.getProperty("java.home"); - - /** True if running on a Mac OS. */ - static public final boolean isMac = System.getProperty("os.name").toLowerCase().contains("mac os x"); - - /** True if running on a Windows OS. */ - static public final boolean isWindows = System.getProperty("os.name").toLowerCase().contains("windows"); - - static private final String manifestFileName = "META-INF" + File.separator + "MANIFEST.MF"; - - static { - Paths.setDefaultGlobExcludes("**/.svn/**"); - } - - /** Returns the full path for the specified file name in the current working directory, the {@link #SCAR_HOME}, and the bin - * directory of {@link #JAVA_HOME}. */ - static public String resolvePath (String fileName) { - if (fileName == null) return null; - - String foundFile; - while (true) { - foundFile = canonical(fileName); - if (fileExists(foundFile)) break; - - foundFile = new File(SCAR_HOME, fileName).getPath(); - if (fileExists(foundFile)) break; - - foundFile = new File(JAVA_HOME, "bin/" + fileName).getPath(); - if (fileExists(foundFile)) break; - - foundFile = fileName; - break; - } - if (TRACE) trace("scar", "Path \"" + fileName + "\" resolved to: " + foundFile); - return foundFile; - } - - static public void jar (String outputFile, String inputDir) throws IOException { - jar(outputFile, new Paths(inputDir), null, null); - } - - static public void jar (String outputFile, String inputDir, String mainClass, Paths classpath) throws IOException { - jar(outputFile, new Paths(inputDir), mainClass, classpath); - } - - static public void jar (String outputFile, Paths inputPaths) throws IOException { - jar(outputFile, inputPaths, null, null); - } - - // BOZO - javadoc - static public void jar (String outputFile, Paths inputPaths, String mainClass, Paths classpath) throws IOException { - if (outputFile == null) throw new IllegalArgumentException("jarFile cannot be null."); - if (inputPaths == null) throw new IllegalArgumentException("inputPaths cannot be null."); - - inputPaths = inputPaths.filesOnly(); - if (inputPaths.isEmpty()) { - if (WARN) warn("scar", "No files to JAR."); - return; - } - - List fullPaths = inputPaths.getPaths(); - List relativePaths = inputPaths.getRelativePaths(); - int manifestIndex = relativePaths.indexOf(manifestFileName); - if (manifestIndex > 0) { - // Ensure MANIFEST.MF is first. - relativePaths.remove(manifestIndex); - relativePaths.add(0, manifestFileName); - String manifestFullPath = fullPaths.get(manifestIndex); - fullPaths.remove(manifestIndex); - fullPaths.add(0, manifestFullPath); - } else if (mainClass != null) { - if (DEBUG) debug("scar", "Generating JAR manifest."); - String manifestFile = tempFile("manifest"); - relativePaths.add(0, manifestFileName); - fullPaths.add(0, manifestFile); - - Manifest manifest = new Manifest(); - Attributes attributes = manifest.getMainAttributes(); - attributes.putValue(Attributes.Name.MANIFEST_VERSION.toString(), "1.0"); - if (DEBUG) debug("scar", "Main class: " + mainClass); - attributes.putValue(Attributes.Name.MAIN_CLASS.toString(), mainClass); - StringBuilder buffer = new StringBuilder(512); - buffer.append(fileName(outputFile)); - buffer.append(" ."); - for (String name : classpath.getRelativePaths()) { - buffer.append(' '); - buffer.append(name); - } - attributes.putValue(Attributes.Name.CLASS_PATH.toString(), buffer.toString()); - FileOutputStream output = new FileOutputStream(manifestFile); - try { - manifest.write(output); - } finally { - try { - output.close(); - } catch (Exception ignored) { - } - } - } - - if (DEBUG) debug("scar", "Creating JAR (" + inputPaths.count() + " entries): " + outputFile); - - mkdir(new File(outputFile).getParent()); - JarOutputStream output = new JarOutputStream(new BufferedOutputStream(new FileOutputStream(outputFile))); - output.setLevel(Deflater.BEST_COMPRESSION); - try { - for (int i = 0, n = fullPaths.size(); i < n; i++) { - JarEntry jarEntry = new JarEntry(relativePaths.get(i).replace('\\', '/')); - jarEntry.setTime(1370273339); - output.putNextEntry(jarEntry); - FileInputStream input = new FileInputStream(fullPaths.get(i)); - try { - byte[] buffer = new byte[4096]; - while (true) { - int length = input.read(buffer); - if (length == -1) break; - output.write(buffer, 0, length); - } - } finally { - try { - input.close(); - } catch (Exception ignored) { - } - } - } - } finally { - try { - output.close(); - } catch (Exception ignored) { - } - } - } - - static public void oneJAR (String inputDir, String outputFile, String mainClass, Paths classpath) throws IOException { - oneJAR(paths(inputDir, "*.jar"), outputFile, mainClass, classpath); - } - - static public void oneJAR (Paths jars, String outputFile, String mainClass, Paths classpath) throws IOException { - if (jars == null) throw new IllegalArgumentException("jars cannot be null."); - - String tempDir = tempDirectory("oneJAR"); - - ArrayList processedJARs = new ArrayList(); - for (String jarFile : jars) { - unzip(jarFile, tempDir); - processedJARs.add(jarFile); - } - - if (mainClass != null) new File(tempDir, manifestFileName).delete(); - - mkdir(parent(outputFile)); - jar(outputFile, tempDir, mainClass, classpath); - delete(tempDir); - } - - /** Removes any code signatures on the specified JAR. Removes any signature files in the META-INF directory and removes any - * signature entries from the JAR's manifest. - * @return The path to the JAR file. */ - static public String unsign (String jarFile) throws IOException { - if (jarFile == null) throw new IllegalArgumentException("jarFile cannot be null."); - - if (DEBUG) debug("scar", "Removing signature from JAR: " + jarFile); - - File tempFile = File.createTempFile("scar", "removejarsig"); - JarOutputStream jarOutput = null; - JarInputStream jarInput = null; - try { - jarOutput = new JarOutputStream(new FileOutputStream(tempFile)); - jarInput = new JarInputStream(new FileInputStream(jarFile)); - Manifest manifest = jarInput.getManifest(); - if (manifest != null) { - // Remove manifest file entries. - manifest.getEntries().clear(); - jarOutput.putNextEntry(new JarEntry(manifestFileName)); - manifest.write(jarOutput); - } - byte[] buffer = new byte[4096]; - while (true) { - JarEntry entry = jarInput.getNextJarEntry(); - if (entry == null) break; - String name = entry.getName(); - // Skip signature files. - if (name.startsWith("META-INF/") && (name.endsWith(".SF") || name.endsWith(".DSA") || name.endsWith(".RSA"))) - continue; - jarOutput.putNextEntry(new JarEntry(name)); - while (true) { - int length = jarInput.read(buffer); - if (length == -1) break; - jarOutput.write(buffer, 0, length); - } - } - jarInput.close(); - jarOutput.close(); - copyFile(tempFile.getAbsolutePath(), jarFile); - } catch (IOException ex) { - throw new IOException("Error unsigning JAR file: " + jarFile, ex); - } finally { - try { - if (jarInput != null) jarInput.close(); - } catch (Exception ignored) { - } - try { - if (jarOutput != null) jarOutput.close(); - } catch (Exception ignored) { - } - tempFile.delete(); - } - return jarFile; - } - - /** Creates a new keystore for signing JARs. If the keystore file already exists, no action will be taken. - * @return The path to the keystore file. */ - static public String keystore (String keystoreFile, String alias, String password, String company, String title) - throws IOException { - if (keystoreFile == null) throw new IllegalArgumentException("keystoreFile cannot be null."); - if (fileExists(keystoreFile)) return keystoreFile; - if (alias == null) throw new IllegalArgumentException("alias cannot be null."); - if (password == null) throw new IllegalArgumentException("password cannot be null."); - if (password.length() < 6) throw new IllegalArgumentException("password must be 6 or more characters."); - if (company == null) throw new IllegalArgumentException("company cannot be null."); - if (title == null) throw new IllegalArgumentException("title cannot be null."); - - if (DEBUG) - debug("scar", "Creating keystore (" + alias + ":" + password + ", " + company + ", " + title + "): " + keystoreFile); - - File file = new File(keystoreFile); - file.delete(); - Process process = Runtime.getRuntime().exec( - new String[] {resolvePath("keytool"), "-genkey", "-keystore", keystoreFile, "-alias", alias}); - OutputStreamWriter writer = new OutputStreamWriter(process.getOutputStream()); - writer.write(password + "\n"); // Enter keystore password: - writer.write(password + "\n"); // Re-enter new password: - writer.write(company + "\n"); // What is your first and last name? - writer.write(title + "\n"); // What is the name of your organizational unit? - writer.write(title + "\n"); // What is the name of your organization? - writer.write("\n"); // What is the name of your City or Locality? [Unknown] - writer.write("\n"); // What is the name of your State or Province? [Unknown] - writer.write("\n"); // What is the two-letter country code for this unit? [Unknown] - writer.write("yes\n"); // Correct? - writer.write("\n"); // Return if same alias key password as keystore. - writer.flush(); - process.getOutputStream().close(); - process.getInputStream().close(); - process.getErrorStream().close(); - try { - process.waitFor(); - } catch (InterruptedException ignored) { - } - if (!file.exists()) throw new RuntimeException("Error creating keystore."); - return keystoreFile; - } - - /** Signs the specified JAR. - * @return The path to the JAR. */ - static public String sign (String jarFile, String keystoreFile, String alias, String password) throws IOException { - if (jarFile == null) throw new IllegalArgumentException("jarFile cannot be null."); - if (keystoreFile == null) throw new IllegalArgumentException("keystoreFile cannot be null."); - if (alias == null) throw new IllegalArgumentException("alias cannot be null."); - if (password == null) throw new IllegalArgumentException("password cannot be null."); - if (password.length() < 6) throw new IllegalArgumentException("password must be 6 or more characters."); - - if (DEBUG) debug("scar", "Signing JAR (" + keystoreFile + ", " + alias + ":" + password + "): " + jarFile); - - shell("jarsigner", "-keystore", keystoreFile, "-storepass", password, "-keypass", password, jarFile, alias); - return jarFile; - } - - /** Encodes the specified file with pack200. The resulting filename is the filename plus ".pack". The file is deleted after - * encoding. - * @return The path to the encoded file. */ - static public String pack200 (String jarFile) throws IOException { - String packedFile = pack200(jarFile, jarFile + ".pack"); - delete(jarFile); - return packedFile; - } - - /** Encodes the specified file with pack200. - * @return The path to the encoded file. */ - static public String pack200 (String jarFile, String packedFile) throws IOException { - if (jarFile == null) throw new IllegalArgumentException("jarFile cannot be null."); - if (packedFile == null) throw new IllegalArgumentException("packedFile cannot be null."); - - if (DEBUG) debug("scar", "Packing JAR: " + jarFile + " -> " + packedFile); - - shell("pack200", "--no-gzip", "--segment-limit=-1", "--no-keep-file-order", "--effort=7", "--modification-time=latest", - packedFile, jarFile); - return packedFile; - } - - /** Decodes the specified file with pack200. The filename must end in ".pack" and the resulting filename has this stripped. The - * encoded file is deleted after decoding. - * @return The path to the decoded file. */ - static public String unpack200 (String packedFile) throws IOException { - if (packedFile == null) throw new IllegalArgumentException("packedFile cannot be null."); - if (!packedFile.endsWith(".pack")) throw new IllegalArgumentException("packedFile must end with .pack: " + packedFile); - - String jarFile = unpack200(packedFile, substring(packedFile, 0, -5)); - delete(packedFile); - return jarFile; - } - - /** Decodes the specified file with pack200. - * @return The path to the decoded file. */ - static public String unpack200 (String packedFile, String jarFile) throws IOException { - if (packedFile == null) throw new IllegalArgumentException("packedFile cannot be null."); - if (jarFile == null) throw new IllegalArgumentException("jarFile cannot be null."); - - if (DEBUG) debug("scar", "Unpacking JAR: " + packedFile + " -> " + jarFile); - - shell("unpack200", packedFile, jarFile); - return jarFile; - } - - /** Encodes the specified file with GZIP. The resulting filename is the filename plus ".gz". The file is deleted after encoding. - * @return The path to the encoded file. */ - static public String gzip (String file) throws IOException { - String gzipFile = gzip(file, file + ".gz"); - delete(file); - return gzipFile; - } - - /** Encodes the specified file with GZIP. - * @return The path to the encoded file. */ - static public String gzip (String file, String gzipFile) throws IOException { - if (file == null) throw new IllegalArgumentException("file cannot be null."); - if (gzipFile == null) throw new IllegalArgumentException("gzipFile cannot be null."); - - if (DEBUG) debug("scar", "GZIP encoding: " + file + " -> " + gzipFile); - - InputStream input = new FileInputStream(file); - try { - copyStream(input, new GZIPOutputStream(new FileOutputStream(gzipFile))); - } finally { - try { - input.close(); - } catch (Exception ignored) { - } - } - return gzipFile; - } - - /** Decodes the specified GZIP file. The filename must end in ".gz" and the resulting filename has this stripped. The encoded - * file is deleted after decoding. - * @return The path to the decoded file. */ - static public String ungzip (String gzipFile) throws IOException { - if (gzipFile == null) throw new IllegalArgumentException("gzipFile cannot be null."); - if (!gzipFile.endsWith(".gz")) throw new IllegalArgumentException("gzipFile must end with .gz: " + gzipFile); - - String file = ungzip(gzipFile, substring(gzipFile, 0, -3)); - delete(gzipFile); - return file; - } - - /** Decodes the specified GZIP file. - * @return The path to the decoded file. */ - static public String ungzip (String gzipFile, String file) throws IOException { - if (gzipFile == null) throw new IllegalArgumentException("gzipFile cannot be null."); - if (file == null) throw new IllegalArgumentException("file cannot be null."); - - if (DEBUG) debug("scar", "GZIP decoding: " + gzipFile + " -> " + file); - - InputStream input = new GZIPInputStream(new FileInputStream(gzipFile)); - try { - copyStream(input, new FileOutputStream(file)); - } finally { - try { - input.close(); - } catch (Exception ignored) { - } - } - return file; - } - - /** Encodes the specified files with ZIP. - * @return The path to the encoded file. */ - static public String zip (Paths paths, String zipFile) throws IOException { - if (paths == null) throw new IllegalArgumentException("paths cannot be null."); - if (zipFile == null) throw new IllegalArgumentException("zipFile cannot be null."); - - if (DEBUG) debug("scar", "Creating ZIP (" + paths.count() + " entries): " + zipFile); - - paths.zip(zipFile); - return zipFile; - } - - /** Decodes the specified ZIP file. - * @return The path to the output directory. */ - static public String unzip (String zipFile, String outputDir) throws IOException { - if (zipFile == null) throw new IllegalArgumentException("zipFile cannot be null."); - if (outputDir == null) throw new IllegalArgumentException("outputDir cannot be null."); - - if (DEBUG) debug("scar", "ZIP decoding: " + zipFile + " -> " + outputDir); - - ZipInputStream input = new ZipInputStream(new FileInputStream(zipFile)); - try { - byte[] buffer = new byte[4096]; - while (true) { - ZipEntry entry = input.getNextEntry(); - if (entry == null) break; - File file = new File(outputDir, entry.getName()); - if (entry.isDirectory()) { - mkdir(file.getPath()); - continue; - } - mkdir(file.getParent()); - FileOutputStream output = new FileOutputStream(file); - try { - while (true) { - int length = input.read(buffer); - if (length == -1) break; - output.write(buffer, 0, length); - } - } finally { - try { - output.close(); - } catch (Exception ignored) { - } - } - } - } finally { - try { - input.close(); - } catch (Exception ignored) { - } - } - return outputDir; - } - - /** Encodes the specified file with LZMA. The resulting filename is the filename plus ".lzma". The file is deleted after - * encoding. - * @return The path to the encoded file. */ - static public String lzma (String file) throws IOException { - String lzmaFile = lzma(file, file + ".lzma"); - delete(file); - return lzmaFile; - } - - /** Encodes the specified file with LZMA. - * @return The path to the encoded file. */ - static public String lzma (String file, String lzmaFile) throws IOException { - if (file == null) throw new IllegalArgumentException("file cannot be null."); - if (lzmaFile == null) throw new IllegalArgumentException("lzmaFile cannot be null."); - - if (DEBUG) debug("scar", "LZMA encoding: " + file + " -> " + lzmaFile); - - try { - LzmaAlone.main(new String[] {"e", file, lzmaFile}); - } catch (Exception ex) { - throw new IOException("Error lzma compressing file: " + file, ex); - } - return lzmaFile; - } - - /** Decodes the specified LZMA file. The filename must end in ".lzma" and the resulting filename has this stripped. The encoded - * file is deleted after decoding. - * @return The path to the decoded file. */ - static public String unlzma (String lzmaFile) throws IOException { - if (lzmaFile == null) throw new IllegalArgumentException("lzmaFile cannot be null."); - if (!lzmaFile.endsWith(".lzma")) throw new IllegalArgumentException("lzmaFile must end with .lzma: " + lzmaFile); - - String file = unlzma(lzmaFile, substring(lzmaFile, 0, -5)); - delete(lzmaFile); - return file; - } - - /** Decodes the specified LZMA file. - * @return The path to the decoded file. */ - static public String unlzma (String lzmaFile, String file) throws IOException { - if (lzmaFile == null) throw new IllegalArgumentException("lzmaFile cannot be null."); - if (file == null) throw new IllegalArgumentException("file cannot be null."); - - if (DEBUG) debug("scar", "LZMA decoding: " + lzmaFile + " -> " + file); - - try { - LzmaAlone.main(new String[] {"d", lzmaFile, file}); - } catch (Exception ex) { - throw new IOException("Error lzma decompressing file: " + file, ex); - } - return file; - } - - static public String tempFile (String prefix) throws IOException { - return File.createTempFile(prefix, null).getAbsolutePath(); - } - - static public String tempDirectory (String prefix) throws IOException { - File file = File.createTempFile(prefix, null); - if (!file.delete()) throw new IOException("Unable to delete temp file: " + file); - if (!file.mkdir()) throw new IOException("Unable to create temp directory: " + file); - return file.getAbsolutePath(); - } - - /** Splits the specified command at spaces that are not surrounded by quotes and passes the result to {@link #shell(String...)}. */ - static public void shell (String command) throws IOException { - List matchList = new ArrayList(); - Pattern regex = Pattern.compile("[^\\s\"']+|\"([^\"]*)\"|'([^']*)'"); - Matcher regexMatcher = regex.matcher(command); - while (regexMatcher.find()) { - if (regexMatcher.group(1) != null) - matchList.add(regexMatcher.group(1)); - else if (regexMatcher.group(2) != null) - matchList.add(regexMatcher.group(2)); - else - matchList.add(regexMatcher.group()); - } - shell(matchList.toArray(new String[matchList.size()])); - } - - /** Executes the specified shell command. {@link #resolvePath(String)} is used to locate the file to execute. If not found, on - * Windows the same filename with an "exe" extension is also tried. */ - static public void shell (String... command) throws IOException { - if (command == null) throw new IllegalArgumentException("command cannot be null."); - if (command.length == 0) throw new IllegalArgumentException("command cannot be empty."); - - String originalCommand = command[0]; - command[0] = resolvePath(command[0]); - if (!fileExists(command[0]) && isWindows) { - command[0] = resolvePath(command[0] + ".exe"); - if (!fileExists(command[0])) command[0] = originalCommand; - } - - if (TRACE) { - StringBuilder buffer = new StringBuilder(256); - for (String text : command) { - buffer.append(text); - buffer.append(' '); - } - trace("scar", "Executing command: " + buffer); - } - - final Process process = new ProcessBuilder(command).redirectErrorStream(true).start(); - new Thread("shell") { - public void run () { - BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); - try { - while (true) { - String line = reader.readLine(); - if (line == null) break; - System.out.println(line); - } - reader.close(); - } catch (Exception ex) { - ex.printStackTrace(); - } - } - }.start(); - - try { - process.waitFor(); - } catch (InterruptedException ignored) { - } - if (process.exitValue() != 0) { - StringBuilder buffer = new StringBuilder(256); - for (String text : command) { - buffer.append(text); - buffer.append(' '); - } - throw new RuntimeException("Error executing command: " + buffer); - } - } - - /** Reads to the end of the input stream and writes the bytes to the output stream. */ - static public void copyStream (InputStream input, OutputStream output) throws IOException { - if (input == null) throw new IllegalArgumentException("input cannot be null."); - if (output == null) throw new IllegalArgumentException("output cannot be null."); - - try { - byte[] buffer = new byte[4096]; - while (true) { - int length = input.read(buffer); - if (length == -1) break; - output.write(buffer, 0, length); - } - } finally { - try { - output.close(); - } catch (Exception ignored) { - } - try { - input.close(); - } catch (Exception ignored) { - } - } - } - - /** Copies a file, overwriting any existing file at the destination. */ - static public String copyFile (String in, String out) throws IOException { - if (in == null) throw new IllegalArgumentException("in cannot be null."); - if (out == null) throw new IllegalArgumentException("out cannot be null."); - - if (TRACE) trace("scar", "Copying file: " + in + " -> " + out); - - FileChannel sourceChannel = null; - FileChannel destinationChannel = null; - try { - sourceChannel = new FileInputStream(in).getChannel(); - destinationChannel = new FileOutputStream(out).getChannel(); - sourceChannel.transferTo(0, sourceChannel.size(), destinationChannel); - } finally { - try { - if (sourceChannel != null) sourceChannel.close(); - } catch (Exception ignored) { - } - try { - if (destinationChannel != null) destinationChannel.close(); - } catch (Exception ignored) { - } - } - return out; - } - - /** Moves a file, overwriting any existing file at the destination. */ - static public String moveFile (String in, String out) throws IOException { - if (in == null) throw new IllegalArgumentException("in cannot be null."); - if (out == null) throw new IllegalArgumentException("out cannot be null."); - - copyFile(in, out); - delete(in); - return out; - } - - static public String readFile (String file) throws IOException { - return readFile(file, null); - } - - static public String readFile (String fileName, String charset) throws IOException { - if (fileName == null) throw new IllegalArgumentException("file cannot be null."); - File file = new File(fileName); - int fileLength = (int)file.length(); - if (fileLength == 0) fileLength = 512; - StringBuilder output = new StringBuilder(fileLength); - InputStreamReader reader = null; - try { - if (charset == null) - reader = new InputStreamReader(new FileInputStream(file)); - else - reader = new InputStreamReader(new FileInputStream(file), charset); - char[] buffer = new char[256]; - while (true) { - int length = reader.read(buffer); - if (length == -1) break; - output.append(buffer, 0, length); - } - } catch (IOException ex) { - throw new RuntimeException("Error reading layout file: " + fileName, ex); - } finally { - try { - if (reader != null) reader.close(); - } catch (IOException ignored) { - } - } - return output.toString(); - } - - static public void writeFile (String fileName, String contents, boolean append) { - writeFile(fileName, contents, append, null); - } - - static public void writeFile (String fileName, String contents, boolean append, String charset) { - Writer writer = null; - try { - File file = new File(fileName); - FileOutputStream output = new FileOutputStream(file, append); - if (charset == null) - writer = new OutputStreamWriter(output); - else - writer = new OutputStreamWriter(output, charset); - writer.write(contents); - } catch (Exception ex) { - throw new RuntimeException("Error writing file: " + fileName, ex); - } finally { - try { - if (writer != null) writer.close(); - } catch (Exception ignored) { - } - } - } - - /** Deletes a file or directory and all files and subdirecties under it. */ - static public boolean delete (String fileName) { - if (fileName == null) throw new IllegalArgumentException("fileName cannot be null."); - - File file = new File(fileName); - if (file.exists() && file.isDirectory()) { - File[] files = file.listFiles(); - for (int i = 0, n = files.length; i < n; i++) { - if (files[i].isDirectory()) - delete(files[i].getAbsolutePath()); - else { - if (TRACE) trace("scar", "Deleting file: " + files[i]); - files[i].delete(); - } - } - } - if (TRACE) trace("scar", "Deleting file: " + file); - return file.delete(); - } - - /** Creates the directories in the specified path. */ - static public String mkdir (String path) { - if (path == null) throw new IllegalArgumentException("path cannot be null."); - - if (new File(path).mkdirs() && TRACE) trace("scar", "Created directory: " + path); - return path; - } - - /** Returns true if the file exists. */ - static public boolean fileExists (String path) { - if (path == null) throw new IllegalArgumentException("path cannot be null."); - - return new File(path).exists(); - } - - /** Returns the canonical path for the specified path. Eg, if "." is passed, this will resolve the actual path and return it. */ - static public String canonical (String path) { - if (path == null) throw new IllegalArgumentException("path cannot be null."); - - File file = new File(path); - try { - return file.getCanonicalPath(); - } catch (IOException ex) { - file = file.getAbsoluteFile(); - if (file.getName().equals(".")) file = file.getParentFile(); - return file.getPath(); - } - } - - /** Returns the textual contents of the specified file. */ - static public String fileContents (String path) throws IOException { - StringBuilder stringBuffer = new StringBuilder(4096); - FileReader reader = new FileReader(path); - try { - char[] buffer = new char[2048]; - while (true) { - int length = reader.read(buffer); - if (length == -1) break; - stringBuffer.append(buffer, 0, length); - } - } finally { - try { - reader.close(); - } catch (Exception ignored) { - } - } - return stringBuffer.toString(); - } - - /** Returns only the filename portion of the specified path. */ - static public String fileName (String path) { - return new File(canonical(path)).getName(); - } - - /** Returns the parent directory of the specified path. */ - static public String parent (String path) { - return new File(canonical(path)).getParent(); - } - - /** Returns only the extension portion of the specified path, or an empty string if there is no extension. */ - static public String fileExtension (String file) { - if (file == null) throw new IllegalArgumentException("fileName cannot be null."); - - int commaIndex = file.indexOf('.'); - if (commaIndex == -1) return ""; - return file.substring(commaIndex + 1); - } - - /** Returns only the filename portion of the specified path, without the extension, if any. */ - static public String fileWithoutExtension (String file) { - if (file == null) throw new IllegalArgumentException("fileName cannot be null."); - - int commaIndex = file.indexOf('.'); - if (commaIndex == -1) commaIndex = file.length(); - int slashIndex = file.replace('\\', '/').lastIndexOf('/'); - if (slashIndex == -1) - slashIndex = 0; - else - slashIndex++; - return file.substring(slashIndex, commaIndex); - } - - /** Shortcut for System.out.println. */ - static public void log (String text) { - System.out.println(text); - } - - /** Returns a substring of the specified text. - * @param end The end index of the substring. If negative, the index used will be "text.length() + end". */ - static public String substring (String text, int start, int end) { - if (text == null) throw new IllegalArgumentException("text cannot be null."); - - if (end >= 0) return text.substring(start, end); - return text.substring(start, text.length() + end); - } - - static public void jws (String inputDir, String outputDir, boolean pack, String keystoreFile, String alias, String password) - throws IOException { - if (inputDir == null) throw new IllegalArgumentException("inputDir cannot be null."); - if (outputDir == null) throw new IllegalArgumentException("outputDir cannot be null."); - if (keystoreFile == null) throw new IllegalArgumentException("keystoreFile cannot be null."); - if (alias == null) throw new IllegalArgumentException("alias cannot be null."); - if (password == null) throw new IllegalArgumentException("password cannot be null."); - if (password.length() < 6) throw new IllegalArgumentException("password must be 6 or more characters."); - - mkdir(outputDir); - paths(inputDir, "*.jar", "*.jnlp").copyTo(outputDir); - for (String file : paths(outputDir, "*.jar")) - sign(unpack200(pack200(unsign(file))), keystoreFile, alias, password); - if (!pack) return; - for (String file : paths(outputDir, "*.jar", "!*native*")) - gzip(pack200(file)); - } - - static public void jnlp (String inputDir, String mainClass, String mainClassJar, String url, String company, String title, - String splashImage) throws IOException { - if (mainClass == null) throw new IllegalArgumentException("mainClass cannot be null."); - if (url == null) throw new IllegalArgumentException("url cannot be null."); - if (title == null) throw new IllegalArgumentException("title cannot be null."); - if (company == null) throw new IllegalArgumentException("company cannot be null."); - - int firstSlash = url.indexOf("/", 7); - int lastSlash = url.lastIndexOf("/"); - if (firstSlash == -1 || lastSlash == -1) throw new RuntimeException("Invalid url: " + url); - String domain = url.substring(0, firstSlash + 1); - String path = url.substring(firstSlash + 1, lastSlash + 1); - String jnlpFile = url.substring(lastSlash + 1); - - FileWriter writer = new FileWriter(inputDir + jnlpFile); - try { - writer.write("\n"); - writer.write("\n"); - writer.write("\n"); - writer.write("\t" + title + "\n"); - writer.write("\t" + company + "\n"); - writer.write("\t\n"); - writer.write("\t" + title + "\n"); - writer.write("\t" + title + "\n"); - if (splashImage != null) writer.write("\t\n"); - writer.write("\n"); - writer.write("\n"); - writer.write("\t\n"); - writer.write("\n"); - writer.write("\n"); - writer.write("\t\n"); - - // JAR with main class first. - writer.write("\t\n"); - - // Rest of JARs, except natives. - for (String file : paths(inputDir, "**/*.jar", "**/*.jar.pack.lzma", "!*native*", "!**/" + mainClassJar)) - writer.write("\t\n"); - - writer.write("\n"); - Paths nativePaths = paths(inputDir, "*native*win*", "*win*native*"); - if (nativePaths.count() == 1) { - writer.write("\n"); - writer.write("\t\n"); - writer.write("\t\n"); - writer.write("\n"); - } - nativePaths = paths(inputDir, "*native*mac*", "*mac*native*"); - if (nativePaths.count() == 1) { - writer.write("\n"); - writer.write("\t\n"); - writer.write("\t\n"); - writer.write("\n"); - } - nativePaths = paths(inputDir, "*native*linux*", "*linux*native*"); - if (nativePaths.count() == 1) { - writer.write("\n"); - writer.write("\t\n"); - writer.write("\t\n"); - writer.write("\n"); - } - nativePaths = paths(inputDir, "*native*solaris*", "*solaris*native*"); - if (nativePaths.count() == 1) { - writer.write("\n"); - writer.write("\t\n"); - writer.write("\t\n"); - writer.write("\n"); - } - writer.write("\n"); - writer.write(""); - } finally { - try { - writer.close(); - } catch (Exception ignored) { - } - } - } - - static public void lwjglApplet (String inputDir, String outputDir, String keystoreFile, String alias, String password) - throws IOException { - if (inputDir == null) throw new IllegalArgumentException("inputDir cannot be null."); - if (outputDir == null) throw new IllegalArgumentException("outputDir cannot be null."); - if (keystoreFile == null) throw new IllegalArgumentException("keystoreFile cannot be null."); - if (alias == null) throw new IllegalArgumentException("alias cannot be null."); - if (password == null) throw new IllegalArgumentException("password cannot be null."); - if (password.length() < 6) throw new IllegalArgumentException("password must be 6 or more characters."); - - mkdir(outputDir); - paths(inputDir, "**/*.jar", "*.html", "*.htm").flatten().copyTo(outputDir); - for (String jarFile : paths(outputDir, "*.jar")) { - sign(unpack200(pack200(unsign(jarFile))), keystoreFile, alias, password); - String fileName = fileName(jarFile); - if (fileName.equals("lwjgl_util_applet.jar") || fileName.equals("lzma.jar")) continue; - if (fileName.contains("native")) - lzma(jarFile); - else - lzma(pack200(jarFile)); - } - } - - static public void lwjglAppletHtml (String inputDir, String mainClass) throws IOException { - if (INFO) info("scar", "Generating: applet.html"); - FileWriter writer = new FileWriter(inputDir + "applet.html"); - try { - writer.write("\n"); - writer.write("Applet\n"); - writer.write("\n"); - writer - .write("\n"); - writer.write("\n"); - writer.write("\n"); - writer.write("\n"); - writer.write("\n"); - Paths nativePaths = paths(inputDir, "*native*win*.jar.lzma", "*win*native*.jar.lzma"); - if (nativePaths.count() == 1) writer.write("\n"); - nativePaths = paths(inputDir, "*native*mac*.jar.lzma", "*mac*native*.jar.lzma"); - if (nativePaths.count() == 1) writer.write("\n"); - nativePaths = paths(inputDir, "*native*linux*.jar.lzma", "*linux*native*.jar.lzma"); - if (nativePaths.count() == 1) writer.write("\n"); - nativePaths = paths(inputDir, "*native*solaris*.jar.lzma", "*solaris*native*.jar.lzma"); - if (nativePaths.count() == 1) writer.write("\n"); - writer.write("\n"); - writer.write("\n"); - writer.write("\n"); - writer - .write("\n"); - writer.write("\n"); - writer.write("\n"); - } finally { - try { - writer.close(); - } catch (Exception ignored) { - } - } - } - - static public Paths path (String file) { - return new Paths().addFile(file); - } - - static public Paths path (String dir, String file) { - return new Paths().addFile(file); - } - - static public Paths paths (String dir, String... patterns) { - return new Paths(dir, patterns); - } - - static public void compile (Paths source, Paths classpath, String outputDir) { - if (source.isEmpty()) { - if (WARN) warn("scar", "No source files found."); - return; - } - - ArrayList args = new ArrayList(); - if (TRACE) args.add("-verbose"); - args.add("-d"); - args.add(outputDir); - args.add("-g:source,lines"); - args.add("-source"); - args.add("1.6"); - args.add("-target"); - args.add("1.6"); - args.addAll(source.getPaths()); - if (classpath != null && !classpath.isEmpty()) { - args.add("-classpath"); - args.add(isWindows ? classpath.toString(";") : classpath.toString(":")); - } - - JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); - if (compiler == null) - throw new RuntimeException("No compiler available. Ensure you are running from a 1.6+ JDK, and not a JRE."); - if (compiler.run(null, null, null, args.toArray(new String[args.size()])) != 0) { - throw new RuntimeException("Error during compilation.\nSource: " + source.count() + " files\nClasspath: " + classpath); - } - try { - Thread.sleep(100); - } catch (InterruptedException ex) { - } - } - - static public void jwsHtaccess (String jwsDir) throws IOException { - for (String packedFile : paths(jwsDir + "packed", "*.jar.pack.gz")) { - String packedFileName = fileName(packedFile); - String jarFileName = substring(packedFileName, 0, -8); - FileWriter writer = new FileWriter(jwsDir + jarFileName + ".var"); - try { - writer.write("URI: packed/" + packedFileName + "\n"); - writer.write("Content-Type: x-java-archive\n"); - writer.write("Content-Encoding: pack200-gzip\n"); - writer.write("URI: unpacked/" + jarFileName + "\n"); - writer.write("Content-Type: x-java-archive\n"); - } finally { - try { - writer.close(); - } catch (Exception ignored) { - } - } - } - FileWriter writer = new FileWriter(jwsDir + ".htaccess"); - try { - writer.write("AddType application/x-java-jnlp-file .jnlp"); // JNLP mime type. - writer.write("AddType application/x-java-archive .jar\n"); // JAR mime type. - writer.write("AddHandler application/x-type-map .var\n"); // Enable type maps. - writer.write("Options +MultiViews\n"); - writer.write("MultiViewsMatch Any\n"); // Apache 2.0 only. - writer.write("\n"); - writer.write("AddEncoding pack200-gzip .jar\n"); // Enable Content-Encoding header for .jar.pack.gz files. - writer.write("RemoveEncoding .gz\n"); // Prevent mod_gzip from messing with the Content-Encoding response. - writer.write("\n"); - } finally { - try { - writer.close(); - } catch (Exception ignored) { - } - } - } - - static public void executeCode (String code, HashMap parameters) { - executeCode(code, parameters); - } - - /** Compiles and executes the speFcified Java code. The code is compiled as if it were a Java method body. - *

- * Imports statements can be used before any code. These imports are automatically used:
- * import com.esotericsoftware.scar.Scar;
- * import com.esotericsoftware.wildcard.Paths;
- * import com.esotericsoftware.minlog.Log;
- * import static com.esotericsoftware.scar.Scar.*;
- * import static com.esotericsoftware.minlog.Log.*;
- *

- * Entries can be added to the classpath by using "classpath [url];" statements at the start of the code. These classpath - * entries are checked before the classloader that loaded the Scar class is checked. Examples:
- * classpath someTools.jar;
- * classpath some/directory/of/class/files;
- * classpath http://example.com/someTools.jar;
- * If a project parameter is not null, non-absolute classpath entries will be relative to the project directory. - * @param parameters These parameters will be available in the scope where the code is executed. */ - static public void executeCode (String code, HashMap parameters, Project project) { - JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); - if (compiler == null) - throw new RuntimeException("No compiler available. Ensure you are running from a 1.6+ JDK, and not a JRE."); - - try { - // Wrap code in a class. - StringBuilder classBuffer = new StringBuilder(2048); - classBuffer.append("import com.esotericsoftware.scar.*;\n"); - classBuffer.append("import com.esotericsoftware.minlog.Log;\n"); - classBuffer.append("import com.esotericsoftware.wildcard.Paths;\n"); - classBuffer.append("import static com.esotericsoftware.scar.Scar.*;\n"); - classBuffer.append("import static com.esotericsoftware.minlog.Log.*;\n"); - classBuffer.append("public class Generated {\n"); - int templateStartLines = 6; - classBuffer.append("public void execute ("); - int i = 0; - for (Entry entry : parameters.entrySet()) { - if (i++ > 0) classBuffer.append(','); - classBuffer.append('\n'); - templateStartLines++; - classBuffer.append(entry.getValue().getClass().getName()); - classBuffer.append(' '); - classBuffer.append(entry.getKey()); - } - classBuffer.append("\n) throws Exception {\n"); - templateStartLines += 2; - - // Append code, collecting imports statements and classpath URLs. - StringBuilder importBuffer = new StringBuilder(512); - ArrayList classpathURLs = new ArrayList(); - BufferedReader reader = new BufferedReader(new StringReader(code)); - boolean header = true; - while (true) { - String line = reader.readLine(); - if (line == null) break; - String trimmed = line.trim(); - if (header && trimmed.startsWith("import ") && trimmed.endsWith(";")) { - importBuffer.append(line); - importBuffer.append('\n'); - } else if (header && trimmed.startsWith("classpath ") && trimmed.endsWith(";")) { - String path = substring(line.trim(), 10, -1); - try { - classpathURLs.add(new URL(path)); - } catch (MalformedURLException ex) { - if (project != null) classpathURLs.add(new File(project.path(path)).toURI().toURL()); - } - } else { - if (trimmed.length() > 0) header = false; - classBuffer.append(line); - classBuffer.append('\n'); - } - } - classBuffer.append("}}"); - - final String classCode = importBuffer.append(classBuffer).toString(); - if (TRACE) trace("scar", "Executing code:\n" + classCode); - - // Construct classpath option. - List options = new ArrayList(); - { - StringBuffer buffer = new StringBuffer(System.getProperty("java.class.path")); - String pathSeparator = System.getProperty("path.separator"); - for (URL url : classpathURLs) { - buffer.append(pathSeparator); - buffer.append(new File(url.toURI()).getCanonicalPath()); - } - if (TRACE) trace("Using classpath: " + buffer); - options.add("-classpath"); - options.add(buffer.toString()); - } - - // Compile class. - final ByteArrayOutputStream output = new ByteArrayOutputStream(32 * 1024); - final SimpleJavaFileObject javaObject = new SimpleJavaFileObject(URI.create("Generated.java"), Kind.SOURCE) { - public OutputStream openOutputStream () { - return output; - } - - public CharSequence getCharContent (boolean ignoreEncodingErrors) { - return classCode; - } - }; - DiagnosticCollector diagnostics = new DiagnosticCollector(); - compiler.getTask(null, new ForwardingJavaFileManager(compiler.getStandardFileManager(null, null, null)) { - public JavaFileObject getJavaFileForOutput (Location location, String className, Kind kind, FileObject sibling) { - return javaObject; - } - }, diagnostics, options, null, Arrays.asList(new JavaFileObject[] {javaObject})).call(); - - if (!diagnostics.getDiagnostics().isEmpty()) { - StringBuilder buffer = new StringBuilder(1024); - for (Diagnostic diagnostic : diagnostics.getDiagnostics()) { - if (buffer.length() > 0) buffer.append("\n"); - buffer.append("Line "); - buffer.append(diagnostic.getLineNumber() - templateStartLines); - buffer.append(": "); - buffer.append(diagnostic.getMessage(null).replaceAll("^Generated.java:\\d+:\\d* ", "")); - } - throw new RuntimeException("Compilation errors:\n" + buffer); - } - - // Load class. - Class generatedClass = new URLClassLoader(classpathURLs.toArray(new URL[classpathURLs.size()]), - Scar.class.getClassLoader()) { - protected synchronized Class loadClass (String name, boolean resolve) throws ClassNotFoundException { - // Look in this classloader before the parent. - Class c = findLoadedClass(name); - if (c == null) { - try { - c = findClass(name); - } catch (ClassNotFoundException e) { - return super.loadClass(name, resolve); - } - } - if (resolve) resolveClass(c); - return c; - } - - protected Class findClass (String name) throws ClassNotFoundException { - if (name.equals("Generated")) { - byte[] bytes = output.toByteArray(); - return defineClass(name, bytes, 0, bytes.length); - } - return super.findClass(name); - } - }.loadClass("Generated"); - - // Execute. - Class[] parameterTypes = new Class[parameters.size()]; - Object[] parameterValues = new Object[parameters.size()]; - i = 0; - for (Object object : parameters.values()) { - parameterValues[i] = object; - parameterTypes[i++] = object.getClass(); - } - generatedClass.getMethod("execute", parameterTypes).invoke(generatedClass.newInstance(), parameterValues); - } catch (Throwable ex) { - throw new RuntimeException("Error executing code:\n" + code.trim(), ex); - } - } - - static public boolean ftpUpload (String server, String user, String password, String dir, Paths paths, boolean passive) - throws IOException { - FTPClient ftp = new FTPClient(); - InetAddress address = InetAddress.getByName(server); - if (DEBUG) debug("scar", "Connecting to FTP server: " + address); - ftp.connect(address); - if (passive) ftp.enterLocalPassiveMode(); - if (!ftp.login(user, password)) { - if (ERROR) error("scar", "FTP login failed for user: " + user); - return false; - } - if (!ftp.changeWorkingDirectory(dir)) { - if (ERROR) error("scar", "FTP directory change failed: " + dir); - return false; - } - ftp.setFileType(org.apache.commons.net.ftp.FTP.BINARY_FILE_TYPE); - for (String path : paths) { - if (INFO) info("scar", "FTP upload: " + path); - BufferedInputStream input = new BufferedInputStream(new FileInputStream(path)); - try { - ftp.storeFile(new File(path).getName(), input); - } finally { - try { - input.close(); - } catch (Exception ignored) { - } - } - } - ftp.logout(); - ftp.disconnect(); - return true; - } - - static public void sftpUpload (String server, String user, String password, String dir, Paths paths) throws IOException { - Session session = null; - ChannelSftp channel = null; - try { - session = new JSch().getSession(user, server, 22); - session.setConfig("StrictHostKeyChecking", "no"); - session.setPassword(password); - session.connect(); - - channel = (ChannelSftp)session.openChannel("sftp"); - channel.connect(); - channel.cd(dir); - for (String path : paths) { - if (INFO) info("scar", "SFTP upload: " + path); - final File file = new File(path); - BufferedInputStream input = new BufferedInputStream(new FileInputStream(file)); - try { - channel.put(input, new File(path).getName(), new SftpProgressMonitor() { - private long total, interval = Math.max(1, file.length() / 76), lastCount; - - public void init (int op, String source, String dest, long max) { - System.out.print("|-"); - } - - public void end () { - System.out.println("|"); - } - - public boolean count (long count) { - total += count; - while (total - lastCount >= interval) { - lastCount += interval; - System.out.print("-"); - } - return true; - } - }); - } finally { - try { - input.close(); - } catch (Exception ignored) { - } - } - } - } catch (Exception ex) { - throw new IOException(ex); - } finally { - try { - if (session != null) session.disconnect(); - if (channel != null) channel.exit(); - } catch (Exception ignored) { - } - } - } - - static public void ssh (String server, String user, String password, String command) throws IOException { - if (INFO) info("scar", "SSH: " + command); - Session session = null; - ChannelExec channel = null; - try { - session = new JSch().getSession(user, server, 22); - session.setConfig("StrictHostKeyChecking", "no"); - session.setPassword(password); - session.connect(); - channel = (ChannelExec)session.openChannel("exec"); - channel.setCommand(command); - channel.setInputStream(null); - channel.setErrStream(System.err); - InputStream in = channel.getInputStream(); - channel.connect(); - byte[] buffer = new byte[1024]; - while (true) { - while (in.available() > 0) { - int count = in.read(buffer, 0, 1024); - if (count < 0) break; - System.out.print(new String(buffer, 0, count)); - } - if (channel.isClosed()) { - System.out.println("Exit: " + channel.getExitStatus()); - break; - } - try { - Thread.sleep(100); - } catch (Exception ee) { - } - } - } catch (Exception ex) { - throw new IOException(ex); - } finally { - try { - if (session != null) session.disconnect(); - if (channel != null) channel.disconnect(); - } catch (Exception ignored) { - } - } - } - - static public ArrayList list (Object... objects) { - if (objects == null) throw new IllegalArgumentException("objects cannot be null."); - ArrayList list = new ArrayList(objects.length); - for (Object o : objects) - list.add(o); - return list; - } - - private Scar () { - } - - static public void main (String[] args) throws IOException { - Scar.args = new Arguments(args); - - if (Scar.args.has("trace")) - TRACE(); - else if (Scar.args.has("debug")) - DEBUG(); - else if (Scar.args.has("info")) - INFO(); - else if (Scar.args.has("warn")) - WARN(); - else if (Scar.args.has("error")) // - ERROR(); - - // BOZO - Add something that can execute scar methods! - } -} + +package com.esotericsoftware.scar; + +import static com.esotericsoftware.minlog.Log.*; + +import SevenZip.LzmaAlone; + +import com.esotericsoftware.wildcard.Paths; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.StringReader; +import java.io.Writer; +import java.net.InetAddress; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.channels.FileChannel; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map.Entry; +import java.util.jar.Attributes; +import java.util.jar.JarEntry; +import java.util.jar.JarInputStream; +import java.util.jar.JarOutputStream; +import java.util.jar.Manifest; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.zip.Deflater; +import java.util.zip.GZIPInputStream; +import java.util.zip.GZIPOutputStream; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +import javax.tools.Diagnostic; +import javax.tools.DiagnosticCollector; +import javax.tools.FileObject; +import javax.tools.ForwardingJavaFileManager; +import javax.tools.JavaCompiler; +import javax.tools.JavaFileObject; +import javax.tools.JavaFileObject.Kind; +import javax.tools.SimpleJavaFileObject; +import javax.tools.ToolProvider; + +import org.apache.commons.net.ftp.FTPClient; + +import com.jcraft.jsch.ChannelExec; +import com.jcraft.jsch.ChannelSftp; +import com.jcraft.jsch.JSch; +import com.jcraft.jsch.Session; +import com.jcraft.jsch.SftpProgressMonitor; + +// BOZO - Add javadocs method. + +/** Provides utility methods for common Java build tasks. */ +public class Scar { + /** The Scar installation directory. The value comes from the SCAR_HOME environment variable, if it exists. Alternatively, the + * "scar.home" System property can be defined. */ + static public final String SCAR_HOME; + static { + if (System.getProperty("scar.home") != null) + SCAR_HOME = System.getProperty("scar.home"); + else + SCAR_HOME = System.getenv("SCAR_HOME"); + } + + /** The command line arguments Scar was started with. Empty if Scar was started with no arguments or Scar was not started from + * the command line. */ + static public Arguments args = new Arguments(); + + /** The Java installation directory. */ + static public final String JAVA_HOME = System.getProperty("java.home"); + + /** True if running on a Mac OS. */ + static public final boolean isMac = System.getProperty("os.name").toLowerCase().contains("mac os x"); + + /** True if running on a Windows OS. */ + static public final boolean isWindows = System.getProperty("os.name").toLowerCase().contains("windows"); + + static private final String manifestFileName = "META-INF" + File.separator + "MANIFEST.MF"; + + static { + Paths.setDefaultGlobExcludes("**/.svn/**"); + } + + /** Returns the full path for the specified file name in the current working directory, the {@link #SCAR_HOME}, and the bin + * directory of {@link #JAVA_HOME}. */ + static public String resolvePath (String fileName) { + if (fileName == null) return null; + + String foundFile; + while (true) { + foundFile = canonical(fileName); + if (fileExists(foundFile)) break; + + foundFile = new File(SCAR_HOME, fileName).getPath(); + if (fileExists(foundFile)) break; + + foundFile = new File(JAVA_HOME, "bin/" + fileName).getPath(); + if (fileExists(foundFile)) break; + + foundFile = fileName; + break; + } + if (TRACE) trace("scar", "Path \"" + fileName + "\" resolved to: " + foundFile); + return foundFile; + } + + static public void jar (String outputFile, String inputDir) throws IOException { + jar(outputFile, new Paths(inputDir), null, null); + } + + static public void jar (String outputFile, String inputDir, String mainClass, Paths classpath) throws IOException { + jar(outputFile, new Paths(inputDir), mainClass, classpath); + } + + static public void jar (String outputFile, Paths inputPaths) throws IOException { + jar(outputFile, inputPaths, null, null); + } + + // BOZO - javadoc + static public void jar (String outputFile, Paths inputPaths, String mainClass, Paths classpath) throws IOException { + if (outputFile == null) throw new IllegalArgumentException("jarFile cannot be null."); + if (inputPaths == null) throw new IllegalArgumentException("inputPaths cannot be null."); + + inputPaths = inputPaths.filesOnly(); + if (inputPaths.isEmpty()) { + if (WARN) warn("scar", "No files to JAR."); + return; + } + + List fullPaths = inputPaths.getPaths(); + List relativePaths = inputPaths.getRelativePaths(); + int manifestIndex = relativePaths.indexOf(manifestFileName); + if (manifestIndex > 0) { + // Ensure MANIFEST.MF is first. + relativePaths.remove(manifestIndex); + relativePaths.add(0, manifestFileName); + String manifestFullPath = fullPaths.get(manifestIndex); + fullPaths.remove(manifestIndex); + fullPaths.add(0, manifestFullPath); + } else if (mainClass != null) { + if (DEBUG) debug("scar", "Generating JAR manifest."); + String manifestFile = tempFile("manifest"); + relativePaths.add(0, manifestFileName); + fullPaths.add(0, manifestFile); + + Manifest manifest = new Manifest(); + Attributes attributes = manifest.getMainAttributes(); + attributes.putValue(Attributes.Name.MANIFEST_VERSION.toString(), "1.0"); + if (DEBUG) debug("scar", "Main class: " + mainClass); + attributes.putValue(Attributes.Name.MAIN_CLASS.toString(), mainClass); + StringBuilder buffer = new StringBuilder(512); + buffer.append(fileName(outputFile)); + buffer.append(" ."); + for (String name : classpath.getRelativePaths()) { + buffer.append(' '); + buffer.append(name); + } + attributes.putValue(Attributes.Name.CLASS_PATH.toString(), buffer.toString()); + FileOutputStream output = new FileOutputStream(manifestFile); + try { + manifest.write(output); + } finally { + try { + output.close(); + } catch (Exception ignored) { + } + } + } + + if (DEBUG) debug("scar", "Creating JAR (" + inputPaths.count() + " entries): " + outputFile); + + mkdir(new File(outputFile).getParent()); + JarOutputStream output = new JarOutputStream(new BufferedOutputStream(new FileOutputStream(outputFile))); + output.setLevel(Deflater.BEST_COMPRESSION); + try { + for (int i = 0, n = fullPaths.size(); i < n; i++) { + JarEntry jarEntry = new JarEntry(relativePaths.get(i).replace('\\', '/')); + jarEntry.setTime(1370273339); + output.putNextEntry(jarEntry); + FileInputStream input = new FileInputStream(fullPaths.get(i)); + try { + byte[] buffer = new byte[4096]; + while (true) { + int length = input.read(buffer); + if (length == -1) break; + output.write(buffer, 0, length); + } + } finally { + try { + input.close(); + } catch (Exception ignored) { + } + } + } + } finally { + try { + output.close(); + } catch (Exception ignored) { + } + } + } + + static public void oneJAR (String inputDir, String outputFile, String mainClass, Paths classpath) throws IOException { + oneJAR(paths(inputDir, "*.jar"), outputFile, mainClass, classpath); + } + + static public void oneJAR (Paths jars, String outputFile, String mainClass, Paths classpath) throws IOException { + if (jars == null) throw new IllegalArgumentException("jars cannot be null."); + + String tempDir = tempDirectory("oneJAR"); + + ArrayList processedJARs = new ArrayList(); + for (String jarFile : jars) { + unzip(jarFile, tempDir); + processedJARs.add(jarFile); + } + + if (mainClass != null) new File(tempDir, manifestFileName).delete(); + + mkdir(parent(outputFile)); + jar(outputFile, tempDir, mainClass, classpath); + delete(tempDir); + } + + /** Removes any code signatures on the specified JAR. Removes any signature files in the META-INF directory and removes any + * signature entries from the JAR's manifest. + * @return The path to the JAR file. */ + static public String unsign (String jarFile) throws IOException { + if (jarFile == null) throw new IllegalArgumentException("jarFile cannot be null."); + + if (DEBUG) debug("scar", "Removing signature from JAR: " + jarFile); + + File tempFile = File.createTempFile("scar", "removejarsig"); + JarOutputStream jarOutput = null; + JarInputStream jarInput = null; + try { + jarOutput = new JarOutputStream(new FileOutputStream(tempFile)); + jarInput = new JarInputStream(new FileInputStream(jarFile)); + Manifest manifest = jarInput.getManifest(); + if (manifest != null) { + // Remove manifest file entries. + manifest.getEntries().clear(); + jarOutput.putNextEntry(new JarEntry(manifestFileName)); + manifest.write(jarOutput); + } + byte[] buffer = new byte[4096]; + while (true) { + JarEntry entry = jarInput.getNextJarEntry(); + if (entry == null) break; + String name = entry.getName(); + // Skip signature files. + if (name.startsWith("META-INF/") && (name.endsWith(".SF") || name.endsWith(".DSA") || name.endsWith(".RSA"))) + continue; + jarOutput.putNextEntry(new JarEntry(name)); + while (true) { + int length = jarInput.read(buffer); + if (length == -1) break; + jarOutput.write(buffer, 0, length); + } + } + jarInput.close(); + jarOutput.close(); + copyFile(tempFile.getAbsolutePath(), jarFile); + } catch (IOException ex) { + throw new IOException("Error unsigning JAR file: " + jarFile, ex); + } finally { + try { + if (jarInput != null) jarInput.close(); + } catch (Exception ignored) { + } + try { + if (jarOutput != null) jarOutput.close(); + } catch (Exception ignored) { + } + tempFile.delete(); + } + return jarFile; + } + + /** Creates a new keystore for signing JARs. If the keystore file already exists, no action will be taken. + * @return The path to the keystore file. */ + static public String keystore (String keystoreFile, String alias, String password, String company, String title) + throws IOException { + if (keystoreFile == null) throw new IllegalArgumentException("keystoreFile cannot be null."); + if (fileExists(keystoreFile)) return keystoreFile; + if (alias == null) throw new IllegalArgumentException("alias cannot be null."); + if (password == null) throw new IllegalArgumentException("password cannot be null."); + if (password.length() < 6) throw new IllegalArgumentException("password must be 6 or more characters."); + if (company == null) throw new IllegalArgumentException("company cannot be null."); + if (title == null) throw new IllegalArgumentException("title cannot be null."); + + if (DEBUG) + debug("scar", "Creating keystore (" + alias + ":" + password + ", " + company + ", " + title + "): " + keystoreFile); + + File file = new File(keystoreFile); + file.delete(); + Process process = Runtime.getRuntime().exec( + new String[] {resolvePath("keytool"), "-genkey", "-keystore", keystoreFile, "-alias", alias}); + OutputStreamWriter writer = new OutputStreamWriter(process.getOutputStream()); + writer.write(password + "\n"); // Enter keystore password: + writer.write(password + "\n"); // Re-enter new password: + writer.write(company + "\n"); // What is your first and last name? + writer.write(title + "\n"); // What is the name of your organizational unit? + writer.write(title + "\n"); // What is the name of your organization? + writer.write("\n"); // What is the name of your City or Locality? [Unknown] + writer.write("\n"); // What is the name of your State or Province? [Unknown] + writer.write("\n"); // What is the two-letter country code for this unit? [Unknown] + writer.write("yes\n"); // Correct? + writer.write("\n"); // Return if same alias key password as keystore. + writer.flush(); + process.getOutputStream().close(); + process.getInputStream().close(); + process.getErrorStream().close(); + try { + process.waitFor(); + } catch (InterruptedException ignored) { + } + if (!file.exists()) throw new RuntimeException("Error creating keystore."); + return keystoreFile; + } + + /** Signs the specified JAR. + * @return The path to the JAR. */ + static public String sign (String jarFile, String keystoreFile, String alias, String password) throws IOException { + if (jarFile == null) throw new IllegalArgumentException("jarFile cannot be null."); + if (keystoreFile == null) throw new IllegalArgumentException("keystoreFile cannot be null."); + if (alias == null) throw new IllegalArgumentException("alias cannot be null."); + if (password == null) throw new IllegalArgumentException("password cannot be null."); + if (password.length() < 6) throw new IllegalArgumentException("password must be 6 or more characters."); + + if (DEBUG) debug("scar", "Signing JAR (" + keystoreFile + ", " + alias + ":" + password + "): " + jarFile); + + shell("jarsigner", "-keystore", keystoreFile, "-storepass", password, "-keypass", password, jarFile, alias); + return jarFile; + } + + /** Encodes the specified file with pack200. The resulting filename is the filename plus ".pack". The file is deleted after + * encoding. + * @return The path to the encoded file. */ + static public String pack200 (String jarFile) throws IOException { + String packedFile = pack200(jarFile, jarFile + ".pack"); + delete(jarFile); + return packedFile; + } + + /** Encodes the specified file with pack200. + * @return The path to the encoded file. */ + static public String pack200 (String jarFile, String packedFile) throws IOException { + if (jarFile == null) throw new IllegalArgumentException("jarFile cannot be null."); + if (packedFile == null) throw new IllegalArgumentException("packedFile cannot be null."); + + if (DEBUG) debug("scar", "Packing JAR: " + jarFile + " -> " + packedFile); + + shell("pack200", "--no-gzip", "--segment-limit=-1", "--no-keep-file-order", "--effort=7", "--modification-time=latest", + packedFile, jarFile); + return packedFile; + } + + /** Decodes the specified file with pack200. The filename must end in ".pack" and the resulting filename has this stripped. The + * encoded file is deleted after decoding. + * @return The path to the decoded file. */ + static public String unpack200 (String packedFile) throws IOException { + if (packedFile == null) throw new IllegalArgumentException("packedFile cannot be null."); + if (!packedFile.endsWith(".pack")) throw new IllegalArgumentException("packedFile must end with .pack: " + packedFile); + + String jarFile = unpack200(packedFile, substring(packedFile, 0, -5)); + delete(packedFile); + return jarFile; + } + + /** Decodes the specified file with pack200. + * @return The path to the decoded file. */ + static public String unpack200 (String packedFile, String jarFile) throws IOException { + if (packedFile == null) throw new IllegalArgumentException("packedFile cannot be null."); + if (jarFile == null) throw new IllegalArgumentException("jarFile cannot be null."); + + if (DEBUG) debug("scar", "Unpacking JAR: " + packedFile + " -> " + jarFile); + + shell("unpack200", packedFile, jarFile); + return jarFile; + } + + /** Encodes the specified file with GZIP. The resulting filename is the filename plus ".gz". The file is deleted after encoding. + * @return The path to the encoded file. */ + static public String gzip (String file) throws IOException { + String gzipFile = gzip(file, file + ".gz"); + delete(file); + return gzipFile; + } + + /** Encodes the specified file with GZIP. + * @return The path to the encoded file. */ + static public String gzip (String file, String gzipFile) throws IOException { + if (file == null) throw new IllegalArgumentException("file cannot be null."); + if (gzipFile == null) throw new IllegalArgumentException("gzipFile cannot be null."); + + if (DEBUG) debug("scar", "GZIP encoding: " + file + " -> " + gzipFile); + + InputStream input = new FileInputStream(file); + try { + copyStream(input, new GZIPOutputStream(new FileOutputStream(gzipFile))); + } finally { + try { + input.close(); + } catch (Exception ignored) { + } + } + return gzipFile; + } + + /** Decodes the specified GZIP file. The filename must end in ".gz" and the resulting filename has this stripped. The encoded + * file is deleted after decoding. + * @return The path to the decoded file. */ + static public String ungzip (String gzipFile) throws IOException { + if (gzipFile == null) throw new IllegalArgumentException("gzipFile cannot be null."); + if (!gzipFile.endsWith(".gz")) throw new IllegalArgumentException("gzipFile must end with .gz: " + gzipFile); + + String file = ungzip(gzipFile, substring(gzipFile, 0, -3)); + delete(gzipFile); + return file; + } + + /** Decodes the specified GZIP file. + * @return The path to the decoded file. */ + static public String ungzip (String gzipFile, String file) throws IOException { + if (gzipFile == null) throw new IllegalArgumentException("gzipFile cannot be null."); + if (file == null) throw new IllegalArgumentException("file cannot be null."); + + if (DEBUG) debug("scar", "GZIP decoding: " + gzipFile + " -> " + file); + + InputStream input = new GZIPInputStream(new FileInputStream(gzipFile)); + try { + copyStream(input, new FileOutputStream(file)); + } finally { + try { + input.close(); + } catch (Exception ignored) { + } + } + return file; + } + + /** Encodes the specified files with ZIP. + * @return The path to the encoded file. */ + static public String zip (Paths paths, String zipFile) throws IOException { + if (paths == null) throw new IllegalArgumentException("paths cannot be null."); + if (zipFile == null) throw new IllegalArgumentException("zipFile cannot be null."); + + if (DEBUG) debug("scar", "Creating ZIP (" + paths.count() + " entries): " + zipFile); + + paths.zip(zipFile); + return zipFile; + } + + /** Decodes the specified ZIP file. + * @return The path to the output directory. */ + static public String unzip (String zipFile, String outputDir) throws IOException { + if (zipFile == null) throw new IllegalArgumentException("zipFile cannot be null."); + if (outputDir == null) throw new IllegalArgumentException("outputDir cannot be null."); + + if (DEBUG) debug("scar", "ZIP decoding: " + zipFile + " -> " + outputDir); + + ZipInputStream input = new ZipInputStream(new FileInputStream(zipFile)); + try { + byte[] buffer = new byte[4096]; + while (true) { + ZipEntry entry = input.getNextEntry(); + if (entry == null) break; + File file = new File(outputDir, entry.getName()); + if (entry.isDirectory()) { + mkdir(file.getPath()); + continue; + } + mkdir(file.getParent()); + FileOutputStream output = new FileOutputStream(file); + try { + while (true) { + int length = input.read(buffer); + if (length == -1) break; + output.write(buffer, 0, length); + } + } finally { + try { + output.close(); + } catch (Exception ignored) { + } + } + } + } finally { + try { + input.close(); + } catch (Exception ignored) { + } + } + return outputDir; + } + + /** Encodes the specified file with LZMA. The resulting filename is the filename plus ".lzma". The file is deleted after + * encoding. + * @return The path to the encoded file. */ + static public String lzma (String file) throws IOException { + String lzmaFile = lzma(file, file + ".lzma"); + delete(file); + return lzmaFile; + } + + /** Encodes the specified file with LZMA. + * @return The path to the encoded file. */ + static public String lzma (String file, String lzmaFile) throws IOException { + if (file == null) throw new IllegalArgumentException("file cannot be null."); + if (lzmaFile == null) throw new IllegalArgumentException("lzmaFile cannot be null."); + + if (DEBUG) debug("scar", "LZMA encoding: " + file + " -> " + lzmaFile); + + try { + LzmaAlone.main(new String[] {"e", file, lzmaFile}); + } catch (Exception ex) { + throw new IOException("Error lzma compressing file: " + file, ex); + } + return lzmaFile; + } + + /** Decodes the specified LZMA file. The filename must end in ".lzma" and the resulting filename has this stripped. The encoded + * file is deleted after decoding. + * @return The path to the decoded file. */ + static public String unlzma (String lzmaFile) throws IOException { + if (lzmaFile == null) throw new IllegalArgumentException("lzmaFile cannot be null."); + if (!lzmaFile.endsWith(".lzma")) throw new IllegalArgumentException("lzmaFile must end with .lzma: " + lzmaFile); + + String file = unlzma(lzmaFile, substring(lzmaFile, 0, -5)); + delete(lzmaFile); + return file; + } + + /** Decodes the specified LZMA file. + * @return The path to the decoded file. */ + static public String unlzma (String lzmaFile, String file) throws IOException { + if (lzmaFile == null) throw new IllegalArgumentException("lzmaFile cannot be null."); + if (file == null) throw new IllegalArgumentException("file cannot be null."); + + if (DEBUG) debug("scar", "LZMA decoding: " + lzmaFile + " -> " + file); + + try { + LzmaAlone.main(new String[] {"d", lzmaFile, file}); + } catch (Exception ex) { + throw new IOException("Error lzma decompressing file: " + file, ex); + } + return file; + } + + static public String tempFile (String prefix) throws IOException { + return File.createTempFile(prefix, null).getAbsolutePath(); + } + + static public String tempDirectory (String prefix) throws IOException { + File file = File.createTempFile(prefix, null); + if (!file.delete()) throw new IOException("Unable to delete temp file: " + file); + if (!file.mkdir()) throw new IOException("Unable to create temp directory: " + file); + return file.getAbsolutePath(); + } + + /** Splits the specified command at spaces that are not surrounded by quotes and passes the result to {@link #shell(String...)}. */ + static public void shell (String command) throws IOException { + List matchList = new ArrayList(); + Pattern regex = Pattern.compile("[^\\s\"']+|\"([^\"]*)\"|'([^']*)'"); + Matcher regexMatcher = regex.matcher(command); + while (regexMatcher.find()) { + if (regexMatcher.group(1) != null) + matchList.add(regexMatcher.group(1)); + else if (regexMatcher.group(2) != null) + matchList.add(regexMatcher.group(2)); + else + matchList.add(regexMatcher.group()); + } + shell(matchList.toArray(new String[matchList.size()])); + } + + /** Executes the specified shell command. {@link #resolvePath(String)} is used to locate the file to execute. If not found, on + * Windows the same filename with an "exe" extension is also tried. */ + static public void shell (String... command) throws IOException { + if (command == null) throw new IllegalArgumentException("command cannot be null."); + if (command.length == 0) throw new IllegalArgumentException("command cannot be empty."); + + String originalCommand = command[0]; + command[0] = resolvePath(command[0]); + if (!fileExists(command[0]) && isWindows) { + command[0] = resolvePath(command[0] + ".exe"); + if (!fileExists(command[0])) command[0] = originalCommand; + } + + if (TRACE) { + StringBuilder buffer = new StringBuilder(256); + for (String text : command) { + buffer.append(text); + buffer.append(' '); + } + trace("scar", "Executing command: " + buffer); + } + + final Process process = new ProcessBuilder(command).redirectErrorStream(true).start(); + new Thread("shell") { + public void run () { + BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); + try { + while (true) { + String line = reader.readLine(); + if (line == null) break; + System.out.println(line); + } + reader.close(); + } catch (Exception ex) { + ex.printStackTrace(); + } + } + }.start(); + + try { + process.waitFor(); + } catch (InterruptedException ignored) { + } + if (process.exitValue() != 0) { + StringBuilder buffer = new StringBuilder(256); + for (String text : command) { + buffer.append(text); + buffer.append(' '); + } + throw new RuntimeException("Error executing command: " + buffer); + } + } + + /** Reads to the end of the input stream and writes the bytes to the output stream. */ + static public void copyStream (InputStream input, OutputStream output) throws IOException { + if (input == null) throw new IllegalArgumentException("input cannot be null."); + if (output == null) throw new IllegalArgumentException("output cannot be null."); + + try { + byte[] buffer = new byte[4096]; + while (true) { + int length = input.read(buffer); + if (length == -1) break; + output.write(buffer, 0, length); + } + } finally { + try { + output.close(); + } catch (Exception ignored) { + } + try { + input.close(); + } catch (Exception ignored) { + } + } + } + + /** Copies a file, overwriting any existing file at the destination. */ + static public String copyFile (String in, String out) throws IOException { + if (in == null) throw new IllegalArgumentException("in cannot be null."); + if (out == null) throw new IllegalArgumentException("out cannot be null."); + + if (TRACE) trace("scar", "Copying file: " + in + " -> " + out); + + FileChannel sourceChannel = null; + FileChannel destinationChannel = null; + try { + sourceChannel = new FileInputStream(in).getChannel(); + destinationChannel = new FileOutputStream(out).getChannel(); + sourceChannel.transferTo(0, sourceChannel.size(), destinationChannel); + } finally { + try { + if (sourceChannel != null) sourceChannel.close(); + } catch (Exception ignored) { + } + try { + if (destinationChannel != null) destinationChannel.close(); + } catch (Exception ignored) { + } + } + return out; + } + + /** Moves a file, overwriting any existing file at the destination. */ + static public String moveFile (String in, String out) throws IOException { + if (in == null) throw new IllegalArgumentException("in cannot be null."); + if (out == null) throw new IllegalArgumentException("out cannot be null."); + + copyFile(in, out); + delete(in); + return out; + } + + static public String readFile (String file) throws IOException { + return readFile(file, null); + } + + static public String readFile (String fileName, String charset) throws IOException { + if (fileName == null) throw new IllegalArgumentException("file cannot be null."); + File file = new File(fileName); + int fileLength = (int)file.length(); + if (fileLength == 0) fileLength = 512; + StringBuilder output = new StringBuilder(fileLength); + InputStreamReader reader = null; + try { + if (charset == null) + reader = new InputStreamReader(new FileInputStream(file)); + else + reader = new InputStreamReader(new FileInputStream(file), charset); + char[] buffer = new char[256]; + while (true) { + int length = reader.read(buffer); + if (length == -1) break; + output.append(buffer, 0, length); + } + } catch (IOException ex) { + throw new RuntimeException("Error reading layout file: " + fileName, ex); + } finally { + try { + if (reader != null) reader.close(); + } catch (IOException ignored) { + } + } + return output.toString(); + } + + static public void writeFile (String fileName, String contents, boolean append) { + writeFile(fileName, contents, append, null); + } + + static public void writeFile (String fileName, String contents, boolean append, String charset) { + Writer writer = null; + try { + File file = new File(fileName); + FileOutputStream output = new FileOutputStream(file, append); + if (charset == null) + writer = new OutputStreamWriter(output); + else + writer = new OutputStreamWriter(output, charset); + writer.write(contents); + } catch (Exception ex) { + throw new RuntimeException("Error writing file: " + fileName, ex); + } finally { + try { + if (writer != null) writer.close(); + } catch (Exception ignored) { + } + } + } + + /** Deletes a file or directory and all files and subdirecties under it. */ + static public boolean delete (String fileName) { + if (fileName == null) throw new IllegalArgumentException("fileName cannot be null."); + + File file = new File(fileName); + if (file.exists() && file.isDirectory()) { + File[] files = file.listFiles(); + for (int i = 0, n = files.length; i < n; i++) { + if (files[i].isDirectory()) + delete(files[i].getAbsolutePath()); + else { + if (TRACE) trace("scar", "Deleting file: " + files[i]); + files[i].delete(); + } + } + } + if (TRACE) trace("scar", "Deleting file: " + file); + return file.delete(); + } + + /** Creates the directories in the specified path. */ + static public String mkdir (String path) { + if (path == null) throw new IllegalArgumentException("path cannot be null."); + + if (new File(path).mkdirs() && TRACE) trace("scar", "Created directory: " + path); + return path; + } + + /** Returns true if the file exists. */ + static public boolean fileExists (String path) { + if (path == null) throw new IllegalArgumentException("path cannot be null."); + + return new File(path).exists(); + } + + /** Returns the canonical path for the specified path. Eg, if "." is passed, this will resolve the actual path and return it. */ + static public String canonical (String path) { + if (path == null) throw new IllegalArgumentException("path cannot be null."); + + File file = new File(path); + try { + return file.getCanonicalPath(); + } catch (IOException ex) { + file = file.getAbsoluteFile(); + if (file.getName().equals(".")) file = file.getParentFile(); + return file.getPath(); + } + } + + /** Returns the textual contents of the specified file. */ + static public String fileContents (String path) throws IOException { + StringBuilder stringBuffer = new StringBuilder(4096); + FileReader reader = new FileReader(path); + try { + char[] buffer = new char[2048]; + while (true) { + int length = reader.read(buffer); + if (length == -1) break; + stringBuffer.append(buffer, 0, length); + } + } finally { + try { + reader.close(); + } catch (Exception ignored) { + } + } + return stringBuffer.toString(); + } + + /** Returns only the filename portion of the specified path. */ + static public String fileName (String path) { + return new File(canonical(path)).getName(); + } + + /** Returns the parent directory of the specified path. */ + static public String parent (String path) { + return new File(canonical(path)).getParent(); + } + + /** Returns only the extension portion of the specified path, or an empty string if there is no extension. */ + static public String fileExtension (String file) { + if (file == null) throw new IllegalArgumentException("fileName cannot be null."); + + int commaIndex = file.indexOf('.'); + if (commaIndex == -1) return ""; + return file.substring(commaIndex + 1); + } + + /** Returns only the filename portion of the specified path, without the extension, if any. */ + static public String fileWithoutExtension (String file) { + if (file == null) throw new IllegalArgumentException("fileName cannot be null."); + + int commaIndex = file.indexOf('.'); + if (commaIndex == -1) commaIndex = file.length(); + int slashIndex = file.replace('\\', '/').lastIndexOf('/'); + if (slashIndex == -1) + slashIndex = 0; + else + slashIndex++; + return file.substring(slashIndex, commaIndex); + } + + /** Shortcut for System.out.println. */ + static public void log (String text) { + System.out.println(text); + } + + /** Returns a substring of the specified text. + * @param end The end index of the substring. If negative, the index used will be "text.length() + end". */ + static public String substring (String text, int start, int end) { + if (text == null) throw new IllegalArgumentException("text cannot be null."); + + if (end >= 0) return text.substring(start, end); + return text.substring(start, text.length() + end); + } + + static public void jws (String inputDir, String outputDir, boolean pack, String keystoreFile, String alias, String password) + throws IOException { + if (inputDir == null) throw new IllegalArgumentException("inputDir cannot be null."); + if (outputDir == null) throw new IllegalArgumentException("outputDir cannot be null."); + if (keystoreFile == null) throw new IllegalArgumentException("keystoreFile cannot be null."); + if (alias == null) throw new IllegalArgumentException("alias cannot be null."); + if (password == null) throw new IllegalArgumentException("password cannot be null."); + if (password.length() < 6) throw new IllegalArgumentException("password must be 6 or more characters."); + + mkdir(outputDir); + paths(inputDir, "*.jar", "*.jnlp").copyTo(outputDir); + for (String file : paths(outputDir, "*.jar")) + sign(unpack200(pack200(unsign(file))), keystoreFile, alias, password); + if (!pack) return; + for (String file : paths(outputDir, "*.jar", "!*native*")) + gzip(pack200(file)); + } + + static public void jnlp (String inputDir, String mainClass, String mainClassJar, String url, String company, String title, + String splashImage) throws IOException { + if (mainClass == null) throw new IllegalArgumentException("mainClass cannot be null."); + if (url == null) throw new IllegalArgumentException("url cannot be null."); + if (title == null) throw new IllegalArgumentException("title cannot be null."); + if (company == null) throw new IllegalArgumentException("company cannot be null."); + + int firstSlash = url.indexOf("/", 7); + int lastSlash = url.lastIndexOf("/"); + if (firstSlash == -1 || lastSlash == -1) throw new RuntimeException("Invalid url: " + url); + String domain = url.substring(0, firstSlash + 1); + String path = url.substring(firstSlash + 1, lastSlash + 1); + String jnlpFile = url.substring(lastSlash + 1); + + FileWriter writer = new FileWriter(inputDir + jnlpFile); + try { + writer.write("\n"); + writer.write("\n"); + writer.write("\n"); + writer.write("\t" + title + "\n"); + writer.write("\t" + company + "\n"); + writer.write("\t\n"); + writer.write("\t" + title + "\n"); + writer.write("\t" + title + "\n"); + if (splashImage != null) writer.write("\t\n"); + writer.write("\n"); + writer.write("\n"); + writer.write("\t\n"); + writer.write("\n"); + writer.write("\n"); + writer.write("\t\n"); + + // JAR with main class first. + writer.write("\t\n"); + + // Rest of JARs, except natives. + for (String file : paths(inputDir, "**/*.jar", "**/*.jar.pack.lzma", "!*native*", "!**/" + mainClassJar)) + writer.write("\t\n"); + + writer.write("\n"); + Paths nativePaths = paths(inputDir, "*native*win*", "*win*native*"); + if (nativePaths.count() == 1) { + writer.write("\n"); + writer.write("\t\n"); + writer.write("\t\n"); + writer.write("\n"); + } + nativePaths = paths(inputDir, "*native*mac*", "*mac*native*"); + if (nativePaths.count() == 1) { + writer.write("\n"); + writer.write("\t\n"); + writer.write("\t\n"); + writer.write("\n"); + } + nativePaths = paths(inputDir, "*native*linux*", "*linux*native*"); + if (nativePaths.count() == 1) { + writer.write("\n"); + writer.write("\t\n"); + writer.write("\t\n"); + writer.write("\n"); + } + nativePaths = paths(inputDir, "*native*solaris*", "*solaris*native*"); + if (nativePaths.count() == 1) { + writer.write("\n"); + writer.write("\t\n"); + writer.write("\t\n"); + writer.write("\n"); + } + writer.write("\n"); + writer.write(""); + } finally { + try { + writer.close(); + } catch (Exception ignored) { + } + } + } + + static public void lwjglApplet (String inputDir, String outputDir, String keystoreFile, String alias, String password) + throws IOException { + if (inputDir == null) throw new IllegalArgumentException("inputDir cannot be null."); + if (outputDir == null) throw new IllegalArgumentException("outputDir cannot be null."); + if (keystoreFile == null) throw new IllegalArgumentException("keystoreFile cannot be null."); + if (alias == null) throw new IllegalArgumentException("alias cannot be null."); + if (password == null) throw new IllegalArgumentException("password cannot be null."); + if (password.length() < 6) throw new IllegalArgumentException("password must be 6 or more characters."); + + mkdir(outputDir); + paths(inputDir, "**/*.jar", "*.html", "*.htm").flatten().copyTo(outputDir); + for (String jarFile : paths(outputDir, "*.jar")) { + sign(unpack200(pack200(unsign(jarFile))), keystoreFile, alias, password); + String fileName = fileName(jarFile); + if (fileName.equals("lwjgl_util_applet.jar") || fileName.equals("lzma.jar")) continue; + if (fileName.contains("native")) + lzma(jarFile); + else + lzma(pack200(jarFile)); + } + } + + static public void lwjglAppletHtml (String inputDir, String mainClass) throws IOException { + if (INFO) info("scar", "Generating: applet.html"); + FileWriter writer = new FileWriter(inputDir + "applet.html"); + try { + writer.write("\n"); + writer.write("Applet\n"); + writer.write("\n"); + writer + .write("\n"); + writer.write("\n"); + writer.write("\n"); + writer.write("\n"); + writer.write("\n"); + Paths nativePaths = paths(inputDir, "*native*win*.jar.lzma", "*win*native*.jar.lzma"); + if (nativePaths.count() == 1) writer.write("\n"); + nativePaths = paths(inputDir, "*native*mac*.jar.lzma", "*mac*native*.jar.lzma"); + if (nativePaths.count() == 1) writer.write("\n"); + nativePaths = paths(inputDir, "*native*linux*.jar.lzma", "*linux*native*.jar.lzma"); + if (nativePaths.count() == 1) writer.write("\n"); + nativePaths = paths(inputDir, "*native*solaris*.jar.lzma", "*solaris*native*.jar.lzma"); + if (nativePaths.count() == 1) writer.write("\n"); + writer.write("\n"); + writer.write("\n"); + writer.write("\n"); + writer + .write("\n"); + writer.write("\n"); + writer.write("\n"); + } finally { + try { + writer.close(); + } catch (Exception ignored) { + } + } + } + + static public Paths path (String file) { + return new Paths().addFile(file); + } + + static public Paths path (String dir, String file) { + return new Paths().addFile(file); + } + + static public Paths paths (String dir, String... patterns) { + return new Paths(dir, patterns); + } + + static public void compile (Paths source, Paths classpath, String outputDir) { + if (source.isEmpty()) { + if (WARN) warn("scar", "No source files found."); + return; + } + + ArrayList args = new ArrayList(); + if (TRACE) args.add("-verbose"); + args.add("-d"); + args.add(outputDir); + args.add("-g:source,lines"); + args.add("-source"); + args.add("1.6"); + args.add("-target"); + args.add("1.6"); + args.addAll(source.getPaths()); + if (classpath != null && !classpath.isEmpty()) { + args.add("-classpath"); + args.add(isWindows ? classpath.toString(";") : classpath.toString(":")); + } + + JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); + if (compiler == null) + throw new RuntimeException("No compiler available. Ensure you are running from a 1.6+ JDK, and not a JRE."); + if (compiler.run(null, null, null, args.toArray(new String[args.size()])) != 0) { + throw new RuntimeException("Error during compilation.\nSource: " + source.count() + " files\nClasspath: " + classpath); + } + try { + Thread.sleep(100); + } catch (InterruptedException ex) { + } + } + + static public void jwsHtaccess (String jwsDir) throws IOException { + for (String packedFile : paths(jwsDir + "packed", "*.jar.pack.gz")) { + String packedFileName = fileName(packedFile); + String jarFileName = substring(packedFileName, 0, -8); + FileWriter writer = new FileWriter(jwsDir + jarFileName + ".var"); + try { + writer.write("URI: packed/" + packedFileName + "\n"); + writer.write("Content-Type: x-java-archive\n"); + writer.write("Content-Encoding: pack200-gzip\n"); + writer.write("URI: unpacked/" + jarFileName + "\n"); + writer.write("Content-Type: x-java-archive\n"); + } finally { + try { + writer.close(); + } catch (Exception ignored) { + } + } + } + FileWriter writer = new FileWriter(jwsDir + ".htaccess"); + try { + writer.write("AddType application/x-java-jnlp-file .jnlp"); // JNLP mime type. + writer.write("AddType application/x-java-archive .jar\n"); // JAR mime type. + writer.write("AddHandler application/x-type-map .var\n"); // Enable type maps. + writer.write("Options +MultiViews\n"); + writer.write("MultiViewsMatch Any\n"); // Apache 2.0 only. + writer.write("\n"); + writer.write("AddEncoding pack200-gzip .jar\n"); // Enable Content-Encoding header for .jar.pack.gz files. + writer.write("RemoveEncoding .gz\n"); // Prevent mod_gzip from messing with the Content-Encoding response. + writer.write("\n"); + } finally { + try { + writer.close(); + } catch (Exception ignored) { + } + } + } + + static public void executeCode (String code, HashMap parameters) { + executeCode(code, parameters); + } + + /** Compiles and executes the speFcified Java code. The code is compiled as if it were a Java method body. + *

+ * Imports statements can be used before any code. These imports are automatically used:
+ * import com.esotericsoftware.scar.Scar;
+ * import com.esotericsoftware.wildcard.Paths;
+ * import com.esotericsoftware.minlog.Log;
+ * import static com.esotericsoftware.scar.Scar.*;
+ * import static com.esotericsoftware.minlog.Log.*;
+ *

+ * Entries can be added to the classpath by using "classpath [url];" statements at the start of the code. These classpath + * entries are checked before the classloader that loaded the Scar class is checked. Examples:
+ * classpath someTools.jar;
+ * classpath some/directory/of/class/files;
+ * classpath http://example.com/someTools.jar;
+ * If a project parameter is not null, non-absolute classpath entries will be relative to the project directory. + * @param parameters These parameters will be available in the scope where the code is executed. */ + static public void executeCode (String code, HashMap parameters, Project project) { + JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); + if (compiler == null) + throw new RuntimeException("No compiler available. Ensure you are running from a 1.6+ JDK, and not a JRE."); + + try { + // Wrap code in a class. + StringBuilder classBuffer = new StringBuilder(2048); + classBuffer.append("import com.esotericsoftware.scar.*;\n"); + classBuffer.append("import com.esotericsoftware.minlog.Log;\n"); + classBuffer.append("import com.esotericsoftware.wildcard.Paths;\n"); + classBuffer.append("import static com.esotericsoftware.scar.Scar.*;\n"); + classBuffer.append("import static com.esotericsoftware.minlog.Log.*;\n"); + classBuffer.append("public class Generated {\n"); + int templateStartLines = 6; + classBuffer.append("public void execute ("); + int i = 0; + for (Entry entry : parameters.entrySet()) { + if (i++ > 0) classBuffer.append(','); + classBuffer.append('\n'); + templateStartLines++; + classBuffer.append(entry.getValue().getClass().getName()); + classBuffer.append(' '); + classBuffer.append(entry.getKey()); + } + classBuffer.append("\n) throws Exception {\n"); + templateStartLines += 2; + + // Append code, collecting imports statements and classpath URLs. + StringBuilder importBuffer = new StringBuilder(512); + ArrayList classpathURLs = new ArrayList(); + BufferedReader reader = new BufferedReader(new StringReader(code)); + boolean header = true; + while (true) { + String line = reader.readLine(); + if (line == null) break; + String trimmed = line.trim(); + if (header && trimmed.startsWith("import ") && trimmed.endsWith(";")) { + importBuffer.append(line); + importBuffer.append('\n'); + } else if (header && trimmed.startsWith("classpath ") && trimmed.endsWith(";")) { + String path = substring(line.trim(), 10, -1); + try { + classpathURLs.add(new URL(path)); + } catch (MalformedURLException ex) { + if (project != null) classpathURLs.add(new File(project.path(path)).toURI().toURL()); + } + } else { + if (trimmed.length() > 0) header = false; + classBuffer.append(line); + classBuffer.append('\n'); + } + } + classBuffer.append("}}"); + + final String classCode = importBuffer.append(classBuffer).toString(); + if (TRACE) trace("scar", "Executing code:\n" + classCode); + + // Construct classpath option. + List options = new ArrayList(); + { + StringBuffer buffer = new StringBuffer(System.getProperty("java.class.path")); + String pathSeparator = System.getProperty("path.separator"); + for (URL url : classpathURLs) { + buffer.append(pathSeparator); + buffer.append(new File(url.toURI()).getCanonicalPath()); + } + if (TRACE) trace("Using classpath: " + buffer); + options.add("-classpath"); + options.add(buffer.toString()); + } + + // Compile class. + final ByteArrayOutputStream output = new ByteArrayOutputStream(32 * 1024); + final SimpleJavaFileObject javaObject = new SimpleJavaFileObject(URI.create("Generated.java"), Kind.SOURCE) { + public OutputStream openOutputStream () { + return output; + } + + public CharSequence getCharContent (boolean ignoreEncodingErrors) { + return classCode; + } + }; + DiagnosticCollector diagnostics = new DiagnosticCollector(); + compiler.getTask(null, new ForwardingJavaFileManager(compiler.getStandardFileManager(null, null, null)) { + public JavaFileObject getJavaFileForOutput (Location location, String className, Kind kind, FileObject sibling) { + return javaObject; + } + }, diagnostics, options, null, Arrays.asList(new JavaFileObject[] {javaObject})).call(); + + if (!diagnostics.getDiagnostics().isEmpty()) { + StringBuilder buffer = new StringBuilder(1024); + for (Diagnostic diagnostic : diagnostics.getDiagnostics()) { + if (buffer.length() > 0) buffer.append("\n"); + buffer.append("Line "); + buffer.append(diagnostic.getLineNumber() - templateStartLines); + buffer.append(": "); + buffer.append(diagnostic.getMessage(null).replaceAll("^Generated.java:\\d+:\\d* ", "")); + } + throw new RuntimeException("Compilation errors:\n" + buffer); + } + + // Load class. + Class generatedClass = new URLClassLoader(classpathURLs.toArray(new URL[classpathURLs.size()]), + Scar.class.getClassLoader()) { + protected synchronized Class loadClass (String name, boolean resolve) throws ClassNotFoundException { + // Look in this classloader before the parent. + Class c = findLoadedClass(name); + if (c == null) { + try { + c = findClass(name); + } catch (ClassNotFoundException e) { + return super.loadClass(name, resolve); + } + } + if (resolve) resolveClass(c); + return c; + } + + protected Class findClass (String name) throws ClassNotFoundException { + if (name.equals("Generated")) { + byte[] bytes = output.toByteArray(); + return defineClass(name, bytes, 0, bytes.length); + } + return super.findClass(name); + } + }.loadClass("Generated"); + + // Execute. + Class[] parameterTypes = new Class[parameters.size()]; + Object[] parameterValues = new Object[parameters.size()]; + i = 0; + for (Object object : parameters.values()) { + parameterValues[i] = object; + parameterTypes[i++] = object.getClass(); + } + generatedClass.getMethod("execute", parameterTypes).invoke(generatedClass.newInstance(), parameterValues); + } catch (Throwable ex) { + throw new RuntimeException("Error executing code:\n" + code.trim(), ex); + } + } + + static public boolean ftpUpload (String server, String user, String password, String dir, Paths paths, boolean passive) + throws IOException { + FTPClient ftp = new FTPClient(); + InetAddress address = InetAddress.getByName(server); + if (DEBUG) debug("scar", "Connecting to FTP server: " + address); + ftp.connect(address); + if (passive) ftp.enterLocalPassiveMode(); + if (!ftp.login(user, password)) { + if (ERROR) error("scar", "FTP login failed for user: " + user); + return false; + } + if (!ftp.changeWorkingDirectory(dir)) { + if (ERROR) error("scar", "FTP directory change failed: " + dir); + return false; + } + ftp.setFileType(org.apache.commons.net.ftp.FTP.BINARY_FILE_TYPE); + for (String path : paths) { + if (INFO) info("scar", "FTP upload: " + path); + BufferedInputStream input = new BufferedInputStream(new FileInputStream(path)); + try { + ftp.storeFile(new File(path).getName(), input); + } finally { + try { + input.close(); + } catch (Exception ignored) { + } + } + } + ftp.logout(); + ftp.disconnect(); + return true; + } + + static public void sftpUpload (String server, String user, String password, String dir, Paths paths) throws IOException { + Session session = null; + ChannelSftp channel = null; + try { + session = new JSch().getSession(user, server, 22); + session.setConfig("StrictHostKeyChecking", "no"); + session.setPassword(password); + session.connect(); + + channel = (ChannelSftp)session.openChannel("sftp"); + channel.connect(); + channel.cd(dir); + for (String path : paths) { + if (INFO) info("scar", "SFTP upload: " + path); + final File file = new File(path); + BufferedInputStream input = new BufferedInputStream(new FileInputStream(file)); + try { + channel.put(input, new File(path).getName(), new SftpProgressMonitor() { + private long total, interval = Math.max(1, file.length() / 76), lastCount; + + public void init (int op, String source, String dest, long max) { + System.out.print("|-"); + } + + public void end () { + System.out.println("|"); + } + + public boolean count (long count) { + total += count; + while (total - lastCount >= interval) { + lastCount += interval; + System.out.print("-"); + } + return true; + } + }); + } finally { + try { + input.close(); + } catch (Exception ignored) { + } + } + } + } catch (Exception ex) { + throw new IOException(ex); + } finally { + try { + if (session != null) session.disconnect(); + if (channel != null) channel.exit(); + } catch (Exception ignored) { + } + } + } + + static public void ssh (String server, String user, String password, String command) throws IOException { + if (INFO) info("scar", "SSH: " + command); + Session session = null; + ChannelExec channel = null; + try { + session = new JSch().getSession(user, server, 22); + session.setConfig("StrictHostKeyChecking", "no"); + session.setPassword(password); + session.connect(); + channel = (ChannelExec)session.openChannel("exec"); + channel.setCommand(command); + channel.setInputStream(null); + channel.setErrStream(System.err); + InputStream in = channel.getInputStream(); + channel.connect(); + byte[] buffer = new byte[1024]; + while (true) { + while (in.available() > 0) { + int count = in.read(buffer, 0, 1024); + if (count < 0) break; + System.out.print(new String(buffer, 0, count)); + } + if (channel.isClosed()) { + System.out.println("Exit: " + channel.getExitStatus()); + break; + } + try { + Thread.sleep(100); + } catch (Exception ee) { + } + } + } catch (Exception ex) { + throw new IOException(ex); + } finally { + try { + if (session != null) session.disconnect(); + if (channel != null) channel.disconnect(); + } catch (Exception ignored) { + } + } + } + + static public ArrayList list (Object... objects) { + if (objects == null) throw new IllegalArgumentException("objects cannot be null."); + ArrayList list = new ArrayList(objects.length); + for (Object o : objects) + list.add(o); + return list; + } + + private Scar () { + } + + static public void main (String[] args) throws IOException { + Scar.args = new Arguments(args); + + if (Scar.args.has("trace")) + TRACE(); + else if (Scar.args.has("debug")) + DEBUG(); + else if (Scar.args.has("info")) + INFO(); + else if (Scar.args.has("warn")) + WARN(); + else if (Scar.args.has("error")) // + ERROR(); + + // BOZO - Add something that can execute scar methods! + } +}