All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.xmlvm.main.Arguments Maven / Gradle / Ivy

There is a newer version: 0.96-beta4
Show newest version
/* Copyright (c) 2002-2011 by XMLVM.org
 *
 * Project Info:  http://www.xmlvm.org
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
 * (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
 * USA.
 */

package org.xmlvm.main;

import java.io.File;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;

import java.util.Set;
import java.util.StringTokenizer;
import org.xmlvm.Log;
import org.xmlvm.proc.lib.Libraries;

/**
 * This class parses the arguments given in a string array and makes them easily
 * accessible for the application to use.
 */
public class Arguments
{
	// The arguments that are given by the user on the command line.

	public static final String ARG_IN= "--in=";
	public static final String ARG_OUT= "--out=";
	public static final String ARG_TARGET= "--target=";
	public static final String ARG_RESOURCE= "--resource=";
	public static final String ARG_LIB= "--lib=";
	public static final String ARG_DEPS= "--deps=";
	public static final String ARG_APP_NAME= "--app-name=";
	public static final String ARG_QX_MAIN= "--qx-main=";
	public static final String ARG_QX_DEBUG= "--qx-debug";
	public static final String ARG_QX_NO_GENERATE= "--qx-no-generate";
	public static final String ARG_DEBUG= "--debug=";
	public static final String ARG_VERSION= "--version";
	public static final String ARG_GEN_NATIVE_SKELETONS= "--gen-native-skeletons";
	public static final String ARG_HELP= "--help";
	public static final String ARG_SKELETON= "--skeleton=";
	// These are obsolete arguments, being here for compatibility reasons
	public static final String ARG_IPHONE_APP= "--iphone-app=";
	public static final String ARG_QX_APP= "--qx-app=";
	public static final String ARG_QUIET= "--quiet";
	// This is just temporary for activating the new DEX processing.
	public static final String ARG_USE_JVM= "--use-jvm";
	// Enables/disables the dependency resolution feature.
	public static final String ARG_LOAD_DEPENDENCIES= "--load-dependencies";
	public static final String ARG_DISABLE_LOAD_DEPENDENCIES= "--disable-load-dependencies";
	public static final String ARG_REDLIST= "--redlist=";
	public static final String ARG_GREENLIST= "--greenlist=";
	public static final String ARG_REFLECTION_CLASS_LIST= "--reflection-class-list=";
	// Enables reference counting for DEX input.
	public static final String ARG_ENABLE_REF_COUNTING= "--enable-ref-counting";
	// Enables a debug counter for measuring execution time.
	public static final String ARG_ENABLE_TIMER= "--enable-timer";
	public static final String ARG_C_SOURCE_EXTENSION= "--c-source-extension=";
	public static final String ARG_NO_CACHE= "--no-cache";
	public static final String ARG_MOBILE= "--mobile";
	// This argument will store various properties to XMLVM
	// An example of these values can be found in the long help
	public static final String ARG_PROPERTY= "-D";

	public static final String ARG_XMLVM_NEW_IOS_API= "--xmlvm-new-ios-api";

	// The parsed values will be stored here.
	private List option_in= new ArrayList();
	private String option_out= null;
	private Targets option_target= Targets.NONE;
	private boolean option_gen_native_skeletons= false;
	private Set option_resource= new HashSet();
	private Set option_lib= new HashSet();
	private Set option_deps= new HashSet();
	private String option_app_name= null;
	private String option_qx_main= null;
	private boolean option_qx_debug= false;
	private boolean option_qx_no_generate= false;
	private Log.Level option_debug= Log.Level.WARNING;
	private String option_skeleton= null;
	private boolean option_use_jvm= false;
	private boolean option_load_dependencies= false;
	private boolean option_disable_load_dependencies= false;
	private String option_redlist= null;
	private String option_greenlist= null;
	private String option_reflection_class_list= null;
	private boolean option_enable_ref_counting= false;
	private boolean option_enable_timer= false;
	private String option_c_source_extension= "c";
	private boolean option_no_cache= false;
	private boolean option_mobile= false;
	private Map option_property= new HashMap();
	private boolean option_xmlvm_new_ios_api= false;

