ADT #1856119: 'android update project' can now update sub-projects.

This commit is contained in:
Raphael
2009-06-10 11:34:08 -07:00
parent 891968b78e
commit 61df13dec4
5 changed files with 165 additions and 126 deletions

View File

@@ -85,13 +85,13 @@ public class CommandLineProcessor {
mLog = logger; mLog = logger;
mActions = actions; mActions = actions;
define(MODE.BOOLEAN, false, GLOBAL_FLAG_VERB, NO_VERB_OBJECT, "v", KEY_VERBOSE, define(Mode.BOOLEAN, false, GLOBAL_FLAG_VERB, NO_VERB_OBJECT, "v", KEY_VERBOSE,
"Verbose mode: errors, warnings and informational messages are printed.", "Verbose mode: errors, warnings and informational messages are printed.",
false); false);
define(MODE.BOOLEAN, false, GLOBAL_FLAG_VERB, NO_VERB_OBJECT, "s", KEY_SILENT, define(Mode.BOOLEAN, false, GLOBAL_FLAG_VERB, NO_VERB_OBJECT, "s", KEY_SILENT,
"Silent mode: only errors are printed out.", "Silent mode: only errors are printed out.",
false); false);
define(MODE.BOOLEAN, false, GLOBAL_FLAG_VERB, NO_VERB_OBJECT, "h", KEY_HELP, define(Mode.BOOLEAN, false, GLOBAL_FLAG_VERB, NO_VERB_OBJECT, "h", KEY_HELP,
"This help.", "This help.",
false); false);
} }
@@ -507,7 +507,7 @@ public class CommandLineProcessor {
} }
} else if (arg.getDefaultValue() != null) { } else if (arg.getDefaultValue() != null) {
Object v = arg.getDefaultValue(); Object v = arg.getDefaultValue();
if (arg.getMode() != MODE.BOOLEAN || v.equals(Boolean.TRUE)) { if (arg.getMode() != Mode.BOOLEAN || v.equals(Boolean.TRUE)) {
value = v.toString(); value = v.toString();
} }
} }
@@ -537,7 +537,7 @@ public class CommandLineProcessor {
* The mode of an argument specifies the type of variable it represents, * The mode of an argument specifies the type of variable it represents,
* whether an extra parameter is required after the flag and how to parse it. * whether an extra parameter is required after the flag and how to parse it.
*/ */
static enum MODE { static enum Mode {
/** Argument value is a Boolean. Default value is a Boolean. */ /** Argument value is a Boolean. Default value is a Boolean. */
BOOLEAN { BOOLEAN {
@Override @Override
@@ -628,7 +628,7 @@ public class CommandLineProcessor {
* An argument accepted by the command-line, also called "a flag". * An argument accepted by the command-line, also called "a flag".
* Arguments must have a short version (one letter), a long version name and a description. * Arguments must have a short version (one letter), a long version name and a description.
* They can have a default value, or it can be null. * They can have a default value, or it can be null.
* Depending on the {@link MODE}, the default value can be a Boolean, an Integer, a String * Depending on the {@link Mode}, the default value can be a Boolean, an Integer, a String
* or a String array (in which case the first item is the current by default.) * or a String array (in which case the first item is the current by default.)
*/ */
static class Arg { static class Arg {
@@ -645,7 +645,7 @@ public class CommandLineProcessor {
/** A default value. Can be null. */ /** A default value. Can be null. */
private final Object mDefaultValue; private final Object mDefaultValue;
/** The argument mode (type + process method). Never null. */ /** The argument mode (type + process method). Never null. */
private final MODE mMode; private final Mode mMode;
/** True if this argument is mandatory for this verb/directobject. */ /** True if this argument is mandatory for this verb/directobject. */
private final boolean mMandatory; private final boolean mMandatory;
/** Current value. Initially set to the default value. */ /** Current value. Initially set to the default value. */
@@ -656,15 +656,15 @@ public class CommandLineProcessor {
/** /**
* Creates a new argument flag description. * Creates a new argument flag description.
* *
* @param mode The {@link MODE} for the argument. * @param mode The {@link Mode} for the argument.
* @param mandatory True if this argument is mandatory for this action. * @param mandatory True if this argument is mandatory for this action.
* @param directObject The action name. Can be #NO_VERB_OBJECT or #INTERNAL_FLAG. * @param directObject The action name. Can be #NO_VERB_OBJECT or #INTERNAL_FLAG.
* @param shortName The one-letter short argument name. Cannot be empty nor null. * @param shortName The one-letter short argument name. Cannot be empty nor null.
* @param longName The long argument name. Cannot be empty nor null. * @param longName The long argument name. Cannot be empty nor null.
* @param description The description. Cannot be null. * @param description The description. Cannot be null.
* @param defaultValue The default value (or values), which depends on the selected {@link MODE}. * @param defaultValue The default value (or values), which depends on the selected {@link Mode}.
*/ */
public Arg(MODE mode, public Arg(Mode mode,
boolean mandatory, boolean mandatory,
String verb, String verb,
String directObject, String directObject,
@@ -734,7 +734,7 @@ public class CommandLineProcessor {
} }
/** Returns the argument mode (type + process method). Never null. */ /** Returns the argument mode (type + process method). Never null. */
public MODE getMode() { public Mode getMode() {
return mMode; return mMode;
} }
@@ -752,21 +752,21 @@ public class CommandLineProcessor {
/** /**
* Internal helper to define a new argument for a give action. * Internal helper to define a new argument for a give action.
* *
* @param mode The {@link MODE} for the argument. * @param mode The {@link Mode} for the argument.
* @param verb The verb name. Can be #INTERNAL_VERB. * @param verb The verb name. Can be #INTERNAL_VERB.
* @param directObject The action name. Can be #NO_VERB_OBJECT or #INTERNAL_FLAG. * @param directObject The action name. Can be #NO_VERB_OBJECT or #INTERNAL_FLAG.
* @param shortName The one-letter short argument name. Cannot be empty nor null. * @param shortName The one-letter short argument name. Cannot be empty nor null.
* @param longName The long argument name. Cannot be empty nor null. * @param longName The long argument name. Cannot be empty nor null.
* @param description The description. Cannot be null. * @param description The description. Cannot be null.
* @param defaultValue The default value (or values), which depends on the selected {@link MODE}. * @param defaultValue The default value (or values), which depends on the selected {@link Mode}.
*/ */
protected void define(MODE mode, protected void define(Mode mode,
boolean mandatory, boolean mandatory,
String verb, String verb,
String directObject, String directObject,
String shortName, String longName, String shortName, String longName,
String description, Object defaultValue) { String description, Object defaultValue) {
assert(mandatory || mode == MODE.BOOLEAN); // a boolean mode cannot be mandatory assert(mandatory || mode == Mode.BOOLEAN); // a boolean mode cannot be mandatory
if (directObject == null) { if (directObject == null) {
directObject = NO_VERB_OBJECT; directObject = NO_VERB_OBJECT;

View File

@@ -337,6 +337,33 @@ class Main {
creator.updateProject(projectDir, creator.updateProject(projectDir,
target, target,
mSdkCommandLine.getParamName()); mSdkCommandLine.getParamName());
boolean doSubProjects = mSdkCommandLine.getParamSubProject();
boolean couldHaveDone = false;
// If there are any sub-folders with a manifest, try to update them as projects
// too. This will take care of updating any underlying test project even if the
// user changed the folder name.
File[] files = new File(projectDir).listFiles();
if (files != null) {
for (File dir : files) {
if (dir.isDirectory() &&
new File(dir, SdkConstants.FN_ANDROID_MANIFEST_XML).isFile()) {
if (doSubProjects) {
creator.updateProject(dir.getPath(),
target,
mSdkCommandLine.getParamName());
} else {
couldHaveDone = true;
}
}
}
}
if (couldHaveDone) {
mSdkLog.printf("It seems that there are sub-projects. If you want to update them\nplease use the --%1$s parameter.",
SdkCommandLine.KEY_SUBPROJECTS);
}
} }
/** /**

View File

@@ -38,20 +38,21 @@ public class SdkCommandLine extends CommandLineProcessor {
public static final String OBJECT_PROJECT = "project"; public static final String OBJECT_PROJECT = "project";
public static final String OBJECT_ADB = "adb"; public static final String OBJECT_ADB = "adb";
public static final String ARG_ALIAS = "alias"; public static final String ARG_ALIAS = "alias";
public static final String ARG_ACTIVITY = "activity"; public static final String ARG_ACTIVITY = "activity";
public static final String KEY_ACTIVITY = ARG_ACTIVITY; public static final String KEY_ACTIVITY = ARG_ACTIVITY;
public static final String KEY_PACKAGE = "package"; public static final String KEY_PACKAGE = "package";
public static final String KEY_MODE = "mode"; public static final String KEY_MODE = "mode";
public static final String KEY_TARGET_ID = OBJECT_TARGET; public static final String KEY_TARGET_ID = OBJECT_TARGET;
public static final String KEY_NAME = "name"; public static final String KEY_NAME = "name";
public static final String KEY_PATH = "path"; public static final String KEY_PATH = "path";
public static final String KEY_FILTER = "filter"; public static final String KEY_FILTER = "filter";
public static final String KEY_SKIN = "skin"; public static final String KEY_SKIN = "skin";
public static final String KEY_SDCARD = "sdcard"; public static final String KEY_SDCARD = "sdcard";
public static final String KEY_FORCE = "force"; public static final String KEY_FORCE = "force";
public static final String KEY_RENAME = "rename"; public static final String KEY_RENAME = "rename";
public static final String KEY_SUBPROJECTS = "subprojects";
/** /**
* Action definitions for SdkManager command line. * Action definitions for SdkManager command line.
@@ -97,46 +98,46 @@ public class SdkCommandLine extends CommandLineProcessor {
// --- create avd --- // --- create avd ---
define(MODE.STRING, false, define(Mode.STRING, false,
VERB_CREATE, OBJECT_AVD, "p", KEY_PATH, VERB_CREATE, OBJECT_AVD, "p", KEY_PATH,
"Location path of the directory where the new AVD will be created", null); "Location path of the directory where the new AVD will be created", null);
define(MODE.STRING, true, define(Mode.STRING, true,
VERB_CREATE, OBJECT_AVD, "n", KEY_NAME, VERB_CREATE, OBJECT_AVD, "n", KEY_NAME,
"Name of the new AVD", null); "Name of the new AVD", null);
define(MODE.INTEGER, true, define(Mode.INTEGER, true,
VERB_CREATE, OBJECT_AVD, "t", KEY_TARGET_ID, VERB_CREATE, OBJECT_AVD, "t", KEY_TARGET_ID,
"Target id of the new AVD", null); "Target id of the new AVD", null);
define(MODE.STRING, false, define(Mode.STRING, false,
VERB_CREATE, OBJECT_AVD, "s", KEY_SKIN, VERB_CREATE, OBJECT_AVD, "s", KEY_SKIN,
"Skin of the new AVD", null); "Skin of the new AVD", null);
define(MODE.STRING, false, define(Mode.STRING, false,
VERB_CREATE, OBJECT_AVD, "c", KEY_SDCARD, VERB_CREATE, OBJECT_AVD, "c", KEY_SDCARD,
"Path to a shared SD card image, or size of a new sdcard for the new AVD", null); "Path to a shared SD card image, or size of a new sdcard for the new AVD", null);
define(MODE.BOOLEAN, false, define(Mode.BOOLEAN, false,
VERB_CREATE, OBJECT_AVD, "f", KEY_FORCE, VERB_CREATE, OBJECT_AVD, "f", KEY_FORCE,
"Force creation (override an existing AVD)", false); "Force creation (override an existing AVD)", false);
// --- delete avd --- // --- delete avd ---
define(MODE.STRING, true, define(Mode.STRING, true,
VERB_DELETE, OBJECT_AVD, "n", KEY_NAME, VERB_DELETE, OBJECT_AVD, "n", KEY_NAME,
"Name of the AVD to delete", null); "Name of the AVD to delete", null);
// --- move avd --- // --- move avd ---
define(MODE.STRING, true, define(Mode.STRING, true,
VERB_MOVE, OBJECT_AVD, "n", KEY_NAME, VERB_MOVE, OBJECT_AVD, "n", KEY_NAME,
"Name of the AVD to move or rename", null); "Name of the AVD to move or rename", null);
define(MODE.STRING, false, define(Mode.STRING, false,
VERB_MOVE, OBJECT_AVD, "r", KEY_RENAME, VERB_MOVE, OBJECT_AVD, "r", KEY_RENAME,
"New name of the AVD to rename", null); "New name of the AVD to rename", null);
define(MODE.STRING, false, define(Mode.STRING, false,
VERB_MOVE, OBJECT_AVD, "p", KEY_PATH, VERB_MOVE, OBJECT_AVD, "p", KEY_PATH,
"New location path of the directory where to move the AVD", null); "New location path of the directory where to move the AVD", null);
// --- update avd --- // --- update avd ---
define(MODE.STRING, true, define(Mode.STRING, true,
VERB_UPDATE, OBJECT_AVD, "n", KEY_NAME, VERB_UPDATE, OBJECT_AVD, "n", KEY_NAME,
"Name of the AVD to update", null); "Name of the AVD to update", null);
@@ -145,41 +146,45 @@ public class SdkCommandLine extends CommandLineProcessor {
/* Disabled for ADT 0.9 / Cupcake SDK 1.5_r1 release. [bug #1795718]. /* Disabled for ADT 0.9 / Cupcake SDK 1.5_r1 release. [bug #1795718].
This currently does not work, the alias build rules need to be fixed. This currently does not work, the alias build rules need to be fixed.
define(MODE.ENUM, true, define(Mode.ENUM, true,
VERB_CREATE, OBJECT_PROJECT, "m", KEY_MODE, VERB_CREATE, OBJECT_PROJECT, "m", KEY_MODE,
"Project mode", new String[] { ARG_ACTIVITY, ARG_ALIAS }); "Project mode", new String[] { ARG_ACTIVITY, ARG_ALIAS });
*/ */
define(MODE.STRING, true, define(Mode.STRING, true,
VERB_CREATE, OBJECT_PROJECT, VERB_CREATE, OBJECT_PROJECT,
"p", KEY_PATH, "p", KEY_PATH,
"Location path of new project", null); "Location path of new project", null);
define(MODE.INTEGER, true, define(Mode.INTEGER, true,
VERB_CREATE, OBJECT_PROJECT, "t", KEY_TARGET_ID, VERB_CREATE, OBJECT_PROJECT, "t", KEY_TARGET_ID,
"Target id of the new project", null); "Target id of the new project", null);
define(MODE.STRING, true, define(Mode.STRING, true,
VERB_CREATE, OBJECT_PROJECT, "k", KEY_PACKAGE, VERB_CREATE, OBJECT_PROJECT, "k", KEY_PACKAGE,
"Package name", null); "Package name", null);
define(MODE.STRING, true, define(Mode.STRING, true,
VERB_CREATE, OBJECT_PROJECT, "a", KEY_ACTIVITY, VERB_CREATE, OBJECT_PROJECT, "a", KEY_ACTIVITY,
"Activity name", null); "Activity name", null);
define(MODE.STRING, false, define(Mode.STRING, false,
VERB_CREATE, OBJECT_PROJECT, "n", KEY_NAME, VERB_CREATE, OBJECT_PROJECT, "n", KEY_NAME,
"Project name", null); "Project name", null);
// --- update project --- // --- update project ---
define(MODE.STRING, true, define(Mode.STRING, true,
VERB_UPDATE, OBJECT_PROJECT, VERB_UPDATE, OBJECT_PROJECT,
"p", KEY_PATH, "p", KEY_PATH,
"Location path of the project", null); "Location path of the project", null);
define(MODE.INTEGER, true, define(Mode.INTEGER, true,
VERB_UPDATE, OBJECT_PROJECT, VERB_UPDATE, OBJECT_PROJECT,
"t", KEY_TARGET_ID, "t", KEY_TARGET_ID,
"Target id to set for the project", -1); "Target id to set for the project", -1);
define(MODE.STRING, false, define(Mode.STRING, false,
VERB_UPDATE, OBJECT_PROJECT, VERB_UPDATE, OBJECT_PROJECT,
"n", KEY_NAME, "n", KEY_NAME,
"Project name", null); "Project name", null);
define(Mode.BOOLEAN, false,
VERB_UPDATE, OBJECT_PROJECT,
"s", KEY_SUBPROJECTS,
"Also update any projects in sub-folders, such as test projects.", false);
} }
@Override @Override
@@ -234,8 +239,13 @@ public class SdkCommandLine extends CommandLineProcessor {
return ((String) getValue(null, OBJECT_PROJECT, KEY_PACKAGE)); return ((String) getValue(null, OBJECT_PROJECT, KEY_PACKAGE));
} }
/** Helper to retrieve the --activity for the new project action. */ /** Helper to retrieve the --activity for any project action. */
public String getParamProjectActivity() { public String getParamProjectActivity() {
return ((String) getValue(null, OBJECT_PROJECT, KEY_ACTIVITY)); return ((String) getValue(null, OBJECT_PROJECT, KEY_ACTIVITY));
} }
/** Helper to retrieve the --subprojects for any project action. */
public boolean getParamSubProject() {
return ((Boolean) getValue(null, OBJECT_PROJECT, KEY_SUBPROJECTS)).booleanValue();
}
} }

View File

@@ -41,9 +41,9 @@ public class CommandLineProcessorTest extends TestCase {
{ "verb1", "action1", "Some action" }, { "verb1", "action1", "Some action" },
{ "verb1", "action2", "Another action" }, { "verb1", "action2", "Another action" },
}); });
define(MODE.STRING, false /*mandatory*/, define(Mode.STRING, false /*mandatory*/,
"verb1", "action1", "1", "first", "non-mandatory flag", null); "verb1", "action1", "1", "first", "non-mandatory flag", null);
define(MODE.STRING, true /*mandatory*/, define(Mode.STRING, true /*mandatory*/,
"verb1", "action1", "2", "second", "mandatory flag", null); "verb1", "action1", "2", "second", "mandatory flag", null);
} }

View File

@@ -50,7 +50,7 @@ import javax.xml.xpath.XPathFactory;
* @hide * @hide
*/ */
public class ProjectCreator { public class ProjectCreator {
/** Package path substitution string used in template files, i.e. "PACKAGE_PATH" */ /** Package path substitution string used in template files, i.e. "PACKAGE_PATH" */
private final static String PH_JAVA_FOLDER = "PACKAGE_PATH"; private final static String PH_JAVA_FOLDER = "PACKAGE_PATH";
/** Package name substitution string used in template files, i.e. "PACKAGE" */ /** Package name substitution string used in template files, i.e. "PACKAGE" */
@@ -59,9 +59,9 @@ public class ProjectCreator {
private final static String PH_ACTIVITY_NAME = "ACTIVITY_NAME"; private final static String PH_ACTIVITY_NAME = "ACTIVITY_NAME";
/** Project name substitution string used in template files, i.e. "PROJECT_NAME". */ /** Project name substitution string used in template files, i.e. "PROJECT_NAME". */
private final static String PH_PROJECT_NAME = "PROJECT_NAME"; private final static String PH_PROJECT_NAME = "PROJECT_NAME";
private final static String FOLDER_TESTS = "tests"; private final static String FOLDER_TESTS = "tests";
/** Pattern for characters accepted in a project name. Since this will be used as a /** Pattern for characters accepted in a project name. Since this will be used as a
* directory name, we're being a bit conservative on purpose: dot and space cannot be used. */ * directory name, we're being a bit conservative on purpose: dot and space cannot be used. */
public static final Pattern RE_PROJECT_NAME = Pattern.compile("[a-zA-Z0-9_]+"); public static final Pattern RE_PROJECT_NAME = Pattern.compile("[a-zA-Z0-9_]+");
@@ -69,7 +69,7 @@ public class ProjectCreator {
public final static String CHARS_PROJECT_NAME = "a-z A-Z 0-9 _"; public final static String CHARS_PROJECT_NAME = "a-z A-Z 0-9 _";
/** Pattern for characters accepted in a package name. A package is list of Java identifier /** Pattern for characters accepted in a package name. A package is list of Java identifier
* separated by a dot. We need to have at least one dot (e.g. a two-level package name). * separated by a dot. We need to have at least one dot (e.g. a two-level package name).
* A Java identifier cannot start by a digit. */ * A Java identifier cannot start by a digit. */
public static final Pattern RE_PACKAGE_NAME = public static final Pattern RE_PACKAGE_NAME =
Pattern.compile("[a-zA-Z_][a-zA-Z0-9_]*(?:\\.[a-zA-Z_][a-zA-Z0-9_]*)+"); Pattern.compile("[a-zA-Z_][a-zA-Z0-9_]*(?:\\.[a-zA-Z_][a-zA-Z0-9_]*)+");
@@ -82,7 +82,7 @@ public class ProjectCreator {
/** List of valid characters for a project name. Used for display purposes. */ /** List of valid characters for a project name. Used for display purposes. */
public final static String CHARS_ACTIVITY_NAME = "a-z A-Z 0-9 _"; public final static String CHARS_ACTIVITY_NAME = "a-z A-Z 0-9 _";
public enum OutputLevel { public enum OutputLevel {
/** Silent mode. Project creation will only display errors. */ /** Silent mode. Project creation will only display errors. */
SILENT, SILENT,
@@ -100,11 +100,11 @@ public class ProjectCreator {
private static class ProjectCreateException extends Exception { private static class ProjectCreateException extends Exception {
/** default UID. This will not be serialized anyway. */ /** default UID. This will not be serialized anyway. */
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
ProjectCreateException(String message) { ProjectCreateException(String message) {
super(message); super(message);
} }
ProjectCreateException(Throwable t, String format, Object... args) { ProjectCreateException(Throwable t, String format, Object... args) {
super(format != null ? String.format(format, args) : format, t); super(format != null ? String.format(format, args) : format, t);
} }
@@ -113,23 +113,23 @@ public class ProjectCreator {
super(String.format(format, args)); super(String.format(format, args));
} }
} }
private final OutputLevel mLevel; private final OutputLevel mLevel;
private final ISdkLog mLog; private final ISdkLog mLog;
private final String mSdkFolder; private final String mSdkFolder;
public ProjectCreator(String sdkFolder, OutputLevel level, ISdkLog log) { public ProjectCreator(String sdkFolder, OutputLevel level, ISdkLog log) {
mSdkFolder = sdkFolder; mSdkFolder = sdkFolder;
mLevel = level; mLevel = level;
mLog = log; mLog = log;
} }
/** /**
* Creates a new project. * Creates a new project.
* <p/> * <p/>
* The caller should have already checked and sanitized the parameters. * The caller should have already checked and sanitized the parameters.
* *
* @param folderPath the folder of the project to create. * @param folderPath the folder of the project to create.
* @param projectName the name of the project. The name must match the * @param projectName the name of the project. The name must match the
* {@link #RE_PROJECT_NAME} regex. * {@link #RE_PROJECT_NAME} regex.
@@ -137,14 +137,16 @@ public class ProjectCreator {
* {@link #RE_PACKAGE_NAME} regex. * {@link #RE_PACKAGE_NAME} regex.
* @param activityName the activity of the project as it will appear in the manifest. Can be * @param activityName the activity of the project as it will appear in the manifest. Can be
* null if no activity should be created. The name must match the * null if no activity should be created. The name must match the
* {@link #RE_ACTIVITY_NAME} regex. * {@link #RE_ACTIVITY_NAME} regex.
* @param target the project target. * @param target the project target.
* @param isTestProject whether the project to create is a test project. * @param isTestProject whether the project to create is a test project. Caller should
* initially call this will false. The method will call itself back to create
* a test project as needed.
*/ */
public void createProject(String folderPath, String projectName, public void createProject(String folderPath, String projectName,
String packageName, String activityName, IAndroidTarget target, String packageName, String activityName, IAndroidTarget target,
boolean isTestProject) { boolean isTestProject) {
// create project folder if it does not exist // create project folder if it does not exist
File projectFolder = new File(folderPath); File projectFolder = new File(folderPath);
if (!projectFolder.exists()) { if (!projectFolder.exists()) {
@@ -156,7 +158,7 @@ public class ProjectCreator {
} catch (Exception e) { } catch (Exception e) {
t = e; t = e;
} }
if (created) { if (created) {
println("Created project directory: %1$s", projectFolder); println("Created project directory: %1$s", projectFolder);
} else { } else {
@@ -176,7 +178,7 @@ public class ProjectCreator {
} catch (Exception e1) { } catch (Exception e1) {
e = e1; e = e1;
} }
if (e != null || error != null) { if (e != null || error != null) {
mLog.error(e, error, projectFolder, SdkConstants.androidCmdName()); mLog.error(e, error, projectFolder, SdkConstants.androidCmdName());
} }
@@ -196,7 +198,7 @@ public class ProjectCreator {
PropertyType.DEFAULT); PropertyType.DEFAULT);
defaultProperties.setAndroidTarget(target); defaultProperties.setAndroidTarget(target);
defaultProperties.save(); defaultProperties.save();
// create an empty build.properties // create an empty build.properties
ProjectProperties buildProperties = ProjectProperties.create(folderPath, ProjectProperties buildProperties = ProjectProperties.create(folderPath,
PropertyType.BUILD); PropertyType.BUILD);
@@ -225,16 +227,16 @@ public class ProjectCreator {
keywords.put(PH_PROJECT_NAME, projectName); keywords.put(PH_PROJECT_NAME, projectName);
} else { } else {
if (activityName != null) { if (activityName != null) {
// Use the activity as project name // Use the activity as project name
keywords.put(PH_PROJECT_NAME, activityName); keywords.put(PH_PROJECT_NAME, activityName);
} else { } else {
// We need a project name. Just pick up the basename of the project // We need a project name. Just pick up the basename of the project
// directory. // directory.
projectName = projectFolder.getName(); projectName = projectFolder.getName();
keywords.put(PH_PROJECT_NAME, projectName); keywords.put(PH_PROJECT_NAME, projectName);
} }
} }
// create the source folder and the java package folders. // create the source folder and the java package folders.
String srcFolderPath = SdkConstants.FD_SOURCES + File.separator + packagePath; String srcFolderPath = SdkConstants.FD_SOURCES + File.separator + packagePath;
File sourceFolder = createDirs(projectFolder, srcFolderPath); File sourceFolder = createDirs(projectFolder, srcFolderPath);
@@ -270,13 +272,13 @@ public class ProjectCreator {
/* Make AndroidManifest.xml and build.xml files */ /* Make AndroidManifest.xml and build.xml files */
String manifestTemplate = "AndroidManifest.template"; String manifestTemplate = "AndroidManifest.template";
if (isTestProject) { if (isTestProject) {
manifestTemplate = "AndroidManifest.tests.template"; manifestTemplate = "AndroidManifest.tests.template";
} }
installTemplate(manifestTemplate, installTemplate(manifestTemplate,
new File(projectFolder, SdkConstants.FN_ANDROID_MANIFEST_XML), new File(projectFolder, SdkConstants.FN_ANDROID_MANIFEST_XML),
keywords, target); keywords, target);
installTemplate("build.template", installTemplate("build.template",
new File(projectFolder, SdkConstants.FN_BUILD_XML), new File(projectFolder, SdkConstants.FN_BUILD_XML),
keywords); keywords);
@@ -286,7 +288,7 @@ public class ProjectCreator {
// create the test project folder. // create the test project folder.
createDirs(projectFolder, FOLDER_TESTS); createDirs(projectFolder, FOLDER_TESTS);
File testProjectFolder = new File(folderPath, FOLDER_TESTS); File testProjectFolder = new File(folderPath, FOLDER_TESTS);
createProject(testProjectFolder.getAbsolutePath(), projectName, packageName, createProject(testProjectFolder.getAbsolutePath(), projectName, packageName,
activityName, target, true /*isTestProject*/); activityName, target, true /*isTestProject*/);
} }
@@ -296,7 +298,7 @@ public class ProjectCreator {
mLog.error(e, null); mLog.error(e, null);
} }
} }
/** /**
* Updates an existing project. * Updates an existing project.
* <p/> * <p/>
@@ -308,12 +310,12 @@ public class ProjectCreator {
* <li> Refresh/create "sdk" in local.properties * <li> Refresh/create "sdk" in local.properties
* <li> Build.xml: create if not present or no <androidinit(\w|/>) in it * <li> Build.xml: create if not present or no <androidinit(\w|/>) in it
* </ul> * </ul>
* *
* @param folderPath the folder of the project to update. This folder must exist. * @param folderPath the folder of the project to update. This folder must exist.
* @param target the project target. Can be null. * @param target the project target. Can be null.
* @param projectName The project name from --name. Can be null. * @param projectName The project name from --name. Can be null.
*/ */
public void updateProject(String folderPath, IAndroidTarget target, String projectName ) { public void updateProject(String folderPath, IAndroidTarget target, String projectName) {
// project folder must exist and be a directory, since this is an update // project folder must exist and be a directory, since this is an update
File projectFolder = new File(folderPath); File projectFolder = new File(folderPath);
if (!projectFolder.isDirectory()) { if (!projectFolder.isDirectory()) {
@@ -331,7 +333,7 @@ public class ProjectCreator {
folderPath); folderPath);
return; return;
} }
// Check there's a default.properties with a target *or* --target was specified // Check there's a default.properties with a target *or* --target was specified
ProjectProperties props = ProjectProperties.load(folderPath, PropertyType.DEFAULT); ProjectProperties props = ProjectProperties.load(folderPath, PropertyType.DEFAULT);
if (props == null || props.getProperty(ProjectProperties.PROPERTY_TARGET) == null) { if (props == null || props.getProperty(ProjectProperties.PROPERTY_TARGET) == null) {
@@ -351,7 +353,7 @@ public class ProjectCreator {
if (props == null) { if (props == null) {
props = ProjectProperties.create(folderPath, PropertyType.DEFAULT); props = ProjectProperties.create(folderPath, PropertyType.DEFAULT);
} }
// set or replace the target // set or replace the target
props.setAndroidTarget(target); props.setAndroidTarget(target);
try { try {
@@ -364,7 +366,7 @@ public class ProjectCreator {
return; return;
} }
} }
// Refresh/create "sdk" in local.properties // Refresh/create "sdk" in local.properties
// because the file may already exists and contain other values (like apk config), // because the file may already exists and contain other values (like apk config),
// we first try to load it. // we first try to load it.
@@ -372,7 +374,7 @@ public class ProjectCreator {
if (props == null) { if (props == null) {
props = ProjectProperties.create(folderPath, PropertyType.LOCAL); props = ProjectProperties.create(folderPath, PropertyType.LOCAL);
} }
// set or replace the sdk location. // set or replace the sdk location.
props.setProperty(ProjectProperties.PROPERTY_SDK, mSdkFolder); props.setProperty(ProjectProperties.PROPERTY_SDK, mSdkFolder);
try { try {
@@ -384,7 +386,7 @@ public class ProjectCreator {
folderPath); folderPath);
return; return;
} }
// Build.xml: create if not present or no <androidinit/> in it // Build.xml: create if not present or no <androidinit/> in it
File buildXml = new File(projectFolder, SdkConstants.FN_BUILD_XML); File buildXml = new File(projectFolder, SdkConstants.FN_BUILD_XML);
boolean needsBuildXml = projectName != null || !buildXml.exists(); boolean needsBuildXml = projectName != null || !buildXml.exists();
@@ -397,7 +399,7 @@ public class ProjectCreator {
println("File %1$s is too old and needs to be updated.", SdkConstants.FN_BUILD_XML); println("File %1$s is too old and needs to be updated.", SdkConstants.FN_BUILD_XML);
} }
} }
if (needsBuildXml) { if (needsBuildXml) {
// create the map for place-holders of values to replace in the templates // create the map for place-holders of values to replace in the templates
final HashMap<String, String> keywords = new HashMap<String, String>(); final HashMap<String, String> keywords = new HashMap<String, String>();
@@ -408,13 +410,13 @@ public class ProjectCreator {
} else { } else {
extractPackageFromManifest(androidManifest, keywords); extractPackageFromManifest(androidManifest, keywords);
if (keywords.containsKey(PH_ACTIVITY_NAME)) { if (keywords.containsKey(PH_ACTIVITY_NAME)) {
// Use the activity as project name // Use the activity as project name
keywords.put(PH_PROJECT_NAME, keywords.get(PH_ACTIVITY_NAME)); keywords.put(PH_PROJECT_NAME, keywords.get(PH_ACTIVITY_NAME));
} else { } else {
// We need a project name. Just pick up the basename of the project // We need a project name. Just pick up the basename of the project
// directory. // directory.
projectName = projectFolder.getName(); projectName = projectFolder.getName();
keywords.put(PH_PROJECT_NAME, projectName); keywords.put(PH_PROJECT_NAME, projectName);
} }
} }
@@ -423,7 +425,7 @@ public class ProjectCreator {
SdkConstants.FN_BUILD_XML, SdkConstants.FN_BUILD_XML,
keywords.get(PH_PROJECT_NAME)); keywords.get(PH_PROJECT_NAME));
} }
try { try {
installTemplate("build.template", installTemplate("build.template",
new File(projectFolder, SdkConstants.FN_BUILD_XML), new File(projectFolder, SdkConstants.FN_BUILD_XML),
@@ -443,18 +445,18 @@ public class ProjectCreator {
try { try {
BufferedReader in = new BufferedReader(new FileReader(file)); BufferedReader in = new BufferedReader(new FileReader(file));
String line; String line;
while ((line = in.readLine()) != null) { while ((line = in.readLine()) != null) {
if (p.matcher(line).find()) { if (p.matcher(line).find()) {
return true; return true;
} }
} }
in.close(); in.close();
} catch (Exception e) { } catch (Exception e) {
// ignore // ignore
} }
return false; return false;
} }
@@ -464,8 +466,8 @@ public class ProjectCreator {
* The keywords dictionary is always filed the package name under the key {@link #PH_PACKAGE}. * The keywords dictionary is always filed the package name under the key {@link #PH_PACKAGE}.
* If an activity name can be found, it is filed under the key {@link #PH_ACTIVITY_NAME}. * If an activity name can be found, it is filed under the key {@link #PH_ACTIVITY_NAME}.
* When no activity is found, this key is not created. * When no activity is found, this key is not created.
* *
* @param manifestFile The AndroidManifest.xml file * @param manifestFile The AndroidManifest.xml file
* @param outKeywords Place where to put the out parameters: package and activity names. * @param outKeywords Place where to put the out parameters: package and activity names.
* @return True if the package/activity was parsed and updated in the keyword dictionary. * @return True if the package/activity was parsed and updated in the keyword dictionary.
*/ */
@@ -474,9 +476,9 @@ public class ProjectCreator {
try { try {
final String nsPrefix = "android"; final String nsPrefix = "android";
final String nsURI = SdkConstants.NS_RESOURCES; final String nsURI = SdkConstants.NS_RESOURCES;
XPath xpath = XPathFactory.newInstance().newXPath(); XPath xpath = XPathFactory.newInstance().newXPath();
xpath.setNamespaceContext(new NamespaceContext() { xpath.setNamespaceContext(new NamespaceContext() {
public String getNamespaceURI(String prefix) { public String getNamespaceURI(String prefix) {
if (nsPrefix.equals(prefix)) { if (nsPrefix.equals(prefix)) {
@@ -501,18 +503,18 @@ public class ProjectCreator {
} }
return null; return null;
} }
}); });
InputSource source = new InputSource(new FileReader(manifestFile)); InputSource source = new InputSource(new FileReader(manifestFile));
String packageName = xpath.evaluate("/manifest/@package", source); String packageName = xpath.evaluate("/manifest/@package", source);
source = new InputSource(new FileReader(manifestFile)); source = new InputSource(new FileReader(manifestFile));
// Select the "android:name" attribute of all <activity> nodes but only if they // Select the "android:name" attribute of all <activity> nodes but only if they
// contain a sub-node <intent-filter><action> with an "android:name" attribute which // contain a sub-node <intent-filter><action> with an "android:name" attribute which
// is 'android.intent.action.MAIN' and an <intent-filter><category> with an // is 'android.intent.action.MAIN' and an <intent-filter><category> with an
// "android:name" attribute which is 'android.intent.category.LAUNCHER' // "android:name" attribute which is 'android.intent.category.LAUNCHER'
String expression = String.format("/manifest/application/activity" + String expression = String.format("/manifest/application/activity" +
"[intent-filter/action/@%1$s:name='android.intent.action.MAIN' and " + "[intent-filter/action/@%1$s:name='android.intent.action.MAIN' and " +
"intent-filter/category/@%1$s:name='android.intent.category.LAUNCHER']" + "intent-filter/category/@%1$s:name='android.intent.category.LAUNCHER']" +
@@ -524,7 +526,7 @@ public class ProjectCreator {
// If we get here, both XPath expressions were valid so we're most likely dealing // If we get here, both XPath expressions were valid so we're most likely dealing
// with an actual AndroidManifest.xml file. The nodes may not have the requested // with an actual AndroidManifest.xml file. The nodes may not have the requested
// attributes though, if which case we should warn. // attributes though, if which case we should warn.
if (packageName == null || packageName.length() == 0) { if (packageName == null || packageName.length() == 0) {
mLog.error(null, mLog.error(null,
"Missing <manifest package=\"...\"> in '%1$s'", "Missing <manifest package=\"...\"> in '%1$s'",
@@ -545,14 +547,14 @@ public class ProjectCreator {
"Only the first one will be used. If this is not appropriate, you need\n" + "Only the first one will be used. If this is not appropriate, you need\n" +
"to specify one of these values manually instead:", "to specify one of these values manually instead:",
manifestFile.getName()); manifestFile.getName());
for (int i = 0; i < activityNames.getLength(); i++) { for (int i = 0; i < activityNames.getLength(); i++) {
String name = activityNames.item(i).getNodeValue(); String name = activityNames.item(i).getNodeValue();
name = combinePackageActivityNames(packageName, name); name = combinePackageActivityNames(packageName, name);
println("- %1$s", name); println("- %1$s", name);
} }
} }
if (activityName.length() == 0) { if (activityName.length() == 0) {
mLog.warning("Missing <activity %1$s:name=\"...\"> in '%2$s'.\n" + mLog.warning("Missing <activity %1$s:name=\"...\"> in '%2$s'.\n" +
"No activity will be generated.", "No activity will be generated.",
@@ -563,7 +565,7 @@ public class ProjectCreator {
outKeywords.put(PH_PACKAGE, packageName); outKeywords.put(PH_PACKAGE, packageName);
return true; return true;
} catch (IOException e) { } catch (IOException e) {
mLog.error(e, "Failed to read %1$s", manifestFile.getName()); mLog.error(e, "Failed to read %1$s", manifestFile.getName());
} catch (XPathExpressionException e) { } catch (XPathExpressionException e) {
@@ -572,10 +574,10 @@ public class ProjectCreator {
"Failed to parse %1$s", "Failed to parse %1$s",
manifestFile.getName()); manifestFile.getName());
} }
return false; return false;
} }
private String combinePackageActivityNames(String packageName, String activityName) { private String combinePackageActivityNames(String packageName, String activityName) {
// Activity Name can have 3 forms: // Activity Name can have 3 forms:
// - ".Name" means this is a class name in the given package name. // - ".Name" means this is a class name in the given package name.
@@ -585,7 +587,7 @@ public class ProjectCreator {
// To be valid, the package name should have at least two components. This is checked // To be valid, the package name should have at least two components. This is checked
// later during the creation of the build.xml file, so we just need to detect there's // later during the creation of the build.xml file, so we just need to detect there's
// a dot but not at pos==0. // a dot but not at pos==0.
int pos = activityName.indexOf('.'); int pos = activityName.indexOf('.');
if (pos == 0) { if (pos == 0) {
return packageName + activityName; return packageName + activityName;
@@ -600,12 +602,12 @@ public class ProjectCreator {
* Installs a new file that is based on a template file provided by a given target. * Installs a new file that is based on a template file provided by a given target.
* Each match of each key from the place-holder map in the template will be replaced with its * Each match of each key from the place-holder map in the template will be replaced with its
* corresponding value in the created file. * corresponding value in the created file.
* *
* @param templateName the name of to the template file * @param templateName the name of to the template file
* @param destFile the path to the destination file, relative to the project * @param destFile the path to the destination file, relative to the project
* @param placeholderMap a map of (place-holder, value) to create the file from the template. * @param placeholderMap a map of (place-holder, value) to create the file from the template.
* @param target the Target of the project that will be providing the template. * @param target the Target of the project that will be providing the template.
* @throws ProjectCreateException * @throws ProjectCreateException
*/ */
private void installTemplate(String templateName, File destFile, private void installTemplate(String templateName, File destFile,
Map<String, String> placeholderMap, IAndroidTarget target) Map<String, String> placeholderMap, IAndroidTarget target)
@@ -621,11 +623,11 @@ public class ProjectCreator {
* Installs a new file that is based on a template file provided by the tools folder. * Installs a new file that is based on a template file provided by the tools folder.
* Each match of each key from the place-holder map in the template will be replaced with its * Each match of each key from the place-holder map in the template will be replaced with its
* corresponding value in the created file. * corresponding value in the created file.
* *
* @param templateName the name of to the template file * @param templateName the name of to the template file
* @param destFile the path to the destination file, relative to the project * @param destFile the path to the destination file, relative to the project
* @param placeholderMap a map of (place-holder, value) to create the file from the template. * @param placeholderMap a map of (place-holder, value) to create the file from the template.
* @throws ProjectCreateException * @throws ProjectCreateException
*/ */
private void installTemplate(String templateName, File destFile, private void installTemplate(String templateName, File destFile,
Map<String, String> placeholderMap) Map<String, String> placeholderMap)
@@ -641,38 +643,38 @@ public class ProjectCreator {
* Installs a new file that is based on a template. * Installs a new file that is based on a template.
* Each match of each key from the place-holder map in the template will be replaced with its * Each match of each key from the place-holder map in the template will be replaced with its
* corresponding value in the created file. * corresponding value in the created file.
* *
* @param sourcePath the full path to the source template file * @param sourcePath the full path to the source template file
* @param destFile the destination file * @param destFile the destination file
* @param placeholderMap a map of (place-holder, value) to create the file from the template. * @param placeholderMap a map of (place-holder, value) to create the file from the template.
* @throws ProjectCreateException * @throws ProjectCreateException
*/ */
private void installFullPathTemplate(String sourcePath, File destFile, private void installFullPathTemplate(String sourcePath, File destFile,
Map<String, String> placeholderMap) throws ProjectCreateException { Map<String, String> placeholderMap) throws ProjectCreateException {
boolean existed = destFile.exists(); boolean existed = destFile.exists();
try { try {
BufferedWriter out = new BufferedWriter(new FileWriter(destFile)); BufferedWriter out = new BufferedWriter(new FileWriter(destFile));
BufferedReader in = new BufferedReader(new FileReader(sourcePath)); BufferedReader in = new BufferedReader(new FileReader(sourcePath));
String line; String line;
while ((line = in.readLine()) != null) { while ((line = in.readLine()) != null) {
for (String key : placeholderMap.keySet()) { for (String key : placeholderMap.keySet()) {
line = line.replace(key, placeholderMap.get(key)); line = line.replace(key, placeholderMap.get(key));
} }
out.write(line); out.write(line);
out.newLine(); out.newLine();
} }
out.close(); out.close();
in.close(); in.close();
} catch (Exception e) { } catch (Exception e) {
throw new ProjectCreateException(e, "Could not access %1$s: %2$s", throw new ProjectCreateException(e, "Could not access %1$s: %2$s",
destFile, e.getMessage()); destFile, e.getMessage());
} }
println("%1$s file %2$s", println("%1$s file %2$s",
existed ? "Updated" : "Added", existed ? "Updated" : "Added",
destFile); destFile);
@@ -683,7 +685,7 @@ public class ProjectCreator {
* <p/> * <p/>
* This is just a convenience wrapper around {@link ISdkLog#printf(String, Object...)} from * This is just a convenience wrapper around {@link ISdkLog#printf(String, Object...)} from
* {@link #mLog} after testing if ouput level is {@link OutputLevel#VERBOSE}. * {@link #mLog} after testing if ouput level is {@link OutputLevel#VERBOSE}.
* *
* @param format Format for String.format * @param format Format for String.format
* @param args Arguments for String.format * @param args Arguments for String.format
*/ */
@@ -698,10 +700,10 @@ public class ProjectCreator {
/** /**
* Creates a new folder, along with any parent folders that do not exists. * Creates a new folder, along with any parent folders that do not exists.
* *
* @param parent the parent folder * @param parent the parent folder
* @param name the name of the directory to create. * @param name the name of the directory to create.
* @throws ProjectCreateException * @throws ProjectCreateException
*/ */
private File createDirs(File parent, String name) throws ProjectCreateException { private File createDirs(File parent, String name) throws ProjectCreateException {
final File newFolder = new File(parent, name); final File newFolder = new File(parent, name);
@@ -730,7 +732,7 @@ public class ProjectCreator {
"Could not determine canonical path of created directory", e); "Could not determine canonical path of created directory", e);
} }
} }
return newFolder; return newFolder;
} }
@@ -738,7 +740,7 @@ public class ProjectCreator {
* Strips the string of beginning and trailing characters (multiple * Strips the string of beginning and trailing characters (multiple
* characters will be stripped, example stripString("..test...", '.') * characters will be stripped, example stripString("..test...", '.')
* results in "test"; * results in "test";
* *
* @param s the string to strip * @param s the string to strip
* @param strip the character to strip from beginning and end * @param strip the character to strip from beginning and end
* @return the stripped string or the empty string if everything is stripped. * @return the stripped string or the empty string if everything is stripped.
@@ -746,24 +748,24 @@ public class ProjectCreator {
private static String stripString(String s, char strip) { private static String stripString(String s, char strip) {
final int sLen = s.length(); final int sLen = s.length();
int newStart = 0, newEnd = sLen - 1; int newStart = 0, newEnd = sLen - 1;
while (newStart < sLen && s.charAt(newStart) == strip) { while (newStart < sLen && s.charAt(newStart) == strip) {
newStart++; newStart++;
} }
while (newEnd >= 0 && s.charAt(newEnd) == strip) { while (newEnd >= 0 && s.charAt(newEnd) == strip) {
newEnd--; newEnd--;
} }
/* /*
* newEnd contains a char we want, and substring takes end as being * newEnd contains a char we want, and substring takes end as being
* exclusive * exclusive
*/ */
newEnd++; newEnd++;
if (newStart >= sLen || newEnd < 0) { if (newStart >= sLen || newEnd < 0) {
return ""; return "";
} }
return s.substring(newStart, newEnd); return s.substring(newStart, newEnd);
} }
} }