Home | History | Annotate | Download | only in setup
      1 /*******************************************************************************
      2  * Copyright 2011 See AUTHORS file.
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *   http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  ******************************************************************************/
     16 
     17 package com.badlogic.gdx.setup;
     18 
     19 import java.io.*;
     20 import java.util.ArrayList;
     21 import java.util.HashMap;
     22 import java.util.List;
     23 import java.util.Map;
     24 
     25 import javax.swing.JOptionPane;
     26 
     27 import com.badlogic.gdx.setup.DependencyBank.ProjectDependency;
     28 import com.badlogic.gdx.setup.DependencyBank.ProjectType;
     29 import com.badlogic.gdx.setup.Executor.CharCallback;
     30 
     31 /** Command line tool to generate libgdx projects
     32  * @author badlogic
     33  * @author Tomski */
     34 public class GdxSetup {
     35 	public static boolean isSdkLocationValid (String sdkLocation) {
     36 		return new File(sdkLocation, "tools").exists() && new File(sdkLocation, "platforms").exists();
     37 	}
     38 
     39 	public static boolean isEmptyDirectory (String destination) {
     40 		if (new File(destination).exists()) {
     41 			return new File(destination).list().length == 0;
     42 		} else {
     43 			return true;
     44 		}
     45 	}
     46 
     47 	public static boolean isSdkUpToDate (String sdkLocation) {
     48 		File buildTools = new File(sdkLocation, "build-tools");
     49 		if (!buildTools.exists()) {
     50 			JOptionPane.showMessageDialog(null, "You have no build tools!\nUpdate your Android SDK with build tools version: "
     51 				+ DependencyBank.buildToolsVersion);
     52 			return false;
     53 		}
     54 
     55 		File apis = new File(sdkLocation, "platforms");
     56 		if (!apis.exists()) {
     57 			JOptionPane.showMessageDialog(null, "You have no Android APIs!\nUpdate your Android SDK with API level: "
     58 				+ DependencyBank.androidAPILevel);
     59 			return false;
     60 		}
     61 		String newestLocalTool = getLatestTools(buildTools);
     62 		int[] localToolVersion = convertTools(newestLocalTool);
     63 		int[] targetToolVersion = convertTools(DependencyBank.buildToolsVersion);
     64 		if (compareVersions(targetToolVersion, localToolVersion)) {
     65 			int value = JOptionPane.showConfirmDialog(null,
     66 				"You have a more recent version of android build tools than the recommended.\nDo you want to use your more recent version?",
     67 				"Warning!", JOptionPane.YES_NO_OPTION);
     68 			if (value != 0) {
     69 				JOptionPane.showMessageDialog(null, "Using build tools: " + DependencyBank.buildToolsVersion);
     70 			} else {
     71 				DependencyBank.buildToolsVersion = newestLocalTool;
     72 			}
     73 		} else {
     74 			if (!versionsEqual(localToolVersion, targetToolVersion)) {
     75 				JOptionPane.showMessageDialog(null, "Please update your Android SDK, you need build tools: "
     76 					+ DependencyBank.buildToolsVersion);
     77 				return false;
     78 			}
     79 		}
     80 
     81 		int newestLocalApi = getLatestApi(apis);
     82 		if (newestLocalApi > Integer.valueOf(DependencyBank.androidAPILevel)) {
     83 			int value = JOptionPane.showConfirmDialog(null,
     84 				"You have a more recent Android API than the recommended.\nDo you want to use your more recent version?", "Warning!",
     85 				JOptionPane.YES_NO_OPTION);
     86 			if (value != 0) {
     87 				JOptionPane.showMessageDialog(null, "Using API level: " + DependencyBank.androidAPILevel);
     88 			} else {
     89 				DependencyBank.androidAPILevel = String.valueOf(newestLocalApi);
     90 			}
     91 		} else {
     92 			if (newestLocalApi != Integer.valueOf(DependencyBank.androidAPILevel)) {
     93 				JOptionPane.showMessageDialog(null, "Please update your Android SDK, you need the Android API: "
     94 					+ DependencyBank.androidAPILevel);
     95 				return false;
     96 			}
     97 		}
     98 		return true;
     99 	}
    100 
    101 	private static int getLatestApi (File apis) {
    102 		int apiLevel = 0;
    103 		for (File api : apis.listFiles()) {
    104 			int level = readAPIVersion(api);
    105 			if (level > apiLevel) apiLevel = level;
    106 		}
    107 		return apiLevel;
    108 	}
    109 
    110 	private static String getLatestTools (File buildTools) {
    111 		String version = null;
    112 		int[] versionSplit = new int[3];
    113 		int[] testSplit = new int[3];
    114 		for (File toolsVersion : buildTools.listFiles()) {
    115 			if (version == null) {
    116 				version = readBuildToolsVersion(toolsVersion);
    117 				versionSplit = convertTools(version);
    118 				continue;
    119 			}
    120 			testSplit = convertTools(readBuildToolsVersion(toolsVersion));
    121 			if (compareVersions(versionSplit, testSplit)) {
    122 				version = readBuildToolsVersion(toolsVersion);
    123 				versionSplit = convertTools(version);
    124 			}
    125 		}
    126 		if (version != null) {
    127 			return version;
    128 		} else {
    129 			return "0.0.0";
    130 		}
    131 	}
    132 
    133 	private static int readAPIVersion (File parentFile) {
    134 		File properties = new File(parentFile, "source.properties");
    135 		FileReader reader;
    136 		BufferedReader buffer;
    137 		try {
    138 			reader = new FileReader(properties);
    139 			buffer = new BufferedReader(reader);
    140 
    141 			String line = null;
    142 
    143 			while ((line = buffer.readLine()) != null) {
    144 				if (line.contains("AndroidVersion.ApiLevel")) {
    145 
    146 					String versionString = line.split("\\=")[1];
    147 					int apiLevel = Integer.parseInt(versionString);
    148 
    149 					buffer.close();
    150 					reader.close();
    151 
    152 					return apiLevel;
    153 				}
    154 			}
    155 		} catch (FileNotFoundException e) {
    156 			e.printStackTrace();
    157 		} catch (IOException e) {
    158 			e.printStackTrace();
    159 		} catch (NumberFormatException e) {
    160 			e.printStackTrace();
    161 		}
    162 		return 0;
    163 	}
    164 
    165 	private static String readBuildToolsVersion (File parentFile) {
    166 		File properties = new File(parentFile, "source.properties");
    167 		FileReader reader;
    168 		BufferedReader buffer;
    169 		try {
    170 			reader = new FileReader(properties);
    171 			buffer = new BufferedReader(reader);
    172 
    173 			String line = null;
    174 
    175 			while ((line = buffer.readLine()) != null) {
    176 				if (line.contains("Pkg.Revision")) {
    177 
    178 					String versionString = line.split("\\=")[1];
    179 					int count = versionString.split("\\.").length;
    180 					for (int i = 0; i < 3 - count; i++) {
    181 						versionString += ".0";
    182 					}
    183 
    184 					buffer.close();
    185 					reader.close();
    186 
    187 					return versionString;
    188 				}
    189 			}
    190 		} catch (FileNotFoundException e) {
    191 			e.printStackTrace();
    192 		} catch (IOException e) {
    193 			e.printStackTrace();
    194 		}
    195 		return "0.0.0";
    196 	}
    197 
    198 	private static boolean versionsEqual(int[] testVersion, int[] targetVersion) {
    199 		for (int i = 0; i < 3; i++) {
    200 			if (testVersion[i] != targetVersion[i]) return false;
    201 		}
    202 		return true;
    203 	}
    204 
    205 	private static boolean compareVersions(int[] version, int[] testVersion) {
    206 		if (testVersion[0] > version[0]) {
    207 			return true;
    208 		} else if (testVersion[0] == version[0]) {
    209 			if (testVersion[1] > version[1]) {
    210 				return true;
    211 			} else if (testVersion[1] == version[1]) {
    212 				return testVersion[2] > version[2];
    213 			}
    214 		}
    215 		return false;
    216 	}
    217 
    218 	private static int[] convertTools (String toolsVersion) {
    219 		String[] stringSplit = toolsVersion.split("\\.");
    220 		int[] versionSplit = new int[3];
    221 		if (stringSplit.length == 3) {
    222 			try {
    223 				versionSplit[0] = Integer.parseInt(stringSplit[0]);
    224 				versionSplit[1] = Integer.parseInt(stringSplit[1]);
    225 				versionSplit[2] = Integer.parseInt(stringSplit[2]);
    226 				return versionSplit;
    227 			} catch (NumberFormatException nfe) {
    228 				return new int[] {0, 0, 0};
    229 			}
    230 		} else {
    231 			return new int[] {0, 0, 0};
    232 		}
    233 	}
    234 
    235 	public void build (ProjectBuilder builder, String outputDir, String appName, String packageName, String mainClass,
    236 		String sdkLocation, CharCallback callback, List<String> gradleArgs) {
    237 		Project project = new Project();
    238 
    239 		String packageDir = packageName.replace('.', '/');
    240 		String sdkPath = sdkLocation.replace('\\', '/');
    241 
    242 		if (!isSdkLocationValid(sdkLocation)) {
    243 			System.out.println("Android SDK location '" + sdkLocation + "' doesn't contain an SDK");
    244 		}
    245 
    246 		// root dir/gradle files
    247 		project.files.add(new ProjectFile("gitignore", ".gitignore", false));
    248 		project.files.add(new TemporaryProjectFile(builder.settingsFile, "settings.gradle", false));
    249 		project.files.add(new TemporaryProjectFile(builder.buildFile, "build.gradle", true));
    250 		project.files.add(new ProjectFile("gradlew", false));
    251 		project.files.add(new ProjectFile("gradlew.bat", false));
    252 		project.files.add(new ProjectFile("gradle/wrapper/gradle-wrapper.jar", false));
    253 		project.files.add(new ProjectFile("gradle/wrapper/gradle-wrapper.properties", false));
    254 		project.files.add(new ProjectFile("gradle.properties"));
    255 
    256 		// core project
    257 		project.files.add(new ProjectFile("core/build.gradle"));
    258 		project.files.add(new ProjectFile("core/src/MainClass", "core/src/" + packageDir + "/" + mainClass + ".java", true));
    259 		if (builder.modules.contains(ProjectType.HTML)) {
    260 			project.files.add(new ProjectFile("core/CoreGdxDefinition", "core/src/" + mainClass + ".gwt.xml", true));
    261 		}
    262 
    263 		// desktop project
    264 		if (builder.modules.contains(ProjectType.DESKTOP)) {
    265 			project.files.add(new ProjectFile("desktop/build.gradle"));
    266 			project.files.add(new ProjectFile("desktop/src/DesktopLauncher", "desktop/src/" + packageDir + "/desktop/DesktopLauncher.java", true));
    267 		}
    268 
    269 		// Assets
    270 		String assetPath = builder.modules.contains(ProjectType.ANDROID) ? "android/assets" : "core/assets";
    271 		project.files.add(new ProjectFile("android/assets/badlogic.jpg", assetPath + "/badlogic.jpg", false));
    272 
    273 		// android project
    274 		if (builder.modules.contains(ProjectType.ANDROID)) {
    275 			project.files.add(new ProjectFile("android/res/values/strings.xml"));
    276 			project.files.add(new ProjectFile("android/res/values/styles.xml", false));
    277 			project.files.add(new ProjectFile("android/res/drawable-hdpi/ic_launcher.png", false));
    278 			project.files.add(new ProjectFile("android/res/drawable-mdpi/ic_launcher.png", false));
    279 			project.files.add(new ProjectFile("android/res/drawable-xhdpi/ic_launcher.png", false));
    280 			project.files.add(new ProjectFile("android/res/drawable-xxhdpi/ic_launcher.png", false));
    281 			project.files.add(new ProjectFile("android/src/AndroidLauncher", "android/src/" + packageDir + "/AndroidLauncher.java", true));
    282 			project.files.add(new ProjectFile("android/AndroidManifest.xml"));
    283 			project.files.add(new ProjectFile("android/build.gradle", true));
    284 			project.files.add(new ProjectFile("android/ic_launcher-web.png", false));
    285 			project.files.add(new ProjectFile("android/proguard-project.txt", false));
    286 			project.files.add(new ProjectFile("android/project.properties", false));
    287 			project.files.add(new ProjectFile("local.properties", true));
    288 		}
    289 
    290 		// html project
    291 		if (builder.modules.contains(ProjectType.HTML)) {
    292 			project.files.add(new ProjectFile("html/build.gradle"));
    293 			project.files.add(new ProjectFile("html/src/HtmlLauncher", "html/src/" + packageDir + "/client/HtmlLauncher.java", true));
    294 			project.files.add(new ProjectFile("html/GdxDefinition", "html/src/" + packageDir + "/GdxDefinition.gwt.xml", true));
    295 			project.files.add(new ProjectFile("html/GdxDefinitionSuperdev", "html/src/" + packageDir + "/GdxDefinitionSuperdev.gwt.xml", true));
    296 			project.files.add(new ProjectFile("html/war/index", "html/webapp/index.html", true));
    297 			project.files.add(new ProjectFile("html/war/styles.css", "html/webapp/styles.css", false));
    298 			project.files.add(new ProjectFile("html/war/refresh.png", "html/webapp/refresh.png", false));
    299 			project.files.add(new ProjectFile("html/war/soundmanager2-jsmin.js", "html/webapp/soundmanager2-jsmin.js", false));
    300 			project.files.add(new ProjectFile("html/war/soundmanager2-setup.js", "html/webapp/soundmanager2-setup.js", false));
    301 			project.files.add(new ProjectFile("html/war/WEB-INF/web.xml", "html/webapp/WEB-INF/web.xml", true));
    302 		}
    303 
    304 		// ios robovm
    305 		if (builder.modules.contains(ProjectType.IOS)) {
    306 			project.files.add(new ProjectFile("ios/src/IOSLauncher", "ios/src/" + packageDir + "/IOSLauncher.java", true));
    307 			project.files.add(new ProjectFile("ios/data/Default.png", false));
    308 			project.files.add(new ProjectFile("ios/data/Default@2x.png", false));
    309 			project.files.add(new ProjectFile("ios/data/Default@2x~ipad.png", false));
    310 			project.files.add(new ProjectFile("ios/data/Default-568h@2x.png", false));
    311 			project.files.add(new ProjectFile("ios/data/Default~ipad.png", false));
    312 			project.files.add(new ProjectFile("ios/data/Default-375w-667h@2x.png", false));
    313 			project.files.add(new ProjectFile("ios/data/Default-414w-736h@3x.png", false));
    314 			project.files.add(new ProjectFile("ios/data/Icon.png", false));
    315 			project.files.add(new ProjectFile("ios/data/Icon@2x.png", false));
    316 			project.files.add(new ProjectFile("ios/data/Icon-72.png", false));
    317 			project.files.add(new ProjectFile("ios/data/Icon-72@2x.png", false));
    318 			project.files.add(new ProjectFile("ios/build.gradle", true));
    319 			project.files.add(new ProjectFile("ios/Info.plist.xml", false));
    320 			project.files.add(new ProjectFile("ios/robovm.properties"));
    321 			project.files.add(new ProjectFile("ios/robovm.xml", true));
    322 		}
    323 
    324 		if(builder.modules.contains(ProjectType.IOSMOE)) {
    325 			project.files.add(new ProjectFile("ios-moe/resources/Default.png", false));
    326 			project.files.add(new ProjectFile("ios-moe/resources/Default@2x.png", false));
    327 			project.files.add(new ProjectFile("ios-moe/resources/Default@2x~ipad.png", false));
    328 			project.files.add(new ProjectFile("ios-moe/resources/Default-568h@2x.png", false));
    329 			project.files.add(new ProjectFile("ios-moe/resources/Default~ipad.png", false));
    330 			project.files.add(new ProjectFile("ios-moe/resources/Default-375w-667h@2x.png", false));
    331 			project.files.add(new ProjectFile("ios-moe/resources/Default-414w-736h@3x.png", false));
    332 			project.files.add(new ProjectFile("ios-moe/resources/Icon.png", false));
    333 			project.files.add(new ProjectFile("ios-moe/resources/Icon@2x.png", false));
    334 			project.files.add(new ProjectFile("ios-moe/resources/Icon-72.png", false));
    335 			project.files.add(new ProjectFile("ios-moe/resources/Icon-72@2x.png", false));
    336 			project.files.add(new ProjectFile("ios-moe/src/IOSMoeLauncher", "ios-moe/src/" + packageDir + "/IOSMoeLauncher.java", true));
    337 			project.files.add(new ProjectFile("ios-moe/xcode/ios-moe/build.xcconfig", false));
    338 			project.files.add(new ProjectFile("ios-moe/xcode/ios-moe/custom.xcconfig", false));
    339 			project.files.add(new ProjectFile("ios-moe/xcode/ios-moe-Test/build.xcconfig", false));
    340 			project.files.add(new ProjectFile("ios-moe/xcode/ios-moe-Test/Info-Test.plist", false));
    341 			project.files.add(new ProjectFile("ios-moe/xcode/ios-moe/Info.plist", true));
    342 			project.files.add(new ProjectFile("ios-moe/xcode/ios-moe/main.cpp", false));
    343 			project.files.add(new ProjectFile("ios-moe/xcode/ios-moe.xcodeproj/project.pbxproj", true));
    344 			project.files.add(new ProjectFile("ios-moe/build.gradle", true));
    345 		}
    346 
    347 		Map<String, String> values = new HashMap<String, String>();
    348 		values.put("%APP_NAME%", appName);
    349 		values.put("%APP_NAME_ESCAPED%", appName.replace("'", "\\'"));
    350 		values.put("%PACKAGE%", packageName);
    351 		values.put("%PACKAGE_DIR%", packageDir);
    352 		values.put("%MAIN_CLASS%", mainClass);
    353 		values.put("%ANDROID_SDK%", sdkPath);
    354 		values.put("%ASSET_PATH%", assetPath);
    355 		values.put("%BUILD_TOOLS_VERSION%", DependencyBank.buildToolsVersion);
    356 		values.put("%API_LEVEL%", DependencyBank.androidAPILevel);
    357 		values.put("%GWT_VERSION%", DependencyBank.gwtVersion);
    358 		if (builder.modules.contains(ProjectType.HTML)) {
    359 			values.put("%GWT_INHERITS%", parseGwtInherits(builder));
    360 		}
    361 
    362 		copyAndReplace(outputDir, project, values);
    363 
    364 		builder.cleanUp();
    365 
    366 		// HACK executable flag isn't preserved for whatever reason...
    367 		new File(outputDir, "gradlew").setExecutable(true);
    368 
    369 		Executor.execute(new File(outputDir), "gradlew.bat", "gradlew", "clean" + parseGradleArgs(builder.modules, gradleArgs), callback);
    370 	}
    371 
    372 	private void copyAndReplace (String outputDir, Project project, Map<String, String> values) {
    373 		File out = new File(outputDir);
    374 		if (!out.exists() && !out.mkdirs()) {
    375 			throw new RuntimeException("Couldn't create output directory '" + out.getAbsolutePath() + "'");
    376 		}
    377 
    378 		for (ProjectFile file : project.files) {
    379 			copyFile(file, out, values);
    380 		}
    381 	}
    382 
    383 	private byte[] readResource (String resource, String path) {
    384 		InputStream in = null;
    385 		try {
    386 			ByteArrayOutputStream bytes = new ByteArrayOutputStream();
    387 			byte[] buffer = new byte[1024 * 10];
    388 			in = GdxSetup.class.getResourceAsStream(path + resource);
    389 			if (in == null) throw new RuntimeException("Couldn't read resource '" + resource + "'");
    390 			int read = 0;
    391 			while ((read = in.read(buffer)) > 0) {
    392 				bytes.write(buffer, 0, read);
    393 			}
    394 			return bytes.toByteArray();
    395 		} catch (IOException e) {
    396 			throw new RuntimeException("Couldn't read resource '" + resource + "'", e);
    397 		} finally {
    398 			if (in != null) try {
    399 				in.close();
    400 			} catch (IOException e) {
    401 			}
    402 		}
    403 	}
    404 
    405 	private byte[] readResource (File file) {
    406 		InputStream in = null;
    407 		try {
    408 			ByteArrayOutputStream bytes = new ByteArrayOutputStream();
    409 			byte[] buffer = new byte[1024 * 10];
    410 			in = new FileInputStream(file);
    411 			if (in == null) throw new RuntimeException("Couldn't read resource '" + file.getAbsoluteFile() + "'");
    412 			int read = 0;
    413 			while ((read = in.read(buffer)) > 0) {
    414 				bytes.write(buffer, 0, read);
    415 			}
    416 			return bytes.toByteArray();
    417 		} catch (Throwable e) {
    418 			throw new RuntimeException("Couldn't read resource '" + file.getAbsoluteFile() + "'", e);
    419 		} finally {
    420 			if (in != null) try {
    421 				in.close();
    422 			} catch (IOException e) {
    423 			}
    424 		}
    425 	}
    426 
    427 	private String readResourceAsString (String resource, String path) {
    428 		try {
    429 			return new String(readResource(resource, path), "UTF-8");
    430 		} catch (UnsupportedEncodingException e) {
    431 			throw new RuntimeException(e);
    432 		}
    433 	}
    434 
    435 	private String readResourceAsString (File file) {
    436 		try {
    437 			return new String(readResource(file), "UTF-8");
    438 		} catch (UnsupportedEncodingException e) {
    439 			throw new RuntimeException(e);
    440 		}
    441 	}
    442 
    443 	private void writeFile (File outFile, byte[] bytes) {
    444 		OutputStream out = null;
    445 
    446 		try {
    447 			out = new BufferedOutputStream(new FileOutputStream(outFile));
    448 			out.write(bytes);
    449 		} catch (IOException e) {
    450 			throw new RuntimeException("Couldn't write file '" + outFile.getAbsolutePath() + "'", e);
    451 		} finally {
    452 			if (out != null) try {
    453 				out.close();
    454 			} catch (IOException e) {
    455 			}
    456 		}
    457 	}
    458 
    459 	private void writeFile (File outFile, String text) {
    460 		try {
    461 			writeFile(outFile, text.getBytes("UTF-8"));
    462 		} catch (UnsupportedEncodingException e) {
    463 			throw new RuntimeException(e);
    464 		}
    465 	}
    466 
    467 	private void copyFile (ProjectFile file, File out, Map<String, String> values) {
    468 		File outFile = new File(out, file.outputName);
    469 		if (!outFile.getParentFile().exists() && !outFile.getParentFile().mkdirs()) {
    470 			throw new RuntimeException("Couldn't create dir '" + outFile.getAbsolutePath() + "'");
    471 		}
    472 
    473 		boolean isTemp = file instanceof TemporaryProjectFile ? true : false;
    474 
    475 		if (file.isTemplate) {
    476 			String txt;
    477 			if (isTemp) {
    478 				txt = readResourceAsString(((TemporaryProjectFile)file).file);
    479 			} else {
    480 				txt = readResourceAsString(file.resourceName, file.resourceLoc);
    481 			}
    482 			txt = replace(txt, values);
    483 			writeFile(outFile, txt);
    484 		} else {
    485 			if (isTemp) {
    486 				writeFile(outFile, readResource(((TemporaryProjectFile)file).file));
    487 			} else {
    488 				writeFile(outFile, readResource(file.resourceName, file.resourceLoc));
    489 			}
    490 		}
    491 	}
    492 
    493 	private String replace (String txt, Map<String, String> values) {
    494 		for (String key : values.keySet()) {
    495 			String value = values.get(key);
    496 			txt = txt.replace(key, value);
    497 		}
    498 		return txt;
    499 	}
    500 
    501 	private static void printHelp () {
    502 		System.out
    503 			.println("Usage: GdxSetup --dir <dir-name> --name <app-name> --package <package> --mainClass <mainClass> --sdkLocation <SDKLocation> [--excludeModules <modules>] [--extensions <extensions>]");
    504 		System.out.println("dir ... the directory to write the project files to");
    505 		System.out.println("name ... the name of the application");
    506 		System.out.println("package ... the Java package name of the application");
    507 		System.out.println("mainClass ... the name of your main ApplicationListener");
    508 		System.out.println("sdkLocation ... the location of your android SDK. Uses ANDROID_HOME if not specified. Ignored if android module is excluded");
    509 		System.out.println("excludeModules ... the modules to exclude on the project generation separated by ';'. Optional");
    510 		System.out.println("extensions ... the extensions to include in the project separated by ';'. Optional");
    511 	}
    512 
    513 	private static Map<String, String> parseArgs (String[] args) {
    514 		if (args.length % 2 != 0) {
    515 			printHelp();
    516 			System.exit(-1);
    517 		}
    518 
    519 		Map<String, String> params = new HashMap<String, String>();
    520 		for (int i = 0; i < args.length; i += 2) {
    521 			String param = args[i].replace("--", "");
    522 			String value = args[i + 1];
    523 			params.put(param, value);
    524 		}
    525 		return params;
    526 	}
    527 
    528 	 private static List<String> parseExcludedModules (String excludedModules) {
    529 		  List<String> excludedModulesList = new ArrayList<String>();
    530 
    531 		  while (excludedModules.contains(";")) {
    532 				excludedModulesList.add(excludedModules.substring(0, excludedModules.indexOf(";")).toLowerCase());
    533 				excludedModules = excludedModules.substring(excludedModules.indexOf(";") + 1);
    534 		  }
    535 		  excludedModulesList.add(excludedModules.toLowerCase());
    536 
    537 		  return excludedModulesList;
    538 	 }
    539 
    540 	 private static List<Dependency> parseDependencies (String dependencies, DependencyBank bank) {
    541 		  List<String> dependencyNames = new ArrayList<String>();
    542 		  while (dependencies.contains(";")) {
    543 				dependencyNames.add(dependencies.substring(0, dependencies.indexOf(";")).toLowerCase());
    544 				dependencies = dependencies.substring(dependencies.indexOf(";") + 1);
    545 		  }
    546 		  dependencyNames.add(dependencies.toLowerCase());
    547 
    548 		  Map<String, Dependency> dependencyMap = new HashMap<String, Dependency>();
    549 		  for (ProjectDependency pd : ProjectDependency.values()) {
    550 				dependencyMap.put(pd.name().toLowerCase(), bank.getDependency(pd));
    551 		  }
    552 
    553 		  List<Dependency> dependencyList = new ArrayList<Dependency>();
    554 		  dependencyList.add(bank.getDependency(ProjectDependency.GDX));
    555 		  for (String name : dependencyNames) {
    556 				if (dependencyMap.containsKey(name)) {
    557 					 System.out.println("Extension " + name + " found");
    558 					 dependencyList.add(dependencyMap.get(name));
    559 				} else
    560 					 System.out.println("Extension " + name + " not found");
    561 		  }
    562 
    563 		  return dependencyList;
    564 	 }
    565 
    566 	private String parseGwtInherits (ProjectBuilder builder) {
    567 		String parsed = "";
    568 
    569 		for (Dependency dep : builder.dependencies) {
    570 			if (dep.getGwtInherits() != null) {
    571 				for (String inherit : dep.getGwtInherits()) {
    572 					parsed += "\t<inherits name='" + inherit + "' />\n";
    573 				}
    574 			}
    575 		}
    576 
    577 		return parsed;
    578 	}
    579 
    580 	private String parseGradleArgs (List<ProjectType> modules, List<String> args) {
    581 		String argString = "";
    582 		if (args == null) return argString;
    583 		for (String argument : args) {
    584 			if (argument.equals("afterEclipseImport") && !modules.contains(ProjectType.DESKTOP)) continue;
    585 			argString += " " + argument;
    586 		}
    587 		return argString;
    588 	}
    589 
    590 	private boolean containsDependency (List<Dependency> dependencyList, ProjectDependency projectDependency) {
    591 		for (Dependency dep : dependencyList) {
    592 			if (dep.getName().equals(projectDependency.name())) {
    593 				return true;
    594 			}
    595 		}
    596 		return false;
    597 	}
    598 
    599 	public static void main (String[] args) throws IOException {
    600 		Map<String, String> params = parseArgs(args);
    601 		List<String> excludedModules = null;
    602 		if (params.containsKey("excludeModules"))
    603 			excludedModules = parseExcludedModules(params.get("excludeModules"));
    604 
    605 		if (!params.containsKey("dir") ||
    606 			!params.containsKey("name") ||
    607 			!params.containsKey("package") ||
    608 			!params.containsKey("mainClass") ||
    609 			(!params.containsKey("sdkLocation") && System.getenv("ANDROID_HOME") == null &&
    610 				(excludedModules == null || !excludedModules.contains("android")))) {
    611 			new GdxSetupUI();
    612 			printHelp();
    613 		} else {
    614 			String sdkLocation = "";
    615 			 if (excludedModules == null || !excludedModules.contains("android")) {
    616 				  if (System.getenv("ANDROID_HOME") != null && !params.containsKey("sdkLocation")) {
    617 						sdkLocation = System.getenv("ANDROID_HOME");
    618 				  } else {
    619 						sdkLocation = params.get("sdkLocation");
    620 				  }
    621 			 }
    622 
    623 			DependencyBank bank = new DependencyBank();
    624 			ProjectBuilder builder = new ProjectBuilder(bank);
    625 			List<ProjectType> projects = new ArrayList<ProjectType>();
    626 
    627 			projects.add(ProjectType.CORE);
    628 			 if (excludedModules == null) {
    629 				  projects.add(ProjectType.DESKTOP);
    630 				  projects.add(ProjectType.ANDROID);
    631 				  projects.add(ProjectType.IOS);
    632 				  projects.add(ProjectType.IOSMOE);
    633 				  projects.add(ProjectType.HTML);
    634 			 } else {
    635 				  if (!excludedModules.contains("desktop"))
    636 						projects.add(ProjectType.DESKTOP);
    637 				  if (!excludedModules.contains("android"))
    638 						projects.add(ProjectType.ANDROID);
    639 				  if (!excludedModules.contains("ios"))
    640 						projects.add(ProjectType.IOS);
    641 				  if (!excludedModules.contains("iosmoe"))
    642 					  	projects.add(ProjectType.IOSMOE);
    643 				  if (!excludedModules.contains("html"))
    644 						projects.add(ProjectType.HTML);
    645 			 }
    646 
    647 			List<Dependency> dependencies = new ArrayList<Dependency>();
    648 			if (params.containsKey("extensions")) {
    649 				 dependencies.addAll(parseDependencies(params.get("extensions"), bank));
    650 			} else {
    651 				 dependencies.add(bank.getDependency(ProjectDependency.GDX));
    652 			}
    653 
    654 			builder.buildProject(projects, dependencies);
    655 			builder.build();
    656 			new GdxSetup().build(builder, params.get("dir"), params.get("name"), params.get("package"), params.get("mainClass"),
    657 				sdkLocation, new CharCallback() {
    658 					@Override
    659 					public void character (char c) {
    660 						System.out.print(c);
    661 					}
    662 				}, null);
    663 		}
    664 	}
    665 }
    666