	private static final String[] shortUsage= { "Usage: ", "xmlvm [--in= [--out=]]", "      [--deps=]", "      [--target=[xmlvm|dexmlvm|jvm|clr|dfa|class|exe|dex|js|java|c|python|objc|iphone|qooxdoo|vtable|webos|csharp]]", "      [--skeleton=]", "      [--lib=", "      [--app-name=]", "      [--resource=]", "      [--qx-main= [--qx-debug]]", "      [--debug=[none|error|warning|all]]", "      [--version] [--help]" };
	private static final String[] longUsage= { "Detailed usage:", "===============", "", " --in=       Pathname of input files. Can be *.class *.exe and *.xmlvm", "", " --out=       Directory of output files (defaults to \".\")", "", " --target= Desired target, could be one of the following:", "    xmlvm            XMLVM output, depending on the input (default)", "    dexmlvm          XMLVM_dex output", "    jvm              XMLVM_jvm output", "    clr              XMLVM_clr output", "    dfa              Data Flow Analysis on input files", "    class            Java class bytecode", "    exe              .NET executable", "    dex              DEX bytecode", "    js               JavaScript", "    java             Java source code", "    c                C source code", "    gen-c-wrappers   Generates C wrappers while preserving hand-written code from overridden files in the 'out' directory.", "    python           Python",
	        "    objc             Objective C source code", "    iphone           iPhone Objective-C", "    qooxdoo          JavaScript Qooxdoo web application", "    vtable           Vtable calculation (pre-step for e.g. C generation)", "    webos            WebOS JavaScript Project", "    csharp           C# source code", "", " --deps=  Additional dependencies such as libraries your app is depending on.", "                     Only the classes your app is depending on are actually cross-compiled.", "                     A dependency can be a classpath folder on the file system or a JAR file.", "", "--gen-native-skeletons Generates skeletons for Java native methods in the target", "                   language (currently only available for --target=c", "", " --skeleton= Skeleton to create a new template project:", "    iphone           iPhone project skeleton",
	        "    iphone-hybrid    iPhone project skeleton with a cross compiled part (in 'src-common') and a native part ('in 'src-ios')", "    android          Android/iPhone project skeleton", "    android:migrate  Migrate an existing Android project to XMLVM (needs project created by 'android create project' command)", "    iphone:update    Update an existing XMLVM/iPhone project to latest build scripts", "    android:update   Update an existing XMLVM/Android project to latest build scripts", "", " --lib= Comma separated list of extra libraries required for the specified target. Use a tilde at the and of the library name, to mark it as 'Weak'.", "    android          Support of android applications", "    .dylib      iPhone dynamic library ", "    .Framework  iPhone framework ", "", " --app-name= Application name, required for iphone, android-based and qooxdoo targets", "",
	        " --resource= " + (File.pathSeparatorChar == ':' ? "Colon" : "Semicolon") + " separated list of external non parsable files and directories. Used in iphone-based templates to register auxilliary files. If this argument ends with '/', then the contents of this directory will be copied. If it is a directory and does not end with '/', then a verbatim copy of the directory will be performed. This argument can also be used to add extra C/C++/Obj-C source files in the produced Xcode project.", "", " --qx-main= Entry point of Qooxdoo application", "", " --qx-debug        Create debug information of Qooxdoo target", "", " --qx-no-generate  Don't invoke the Qooxdoo 'generate.py' script. Use this option if you need to copy hand written Qooxdoo JavaScript into place before performing a Qooxdoo build.", "", " -Dkey=value       Set an Xcode property", "   XcodeProject       Template to use for Xcode project:", "     iphone             iPhone project skeleton",
	        "     ipad               iPad project skeleton", "     ios                iPhone and iPad project skeleton", "     iphone3            Legacy iPhone 3.1 project skeleton", "   BundleIdentifier   The value of CFBundleIdentifier in Info.plist", "   BundleVersion      The value of CFBundleVersion in Info.plist", "   BundleDisplayName  The value of CFBundleDisplayName in Info.plist", "   MainNib            The value of NSMainNibFile in Info.plist", "   MainStoryboard     The value of UIMainStoryboardFile in Info.plist", "   PrerenderedIcon    The iPhone application icon is already pre-rendered", "   FileSharingEnabled If iTunes file sharing should be enabled (value is 'true') or not (value is 'false')", "   StatusBarHidden    Hide (value is 'true') or display (value is 'false' status bar", "   ApplicationExits   Application does not run in background on suspend",
	        "   InterfaceOrientation Initial interface orientation. Should be one of: UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight", "   SupportedInterfaceOrientations Colon seperated list of supported interface orientations. See property InterfaceOrientation.", "   AppFonts           Colon separated list of custom fonts", "--mobile    Applicable only to the qooxdoo target - creates a qooxdoo mobile application rather than a desktop one.", "   InjectedInfoPlist  Raw XML that will be injected into Info.plist", "", " --debug=   Debug information level", "    none             Be completely quiet, no information is printed", "    error            Only errors will be printed", "    warning          Warning and errors will be printed", "    all              All debug information (including errors and warnings)", "", " --version         Display version information", "",
	        " --help            This message", "" };
	private static final String[] Version= { "XMLVM 2", "Note: Not all command line arguments activated yet." };
	private boolean performSanityChecks= true;

	public static void printVersion()
	{
		printText(Version, System.out);
		System.exit(0);
	}

	private static void longUsage()
	{
		System.out.println("XMLVM: a flexible and extensible cross-compiler toolchain");
		printText(Version, System.out);
		System.out.println();
		printText(shortUsage, System.out);
		System.out.println();
		printText(longUsage, System.out);
		System.exit(0);
	}

	private static void parseError(String error)
	{
		System.err.println("Error: " + error);
		printText(shortUsage, System.err);
		System.err.println("Give --help parameter to see more detailed command line instructions.");
		System.exit(-1);
	}

	/**
	 * Creates a new instance that will parse the arguments of the given array.
	 */
	public Arguments(String[] argv)
	{
		this(argv, true);
	}

	/**
	 * Creates a new instance that will parse the arguments of the given array.
	 */
	public Arguments(String[] argv, boolean performSanityChecks)
	{
		// Add default properties
		option_property.put("xcodeproject", "iphone");
		option_property.put("bundleidentifier", "org.xmlvm.iphone.XMLVM_APP");
		option_property.put("bundleversion", "1.0");
		option_property.put("bundledisplayname", "XMLVM_APP");
		option_property.put("statusbarhidden", "false");
		option_property.put("prerenderedicon", "false");
		option_property.put("applicationexits", "true");
		option_property.put("interfaceorientation", "UIInterfaceOrientationPortrait");
		option_property.put("filesharingenabled", "false");
		option_property.put("injectedinfoplist", "");
		// Add default libraries
		option_lib.add("Foundation.framework");
		option_lib.add("UIKit.framework");
		option_lib.add("CoreGraphics.framework");
		option_lib.add("AVFoundation.framework~");
		option_lib.add("OpenGLES.framework~");
		option_lib.add("QuartzCore.framework~");
		option_lib.add("MessageUI.framework~");
		option_lib.add("MediaPlayer.framework~");
		option_lib.add("StoreKit.framework~");
		option_lib.add("CoreLocation.framework~");
		option_lib.add("MapKit.framework~");
		option_lib.add("GameKit.framework~");
		option_lib.add("iAd.framework~");
		option_lib.add("AudioToolbox.framework~");
		option_lib.add("CoreMotion.framework");
		option_lib.add("QuickLook.framework~");
		option_lib.add("CoreMedia.framework~");

		// Read command line arguments
		for (int i= 0; i < argv.length; i++)
		{
			String arg= argv[i];
			if (arg.startsWith(ARG_IN))
			{
				option_in.add(arg.substring(ARG_IN.length()));
			}
			else if (arg.startsWith(ARG_OUT))
			{
				if (option_out != null)
					parseError("--out can only be used once");
				option_out= arg.substring(ARG_OUT.length());
			}
			else if (arg.startsWith(ARG_TARGET))
			{
				if (option_target != Targets.NONE)
					parseError("--target can only be specified once");
				String target= arg.substring(ARG_TARGET.length());
				option_target= Targets.getTarget(target);
				if (option_target == null)
					parseError("Unkown target: " + target);
				if (option_target.affinity != Targets.Affinity.TARGET)
					parseError("Not valid target: " + target + ". Consider using --skeleton argument.");
			}
			else if (arg.startsWith(ARG_RESOURCE))
			{
				parseListArgument(arg.substring(ARG_RESOURCE.length()), option_resource, File.pathSeparator);
			}
			else if (arg.equals(ARG_GEN_NATIVE_SKELETONS))
			{
				option_gen_native_skeletons= true;
			}
			else if (arg.startsWith(ARG_LIB))
			{
				parseListArgument(arg.substring(ARG_LIB.length()), option_lib, ",");
			}
			else if (arg.startsWith(ARG_DEPS))
			{
				parseListArgument(arg.substring(ARG_DEPS.length()), option_deps, ",");
			}
			else if (arg.startsWith(ARG_APP_NAME))
			{
				option_app_name= arg.substring(ARG_APP_NAME.length());
			}
			else if (arg.startsWith(ARG_QX_MAIN))
			{
				option_qx_main= arg.substring(ARG_QX_MAIN.length());
			}
			else if (arg.equals(ARG_QX_DEBUG))
			{
				option_qx_debug= true;
			}
			else if (arg.equals(ARG_QX_NO_GENERATE))
			{
				option_qx_no_generate= true;
			}
			else if (arg.equals(ARG_VERSION))
			{
				printVersion();
			}
			else if (arg.startsWith(ARG_DEBUG))
			{
				option_debug= Log.Level.getLevel(arg.substring(ARG_DEBUG.length()));
			}
			else if (arg.equals(ARG_HELP))
			{
				longUsage();
			}
			else if (arg.startsWith(ARG_SKELETON))
			{
				if (option_skeleton != null)
					parseError("--skeleton can only be specified once");
				option_skeleton= arg.substring(ARG_SKELETON.length()).toLowerCase();
				// Obsolete arguments
			}
			else if (arg.startsWith(ARG_IPHONE_APP))
			{
				option_app_name= arg.substring(ARG_IPHONE_APP.length());
			}
			else if (arg.startsWith(ARG_QX_APP))
			{
				option_app_name= arg.substring(ARG_QX_APP.length());
			}
			else if (arg.equals(ARG_QUIET))
			{
				option_debug= Log.Level.ERROR;
			}
			else if (arg.equals(ARG_USE_JVM))
			{
				option_use_jvm= true;
			}
			else if (arg.equals(ARG_LOAD_DEPENDENCIES))
			{
				option_load_dependencies= true;
			}
			else if (arg.equals(ARG_DISABLE_LOAD_DEPENDENCIES))
			{
				option_disable_load_dependencies= true;
			}
			else if (arg.startsWith(ARG_REDLIST))
			{
				option_redlist= arg.substring(ARG_REDLIST.length());
			}
			else if (arg.startsWith(ARG_GREENLIST))
			{
				option_greenlist= arg.substring(ARG_GREENLIST.length()); // TODO: Allow multiple greenlists
			}
			else if (arg.startsWith(ARG_REFLECTION_CLASS_LIST))
			{
				option_reflection_class_list= arg.substring(ARG_REFLECTION_CLASS_LIST.length());
			}
			else if (arg.equals(ARG_ENABLE_TIMER))
			{
				option_enable_timer= true;
			}
			else if (arg.equals(ARG_ENABLE_REF_COUNTING))
			{
				option_enable_ref_counting= true;
			}
			else if (arg.startsWith(ARG_C_SOURCE_EXTENSION))
			{
				option_c_source_extension= arg.substring(ARG_C_SOURCE_EXTENSION.length());
			}
			else if (arg.equals(ARG_NO_CACHE))
			{
				option_no_cache= true;
			}
			else if (arg.startsWith(ARG_PROPERTY))
			{
				String value= arg.substring(ARG_PROPERTY.length());
				int equal= value.indexOf("=");
				if (equal < 1)
				{
					parseError("Unable to parse kay/value: " + value);
				}
				option_property.put(value.substring(0, equal).toLowerCase(), value.substring(equal + 1));
			}
			else if (arg.equals(ARG_XMLVM_NEW_IOS_API))
			{
				option_xmlvm_new_ios_api= true;
			}
			else if (arg.equals(ARG_MOBILE))
			{
				option_mobile= true;
			}
			else if (arg.length() == 0)
			{
				// Ignore empty arguments
			}
			else
			{
				parseError("Unknown parameter: " + arg);
			}
		}

		this.performSanityChecks= performSanityChecks;
		if (performSanityChecks)
		{
			// Sanity check command line arguments
			performSanityChecks();
		}

		// Add additional libraries the app might depend on.
		for (String library : option_deps)
		{
			if (!Libraries.addLibrary(library))
			{
				System.exit(-1);
			}
		}
	}

	private void performSanityChecks()
	{
		if (option_skeleton != null && option_target != Targets.NONE)
		{
			parseError("Only one argument of '--target' or '--skeleton' is allowed");
		}
		if (option_gen_native_skeletons && option_target != Targets.C && option_target != Targets.GENCWRAPPERS && option_target != Targets.CSHARP && option_target != Targets.GENCSHARPWRAPPERS)
		{
			parseError("--gen-native-skeletons only available for targets 'c','gen-c-wrappers','csharp' and 'gen-csharp-wrappers'.");
		}

		if ((option_target == Targets.POSIX || option_target == Targets.IPHONE || option_target == Targets.IPHONEC || option_target == Targets.WEBOS || option_skeleton != null) && option_app_name == null)
		{
			option_app_name= guessAppName();
			if (option_app_name == null)
				parseError("Required parameter: --app-name");
		}

		if ("".equals(option_skeleton)) // empty existing skeleton definition
			parseError("--skeleton option is empty");
		if (option_skeleton != null)
		{
			option_target= Targets.getTarget(option_skeleton + "template");
			if (option_target == null)
				parseError("Unknown skeleton: " + option_skeleton);
			// Clearing all inputs will force EmptyInputProcess to be used.
			option_in.clear();
		}

		if (option_target == Targets.NONE)
			option_target= Targets.XMLVM;

		if (option_lib.contains("android"))
		{
			option_lib.remove("android");
			if (option_target == Targets.IPHONE)
			{
				parseError("The ObjC-based Android to iPhone conversion is no longer supported.");
				return;
			}
			else if (option_target == Targets.IPHONEC)
			{
				option_target= Targets.IPHONECANDROID;
			}
			else if (option_target == Targets.WEBOS)
			{
			}
			else
			{
				parseError("--lib=android is meaningless when not --target=[iphone|iphonec|webos]");
			}
		}
		// // Due to default libraries provided, this check can not be performed
		// if (option_lib.size() > 0 && option_target != Targets.IPHONE)
		// parseError("--lib=" + option_lib.iterator().next() +
		// " is not supported for this target");

		// Only skeleton creation mode supports empty inputs.
		if ((option_skeleton == null || option_skeleton.equals("")) && option_in.isEmpty())
			parseError("Need at least one --in argument");
		if (option_target == Targets.QOOXDOO && option_app_name != null && option_qx_main == null)
			parseError("--target=qooxdoo with --qx-app requires --qx-main");
		if (option_debug == null)
			parseError("Unknown --debug level");

		// We need to enforce reference counting for these targets.
		if (option_target == Targets.OBJC || option_target == Targets.IPHONE)
		{
			if (!option_enable_ref_counting)
			{
				option_enable_ref_counting= true;
				Log.debug("Forcing " + ARG_ENABLE_REF_COUNTING + " for target " + option_target);
			}
		}

		if (option_target == Targets.IPHONE || option_target == Targets.IPHONEC || option_target == Targets.IPHONECANDROID)
		{
			option_c_source_extension= "m";
		}

		// Enables the dependency loading for the specified targets.
		if (option_target == Targets.POSIX || option_target == Targets.IPHONEC || option_target == Targets.IPHONECANDROID || option_target == Targets.CSHARP || option_target == Targets.WP7 || option_target == Targets.WP7ANDROID || option_target == Targets.SDLANDROID)
		{
			if (!option_disable_load_dependencies)
			{
				option_load_dependencies= true;
			}
		}
	}

	private static void parseListArgument(String argument, Set option, String separator)
	{
		if (argument == null || option == null || separator == null)
		{
			return;
		}
		StringTokenizer tk= new StringTokenizer(argument, separator);
		while (tk.hasMoreTokens())
		{
			String entry= tk.nextToken().trim();
			if (!entry.equals(""))
			{
				boolean status= true;
				if (entry.startsWith("+"))
				{
					entry= entry.substring(1);
				}
				else if (entry.startsWith("-"))
				{
					status= false;
					entry= entry.substring(1);
				}
				if (!entry.equals(""))
				{
					if (status)
					{
						option.add(entry);
					}
					else
					{
						option.remove(entry);
					}
				}
			}
		}
	}

	public List option_in()
	{
		return option_in;
	}

	public String option_out()
	{
		// Lazy definition of "smart" option_out parameter, so the warning will
		// appear only when needed
		if (option_out == null)
		{
			if (option_app_name == null)
				option_out= ".";
			else
			{
				option_out= option_app_name;
			}
			if (performSanityChecks)
			{
				Log.warn("Using '" + option_out + "' as output directory");
			}
		}
		return option_out;
	}

	public Set option_resource()
	{
		return option_resource;
	}

	public Targets option_target()
	{
		return option_target;
	}

	public boolean option_gen_native_skeletons()
	{
		return option_gen_native_skeletons;
	}

	public String option_app_name()
	{
		return option_app_name;
	}

	public String option_qx_main()
	{
		return option_qx_main;
	}

	public boolean option_qx_debug()
	{
		return option_qx_debug;
	}

	public boolean option_qx_no_generate()
	{
		return option_qx_no_generate;
	}

	public Log.Level option_debug()
	{
		return option_debug;
	}

	public boolean option_use_jvm()
	{
		return option_use_jvm;
	}

	public boolean option_load_dependencies()
	{
		return option_load_dependencies;
	}

	public boolean option_disable_load_dependencies()
	{
		return option_disable_load_dependencies;
	}

	public String option_redlist()
	{
		return option_redlist;
	}

	public String option_greenlist()
	{
		return option_greenlist;
	}

	public String option_reflection_class_list()
	{
		return option_reflection_class_list;
	}

	public boolean option_enable_ref_counting()
	{
		return option_enable_ref_counting;
	}

	public boolean option_enable_timer()
	{
		return option_enable_timer;
	}

	public String option_c_source_extension()
	{
		return option_c_source_extension;
	}

	public boolean option_no_cache()
	{
		return option_no_cache;
	}

	public Set option_lib()
	{
		return option_lib;
	}

	public Set option_deps()
	{
		return option_deps;
	}

	public String option_property(String key)
	{
		return option_property.get(key);
	}

	public boolean option_xmlvm_new_ios_api()
	{
		return option_xmlvm_new_ios_api;
	}

	public boolean option_mobile()
	{
		return option_mobile;
	}

	private static void printText(String[] txt, PrintStream out)
	{
		for (int i= 0; i < txt.length; i++)
			out.println(txt[i]);
	}

	private String guessAppName()
	{
		if (option_out == null)
			return null;
		File outfile= new File(option_out).getAbsoluteFile();
		if (outfile.exists() && outfile.isFile())
			outfile= outfile.getParentFile();
		while (outfile != null && (outfile.getName().equals("..") || outfile.getName().equals(".") || outfile.getName().equals("/")))
			outfile= outfile.getParentFile();
		if (outfile == null)
			return null;
		String guess= outfile.getName();
		if (guess.isEmpty())
			return null;
		Log.warn("Using " + guess + " as application name");
		return guess;
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy