diff options
24 files changed, 647 insertions, 239 deletions
diff --git a/anttasks/src/com/android/ant/AndroidInitTask.java b/anttasks/src/com/android/ant/AndroidInitTask.java index 84c1d27..30779c5 100644 --- a/anttasks/src/com/android/ant/AndroidInitTask.java +++ b/anttasks/src/com/android/ant/AndroidInitTask.java @@ -19,25 +19,30 @@ package com.android.ant; import com.android.sdklib.IAndroidTarget; import com.android.sdklib.ISdkLog; import com.android.sdklib.SdkManager; +import com.android.sdklib.IAndroidTarget.IOptionalLibrary; import com.android.sdklib.project.ProjectProperties; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.Project; import org.apache.tools.ant.taskdefs.ImportTask; +import org.apache.tools.ant.types.Path; +import org.apache.tools.ant.types.Path.PathElement; import java.io.File; import java.util.ArrayList; +import java.util.HashSet; /** * Import Target Ant task. This task accomplishes: * <ul> * <li>Gets the project target hash string from {@link ProjectProperties#PROPERTY_TARGET}, - * and resolves it.</li> - * <li>Sets up ant properties so that the rest of the Ant scripts finds: - * <ul> - * <li>Path to the underlying platform to access the build rules ('android-platform')<li> - * </ul> - * </li> + * and resolves it to get the project's {@link IAndroidTarget}.</li> + * <li>Sets up properties so that aapt can find the android.jar in the resolved target.</li> + * <li>Sets up the boot classpath ref so that the <code>javac</code> task knows where to find + * the libraries. This includes the default android.jar from the resolved target but also optional + * libraries provided by the target (if any, when the target is an add-on).</li> + * <li>Imports the build rules located in the resolved target so that the build actually does + * something.</li> * </ul> * * This is used in build.xml/template. @@ -46,8 +51,12 @@ import java.util.ArrayList; public class AndroidInitTask extends ImportTask { private final static String ANDROID_RULES = "android_rules.xml"; + // ant property with the path to the android.jar private final static String PROPERTY_ANDROID_JAR = "android-jar"; + // ant property with the path to the framework.jar private final static String PROPERTY_ANDROID_AIDL = "android-aidl"; + // ref id to the <path> object containing all the boot classpaths. + private final static String REF_CLASSPATH = "android.target.classpath"; @Override public void execute() throws BuildException { @@ -117,13 +126,39 @@ public class AndroidInitTask extends ImportTask { System.out.println("Platform Version: " + androidTarget.getApiVersionName()); System.out.println("API level: " + androidTarget.getApiVersionNumber()); - // sets up the properties. + // sets up the properties to find android.jar/framework.aidl String androidJar = androidTarget.getPath(IAndroidTarget.ANDROID_JAR); String androidAidl = androidTarget.getPath(IAndroidTarget.ANDROID_AIDL); - antProject.setProperty(PROPERTY_ANDROID_JAR, androidJar); antProject.setProperty(PROPERTY_ANDROID_AIDL, androidAidl); + + // sets up the boot classpath + + // create the Path object + Path bootclasspath = new Path(antProject); + + // create a PathElement for the framework jar + PathElement element = bootclasspath.createPathElement(); + element.setPath(androidJar); + + // create PathElement for each optional library. + IOptionalLibrary[] libraries = androidTarget.getOptionalLibraries(); + if (libraries != null) { + HashSet<String> visitedJars = new HashSet<String>(); + for (IOptionalLibrary library : libraries) { + String jarPath = library.getJarPath(); + if (visitedJars.contains(jarPath) == false) { + visitedJars.add(jarPath); + + element = bootclasspath.createPathElement(); + element.setPath(library.getJarPath()); + } + } + } + // finally sets the path in the project with a reference + antProject.addReference(REF_CLASSPATH, bootclasspath); + // find the file to import, and import it. String templateFolder = androidTarget.getPath(IAndroidTarget.TEMPLATES); diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/.classpath b/eclipse/plugins/com.android.ide.eclipse.adt/.classpath index bbcdff9..c3c8c10 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/.classpath +++ b/eclipse/plugins/com.android.ide.eclipse.adt/.classpath @@ -10,7 +10,7 @@ <classpathentry kind="lib" path="layoutlib_api.jar"/> <classpathentry kind="lib" path="layoutlib_utils.jar"/> <classpathentry kind="lib" path="ninepatch.jar"/> - <classpathentry kind="lib" path="sdklib.jar"/> - <classpathentry kind="lib" path="sdkuilib.jar"/> + <classpathentry combineaccessrules="false" kind="src" path="/SdkLib"/> + <classpathentry combineaccessrules="false" kind="src" path="/SdkUiLib"/> <classpathentry kind="output" path="bin"/> </classpath> diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ApkBuilder.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ApkBuilder.java index e38419a..6743246 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ApkBuilder.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ApkBuilder.java @@ -648,8 +648,11 @@ public class ApkBuilder extends BaseBuilder { return false; } } catch (Throwable ex) { - String message = String.format(Messages.Dalvik_Error_s, - ex.getMessage()); + String message = ex.getMessage(); + if (message == null) { + message = ex.getClass().getCanonicalName(); + } + message = String.format(Messages.Dalvik_Error_s, message); AdtPlugin.printErrorToConsole(getProject(), message); markProject(AdtConstants.MARKER_ADT, message, IMarker.SEVERITY_ERROR); if ((ex instanceof NoClassDefFoundError) diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/internal/AndroidClasspathContainer.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/internal/AndroidClasspathContainer.java index 945fe52..c7cb427 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/internal/AndroidClasspathContainer.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/internal/AndroidClasspathContainer.java @@ -32,12 +32,12 @@ class AndroidClasspathContainer implements IClasspathContainer { /** * Constructs the container with the {@link IClasspathEntry} representing the android * framework jar file and the container id - * @param entry the entry representing the android framework. + * @param entries the entries representing the android framework and optional libraries. * @param path the path containing the classpath container id. * @param name the name of the container to display. */ - AndroidClasspathContainer(IClasspathEntry entry, IPath path, String name) { - mClasspathEntry = new IClasspathEntry[] { entry }; + AndroidClasspathContainer(IClasspathEntry[] entries, IPath path, String name) { + mClasspathEntry = entries; mContainerPath = path; mName = name; } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/internal/AndroidClasspathContainerInitializer.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/internal/AndroidClasspathContainerInitializer.java index 4da216c..afa1fb5 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/internal/AndroidClasspathContainerInitializer.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/internal/AndroidClasspathContainerInitializer.java @@ -22,6 +22,7 @@ import com.android.ide.eclipse.adt.sdk.LoadStatus; import com.android.ide.eclipse.adt.sdk.Sdk; import com.android.ide.eclipse.common.project.BaseProjectHelper; import com.android.sdklib.IAndroidTarget; +import com.android.sdklib.IAndroidTarget.IOptionalLibrary; import org.eclipse.core.resources.IMarker; import org.eclipse.core.resources.IProject; @@ -43,6 +44,9 @@ import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.JavaModelException; +import java.util.ArrayList; +import java.util.HashSet; + /** * Classpath container initializer responsible for binding {@link AndroidClasspathContainer} to * {@link IProject}s. This removes the hard-coded path to the android.jar. @@ -141,7 +145,6 @@ public class AndroidClasspathContainerInitializer extends ClasspathContainerInit // just log the error AdtPlugin.log(ce, "Error removing target marker."); } - // First we check if the SDK has been loaded. // By passing the javaProject to getSdkLoadStatus(), we ensure that, should the SDK @@ -255,15 +258,19 @@ public class AndroidClasspathContainerInitializer extends ClasspathContainerInit } /** - * Creates and returns a new {@link IClasspathEntry} object for the android - * framework. <p/>This references the OS path to the android.jar and the + * Creates and returns an array of {@link IClasspathEntry} objects for the android + * framework and optional libraries. + * <p/>This references the OS path to the android.jar and the * java doc directory. This is dynamically created when a project is opened, * and never saved in the project itself, so there's no risk of storing an * obsolete path. * * @param target The target that contains the libraries. */ - private static IClasspathEntry createFrameworkClasspath(IAndroidTarget target) { + private static IClasspathEntry[] createFrameworkClasspath(IAndroidTarget target) { + ArrayList<IClasspathEntry> list = new ArrayList<IClasspathEntry>(); + + // First, we create the IClasspathEntry for the framework. // now add the android framework to the class path. // create the path object. IPath android_lib = new Path(target.getPath(IAndroidTarget.ANDROID_JAR)); @@ -278,14 +285,49 @@ public class AndroidClasspathContainerInitializer extends ClasspathContainerInit new Path("com/android/internal/**"), //$NON-NLS-1$ IAccessRule.K_NON_ACCESSIBLE); - IClasspathEntry classpathEntry = JavaCore.newLibraryEntry(android_lib, + IClasspathEntry frameworkClasspathEntry = JavaCore.newLibraryEntry(android_lib, android_src, // source attachment path null, // default source attachment root path. new IAccessRule[] { accessRule }, new IClasspathAttribute[] { cpAttribute }, false // not exported. ); + + list.add(frameworkClasspathEntry); + + // now deal with optional libraries + IOptionalLibrary[] libraries = target.getOptionalLibraries(); + if (libraries != null) { + HashSet<String> visitedJars = new HashSet<String>(); + for (IOptionalLibrary library : libraries) { + String jarPath = library.getJarPath(); + if (visitedJars.contains(jarPath) == false) { + visitedJars.add(jarPath); + + // create the java doc link, if needed + String targetDocPath = target.getPath(IAndroidTarget.DOCS); + IClasspathAttribute[] attributes = null; + if (targetDocPath != null) { + attributes = new IClasspathAttribute[] { + JavaCore.newClasspathAttribute( + IClasspathAttribute.JAVADOC_LOCATION_ATTRIBUTE_NAME, + targetDocPath) + }; + } + + IClasspathEntry entry = JavaCore.newLibraryEntry( + new Path(library.getJarPath()), + null, // source attachment path + null, // default source attachment root path. + null, + attributes, + false // not exported. + ); + list.add(entry); + } + } + } - return classpathEntry; + return list.toArray(new IClasspathEntry[list.size()]); } } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/Sdk.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/Sdk.java index 19f8f45..a0a3603 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/Sdk.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/Sdk.java @@ -145,7 +145,9 @@ public class Sdk { /** * Returns a target from a hash that was generated by {@link IAndroidTarget#hashString()}. - * @param hash the hash + * + * @param hash the {@link IAndroidTarget} hash string. + * @return The matching {@link IAndroidTarget} or null. */ public IAndroidTarget getTargetFromHashString(String hash) { return mManager.getTargetFromHashString(hash); diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newproject/NewProjectCreationPage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newproject/NewProjectCreationPage.java index 7fc94ef..33ec2bc 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newproject/NewProjectCreationPage.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newproject/NewProjectCreationPage.java @@ -27,6 +27,8 @@ import com.android.ide.eclipse.common.AndroidConstants; import com.android.ide.eclipse.common.project.AndroidManifestHelper; import com.android.sdklib.IAndroidTarget; import com.android.sdklib.SdkConstants; +import com.android.sdklib.project.ProjectProperties; +import com.android.sdklib.project.ProjectProperties.PropertyType; import com.android.sdkuilib.SdkTargetSelector; import org.eclipse.core.filesystem.URIUtil; @@ -129,6 +131,7 @@ public class NewProjectCreationPage extends WizardPage { protected boolean mProjectNameModifiedByUser; protected boolean mApplicationNameModifiedByUser; private boolean mInternalMinSdkVersionUpdate; + private boolean mMinSdkVersionModifiedByUser; /** @@ -402,6 +405,7 @@ public class NewProjectCreationPage extends WizardPage { mSdkTargetSelector.setSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { + onSdkTargetModified(); updateLocationPathField(null); setPageComplete(validatePage()); } @@ -735,6 +739,14 @@ public class NewProjectCreationPage extends WizardPage { try { int version = Integer.parseInt(getMinSdkVersion()); + // Before changing, compare with the currently selected one, if any. + // There can be multiple targets with the same sdk api version, so don't change + // it if it's already at the right version. + IAndroidTarget curr_target = getSdkTarget(); + if (curr_target != null && curr_target.getApiVersionNumber() == version) { + return; + } + for (IAndroidTarget target : mSdkTargetSelector.getTargets()) { if (target.getApiVersionNumber() == version) { mSdkTargetSelector.setSelection(target); @@ -744,6 +756,24 @@ public class NewProjectCreationPage extends WizardPage { } catch (NumberFormatException e) { // ignore } + + mMinSdkVersionModifiedByUser = true; + } + + /** + * Called when an SDK target is modified. + * + * If the minSdkVersion field hasn't been modified by the user yet, we change it + * to reflect the sdk api level that has just been selected. + */ + private void onSdkTargetModified() { + IAndroidTarget target = getSdkTarget(); + + if (target != null && !mMinSdkVersionModifiedByUser) { + mInternalMinSdkVersionUpdate = true; + mMinSdkVersionField.setText(Integer.toString(target.getApiVersionNumber())); + mInternalMinSdkVersionUpdate = false; + } } /** @@ -872,9 +902,22 @@ public class NewProjectCreationPage extends WizardPage { } } - // Select the target matching the manifest's sdk, if any + // Select the target matching the manifest's sdk or build properties, if any boolean foundTarget = false; - if (minSdkVersion != null) { + + ProjectProperties p = ProjectProperties.create(projectLocation, null); + if (p != null) { + // Check the {build|default}.properties files if present + p.merge(PropertyType.BUILD).merge(PropertyType.DEFAULT); + String v = p.getProperty(ProjectProperties.PROPERTY_TARGET); + IAndroidTarget target = Sdk.getCurrent().getTargetFromHashString(v); + if (target != null) { + mSdkTargetSelector.setSelection(target); + foundTarget = true; + } + } + + if (!foundTarget && minSdkVersion != null) { try { int sdkVersion = Integer.parseInt(minSdkVersion); diff --git a/emulator/qemud/qemud.c b/emulator/qemud/qemud.c index 3a53716..ae6797e 100644 --- a/emulator/qemud/qemud.c +++ b/emulator/qemud/qemud.c @@ -1250,7 +1250,7 @@ static Multiplexer _multiplexer[1]; static const struct { const char* name; ChannelType ctype; } default_channels[] = { { "gsm", CHANNEL_DUPLEX }, /* GSM AT command channel, used by commands/rild/rild.c */ - { "gps", CHANNEL_BROADCAST }, /* GPS NMEA commands, used by libs/hardware/qemu_gps.c */ + { "gps", CHANNEL_BROADCAST }, /* GPS NMEA commands, used by libs/hardware_legacy/qemu_gps.c */ { "control", CHANNEL_DUPLEX }, /* Used for power/leds/vibrator/etc... */ { NULL, 0 } }; diff --git a/scripts/android_rules.xml b/scripts/android_rules.xml index 1331696..ce34e28 100644 --- a/scripts/android_rules.xml +++ b/scripts/android_rules.xml @@ -104,7 +104,7 @@ <javac encoding="ascii" target="1.5" debug="true" extdirs="" srcdir="${source-folder}" destdir="${out-classes}" - bootclasspath="${android-jar}"> + bootclasspathref="android.target.classpath"> <classpath> <fileset dir="${external-libs}" includes="*.jar"/> <pathelement path="${main-out-classes}"/> diff --git a/scripts/build.template b/scripts/build.template index c1afef8..8923428 100644 --- a/scripts/build.template +++ b/scripts/build.template @@ -45,7 +45,7 @@ <taskdef name="androidinit" classname="com.android.ant.AndroidInitTask" classpathref="android.antlibs"/> - <!-- Class the Android Init task that will import the proper rule file containing - all the Ant targets --> + <!-- Execute the Android Init task that will import the proper rule file containing + all the Ant targets, as well as setup some properties specific to the target. --> <androidinit /> </project> diff --git a/sdkmanager/app/etc/android.bat b/sdkmanager/app/etc/android.bat index 2aa458d..1af1e47 100755 --- a/sdkmanager/app/etc/android.bat +++ b/sdkmanager/app/etc/android.bat @@ -20,6 +20,9 @@ rem Set up prog to be the path of this script, including following symlinks, rem and set up progdir to be the fully-qualified pathname of its directory. set prog=%~f0 +rem Grab current directory before we change it +set workdir=%cd% + rem Change current directory to where ddms is, to avoid issues with directories rem containing whitespaces. cd %~dp0 @@ -45,4 +48,4 @@ if debug NEQ "%1" goto NoDebug set jarpath=%frameworkdir%%jarfile% -call java %java_debug% -Djava.ext.dirs=%frameworkdir% -Djava.library.path=%libdir% -Dcom.android.sdkmanager.toolsdir= -jar %jarpath% %* +call java %java_debug% -Djava.ext.dirs=%frameworkdir% -Djava.library.path=%libdir% -Dcom.android.sdkmanager.toolsdir= -Dcom.android.sdkmanager.workdir="%workdir%" -jar %jarpath% %* diff --git a/sdkmanager/app/src/com/android/sdkmanager/CommandLineProcessor.java b/sdkmanager/app/src/com/android/sdkmanager/CommandLineProcessor.java index ef3d0ee..d63272d 100644 --- a/sdkmanager/app/src/com/android/sdkmanager/CommandLineProcessor.java +++ b/sdkmanager/app/src/com/android/sdkmanager/CommandLineProcessor.java @@ -16,6 +16,8 @@ package com.android.sdkmanager; +import com.android.sdklib.ISdkLog; + import java.util.HashMap; import java.util.Map.Entry; @@ -57,8 +59,10 @@ public class CommandLineProcessor { * The key is a string "action/longName". */ private final HashMap<String, Arg> mArguments = new HashMap<String, Arg>(); + private final ISdkLog mLog; - public CommandLineProcessor(String[][] actions) { + public CommandLineProcessor(ISdkLog logger, String[][] actions) { + mLog = logger; mActions = actions; define(MODE.STRING, false, INTERNAL_FLAG, null, KEY_ACTION, "Selected Action", null); @@ -564,7 +568,7 @@ public class CommandLineProcessor { * @param args Format arguments. */ protected void stdout(String format, Object...args) { - System.out.println(String.format(format, args)); + mLog.printf(format + "\n", args); } /** @@ -575,6 +579,6 @@ public class CommandLineProcessor { * @param args Format arguments. */ protected void stderr(String format, Object...args) { - System.err.println(String.format(format, args)); + mLog.error(null, format, args); } } diff --git a/sdkmanager/app/src/com/android/sdkmanager/Main.java b/sdkmanager/app/src/com/android/sdkmanager/Main.java index 3bcb9a3..b50b113 100644 --- a/sdkmanager/app/src/com/android/sdkmanager/Main.java +++ b/sdkmanager/app/src/com/android/sdkmanager/Main.java @@ -40,17 +40,28 @@ import java.util.Map; * Main class for the 'android' application. */ class Main { - + + /** Java property that defines the location of the sdk/tools directory. */ private final static String TOOLSDIR = "com.android.sdkmanager.toolsdir"; + /** Java property that defines the working directory. On Windows the current working directory + * is actually the tools dir, in which case this is used to get the original CWD. */ + private final static String WORKDIR = "com.android.sdkmanager.workdir"; private final static String[] BOOLEAN_YES_REPLIES = new String[] { "yes", "y" }; private final static String[] BOOLEAN_NO_REPLIES = new String[] { "no", "n" }; + /** Path to the SDK folder. This is the parent of {@link #TOOLSDIR}. */ private String mSdkFolder; + /** Logger object. Use this to print normal output, warnings or errors. */ private ISdkLog mSdkLog; + /** The SDK manager parses the SDK folder and gives access to the content. */ private SdkManager mSdkManager; + /** Virtual Machine manager to access the list of VMs or create new ones. */ private VmManager mVmManager; + /** Command-line processor with options specific to SdkManager. */ private SdkCommandLine mSdkCommandLine; + /** The working directory, either null or set to an existing absolute canonical directory. */ + private File mWorkDir; public static void main(String[] args) { new Main().run(args); @@ -58,9 +69,9 @@ class Main { /** * Runs the sdk manager app - * @param args */ private void run(String[] args) { + createLogger(); init(); mSdkCommandLine.parseArgs(args); parseSdk(); @@ -68,13 +79,47 @@ class Main { } /** + * Creates the {@link #mSdkLog} object. + * <p/> + * This must be done before {@link #init()} as it will be used to report errors. + */ + private void createLogger() { + mSdkLog = new ISdkLog() { + public void error(Throwable t, String errorFormat, Object... args) { + if (errorFormat != null) { + System.err.printf("Error: " + errorFormat, args); + if (!errorFormat.endsWith("\n")) { + System.err.printf("\n"); + } + } + if (t != null) { + System.err.printf("Error: %s\n", t.getMessage()); + } + } + + public void warning(String warningFormat, Object... args) { + if (mSdkCommandLine.isVerbose()) { + System.out.printf("Warning: " + warningFormat, args); + if (!warningFormat.endsWith("\n")) { + System.out.printf("\n"); + } + } + } + + public void printf(String msgFormat, Object... args) { + System.out.printf(msgFormat, args); + } + }; + } + + /** * Init the application by making sure the SDK path is available and * doing basic parsing of the SDK. */ private void init() { - mSdkCommandLine = new SdkCommandLine(); + mSdkCommandLine = new SdkCommandLine(mSdkLog); - /* We get passed a property for the tools dir */ + // We get passed a property for the tools dir String toolsDirProp = System.getProperty(TOOLSDIR); if (toolsDirProp == null) { // for debugging, it's easier to override using the process environment @@ -98,15 +143,28 @@ class Main { } if (mSdkFolder == null) { - String os = System.getProperty("os.name"); - String cmd = "android"; - if (os.startsWith("Windows")) { - cmd += ".bat"; + errorAndExit("The tools directory property is not set, please make sure you are executing %1$s", + SdkConstants.AndroidCmdName()); + } + + // We might get passed a property for the working directory + // Either it is a valid directory and mWorkDir is set to it's absolute canonical value + // or mWorkDir remains null. + String workDirProp = System.getProperty(WORKDIR); + if (workDirProp == null) { + workDirProp = System.getenv(WORKDIR); + } + if (workDirProp != null) { + // This should be a valid directory + mWorkDir = new File(workDirProp); + try { + mWorkDir = mWorkDir.getCanonicalFile().getAbsoluteFile(); + } catch (IOException e) { + mWorkDir = null; + } + if (mWorkDir == null || !mWorkDir.isDirectory()) { + errorAndExit("The working directory does not seem to be valid: '%1$s", workDirProp); } - - mSdkCommandLine.printHelpAndExit( - "ERROR: The tools directory property is not set, please make sure you are executing %1$s", - cmd); } } @@ -114,33 +172,10 @@ class Main { * Does the basic SDK parsing required for all actions */ private void parseSdk() { - mSdkLog = new ISdkLog() { - public void error(Throwable t, String errorFormat, Object... args) { - if (errorFormat != null) { - System.err.printf("Error: " + errorFormat, args); - System.err.println(""); - } - if (t != null) { - System.err.print("Error: " + t.getMessage()); - } - } - - public void warning(String warningFormat, Object... args) { - if (false) { - // TODO: on display warnings in verbose mode. - System.out.printf("Warning: " + warningFormat, args); - System.out.println(""); - } - } - - public void printf(String msgFormat, Object... args) { - System.out.printf(msgFormat, args); - } - }; mSdkManager = SdkManager.createManager(mSdkFolder, mSdkLog); if (mSdkManager == null) { - mSdkCommandLine.printHelpAndExit("ERROR: Unable to parse SDK content."); + errorAndExit("Unable to parse SDK content."); } } @@ -167,67 +202,108 @@ class Main { int targetId = mSdkCommandLine.getNewProjectTargetId(); IAndroidTarget[] targets = mSdkManager.getTargets(); if (targetId < 1 || targetId > targets.length) { - mSdkCommandLine.printHelpAndExit("ERROR: Wrong target id."); + errorAndExit("Target id is not valid. Use '%s list -f target' to get the target Ids.", + SdkConstants.AndroidCmdName()); } IAndroidTarget target = targets[targetId - 1]; ProjectCreator creator = new ProjectCreator(mSdkFolder, - OutputLevel.NORMAL, mSdkLog); + mSdkCommandLine.isVerbose() ? OutputLevel.VERBOSE : OutputLevel.NORMAL, + mSdkLog); + + String projectDir = getProjectLocation(mSdkCommandLine.getNewProjectLocation()); - creator.createProject(mSdkCommandLine.getNewProjectLocation(), + creator.createProject(projectDir, mSdkCommandLine.getNewProjectName(), mSdkCommandLine.getNewProjectPackage(), - mSdkCommandLine.getNewProjectActivity(), target, true); + mSdkCommandLine.getNewProjectActivity(), target, false /* isTestProject*/); } else { mSdkCommandLine.printHelpAndExit(null); } } /** + * Adjusts the project location to make it absolute & canonical relative to the + * working directory, if any. + * + * @return The project absolute path relative to {@link #mWorkDir} or the original + * newProjectLocation otherwise. + */ + private String getProjectLocation(String newProjectLocation) { + + // If the new project location is absolute, use it as-is + File projectDir = new File(newProjectLocation); + if (projectDir.isAbsolute()) { + return newProjectLocation; + } + + // if there's no working directory, just use the project location as-is. + if (mWorkDir == null) { + return newProjectLocation; + } + + // Combine then and get an absolute canonical directory + try { + projectDir = new File(mWorkDir, newProjectLocation).getCanonicalFile(); + + return projectDir.getPath(); + } catch (IOException e) { + errorAndExit("Failed to combine working directory '%1$s' with project location '%2$s': %3$s", + mWorkDir.getPath(), + newProjectLocation, + e.getMessage()); + return null; + } + } + + /** * Displays the list of available Targets (Platforms and Add-ons) */ private void displayTargetList() { - System.out.println("Available Android targets:"); + mSdkLog.printf("Available Android targets:\n"); int index = 1; for (IAndroidTarget target : mSdkManager.getTargets()) { if (target.isPlatform()) { - System.out.printf("[%d] %s\n", index, target.getName()); - System.out.printf(" API level: %d\n", target.getApiVersionNumber()); + mSdkLog.printf("[%d] %s\n", index, target.getName()); + mSdkLog.printf(" API level: %d\n", target.getApiVersionNumber()); } else { - System.out.printf("[%d] Add-on: %s\n", index, target.getName()); - System.out.printf(" Vendor: %s\n", target.getVendor()); + mSdkLog.printf("[%d] Add-on: %s\n", index, target.getName()); + mSdkLog.printf(" Vendor: %s\n", target.getVendor()); if (target.getDescription() != null) { - System.out.printf(" Description: %s\n", target.getDescription()); + mSdkLog.printf(" Description: %s\n", target.getDescription()); } - System.out.printf(" Based on Android %s (API level %d)\n", + mSdkLog.printf(" Based on Android %s (API level %d)\n", target.getApiVersionName(), target.getApiVersionNumber()); // display the optional libraries. IOptionalLibrary[] libraries = target.getOptionalLibraries(); if (libraries != null) { + mSdkLog.printf(" Libraries:\n"); for (IOptionalLibrary library : libraries) { - System.out.printf(" Library: %s (%s)\n", library.getName(), - library.getJarName()); + mSdkLog.printf(" * %1$s (%2$s)\n", + library.getName(), library.getJarName()); + mSdkLog.printf(String.format( + " %1$s\n", library.getDescription())); } } } // get the target skins String[] skins = target.getSkins(); - System.out.print(" Skins: "); + mSdkLog.printf(" Skins: "); if (skins != null) { boolean first = true; for (String skin : skins) { if (first == false) { - System.out.print(", "); + mSdkLog.printf(", "); } else { first = false; } - System.out.print(skin); + mSdkLog.printf(skin); } - System.out.println(""); + mSdkLog.printf("\n"); } else { - System.out.println("no skins."); + mSdkLog.printf("no skins.\n"); } index++; @@ -241,30 +317,29 @@ class Main { try { mVmManager = new VmManager(mSdkManager, null /* sdklog */); - System.out.println("Available Android VMs:"); + mSdkLog.printf("Available Android VMs:\n"); int index = 1; for (VmInfo info : mVmManager.getVms()) { - System.out.printf("[%d] %s\n", index, info.getName()); - System.out.printf(" Path: %s\n", info.getPath()); + mSdkLog.printf("[%d] %s\n", index, info.getName()); + mSdkLog.printf(" Path: %s\n", info.getPath()); // get the target of the Vm IAndroidTarget target = info.getTarget(); if (target.isPlatform()) { - System.out.printf(" Target: %s (API level %d)\n", target.getName(), + mSdkLog.printf(" Target: %s (API level %d)\n", target.getName(), target.getApiVersionNumber()); } else { - System.out.printf(" Target: %s (%s)\n", target.getName(), target + mSdkLog.printf(" Target: %s (%s)\n", target.getName(), target .getVendor()); - System.out.printf(" Based on Android %s (API level %d)\n", target + mSdkLog.printf(" Based on Android %s (API level %d)\n", target .getApiVersionName(), target.getApiVersionNumber()); - } index++; } } catch (AndroidLocationException e) { - mSdkCommandLine.printHelpAndExit(e.getMessage()); + errorAndExit(e.getMessage()); } } @@ -279,33 +354,43 @@ class Main { if (targetId >= 1 && targetId <= mSdkManager.getTargets().length) { target = mSdkManager.getTargets()[targetId-1]; // target it is 1-based } else { - mSdkCommandLine.printHelpAndExit( - "ERROR: Target Id is not a valid Id. Check 'android list target' for the list of targets."); + errorAndExit("Target id is not valid. Use '%s list -f target' to get the target Ids.", + SdkConstants.AndroidCmdName()); } - + try { - // default to standard path now - String vmRoot = AndroidLocation.getFolder() + AndroidLocation.FOLDER_VMS; - - Map<String, String> hardwareConfig = null; - if (target.isPlatform()) { - try { - hardwareConfig = promptForHardware(target); - } catch (IOException e) { - mSdkCommandLine.printHelpAndExit(e.getMessage()); + mVmManager = new VmManager(mSdkManager, mSdkLog); + + String vmName = mSdkCommandLine.getNewVmName(); + VmInfo info = mVmManager.getVm(vmName); + if (info != null) { + errorAndExit("VM %s already exists.", vmName); + } else { + String vmParentFolder = mSdkCommandLine.getNewVmLocation(); + if (vmParentFolder == null) { + vmParentFolder = AndroidLocation.getFolder() + AndroidLocation.FOLDER_VMS; } + + Map<String, String> hardwareConfig = null; + if (target.isPlatform()) { + try { + hardwareConfig = promptForHardware(target); + } catch (IOException e) { + errorAndExit(e.getMessage()); + } + } + + mVmManager.createVm(vmParentFolder, + mSdkCommandLine.getNewVmName(), + target, + mSdkCommandLine.getNewVmSkin(), + null /*sdcardPath*/, + 0 /*sdcardSize*/, + hardwareConfig, + mSdkLog); } - - VmManager.createVm(vmRoot, - mSdkCommandLine.getNewVmName(), - target, - null /*skinName*/, - null /*sdcardPath*/, - 0 /*sdcardSize*/, - hardwareConfig, - null /* sdklog */); } catch (AndroidLocationException e) { - mSdkCommandLine.printHelpAndExit(e.getMessage()); + errorAndExit(e.getMessage()); } } @@ -318,10 +403,9 @@ class Main { String result; String defaultAnswer = "no"; - System.out.print(String.format("%s is a basic Android platform.\n", - createTarget.getName())); - System.out.print(String.format("Do you which to create a custom hardware profile [%s]", - defaultAnswer)); + mSdkLog.printf("%s is a basic Android platform.\n", createTarget.getName()); + mSdkLog.printf("Do you wish to create a custom hardware profile [%s]", + defaultAnswer); result = readLine(readLineBuffer).trim(); // handle default: @@ -334,7 +418,7 @@ class Main { return null; } - System.out.println(""); // empty line + mSdkLog.printf("\n"); // empty line // get the list of possible hardware properties File hardwareDefs = new File (mSdkFolder + File.separator + @@ -349,23 +433,23 @@ class Main { String description = property.getDescription(); if (description != null) { - System.out.printf("%s: %s\n", property.getAbstract(), description); + mSdkLog.printf("%s: %s\n", property.getAbstract(), description); } else { - System.out.println(property.getAbstract()); + mSdkLog.printf("%s\n", property.getAbstract()); } String defaultValue = property.getDefault(); if (defaultValue != null) { - System.out.printf("%s [%s]:", property.getName(), defaultValue); + mSdkLog.printf("%s [%s]:", property.getName(), defaultValue); } else { - System.out.printf("%s (%s):", property.getName(), property.getType()); + mSdkLog.printf("%s (%s):", property.getName(), property.getType()); } result = readLine(readLineBuffer); if (result.length() == 0) { if (defaultValue != null) { - System.out.println(""); // empty line + mSdkLog.printf("\n"); // empty line i++; // go to the next property if we have a valid default value. // if there's no default, we'll redo this property } @@ -384,7 +468,7 @@ class Main { } } catch (IOException e) { // display error, and do not increment i to redo this property - System.out.println("\n" + e.getMessage()); + mSdkLog.printf("\n%s\n", e.getMessage()); } break; case INTEGER: @@ -394,7 +478,7 @@ class Main { i++; // valid reply, move to next property } catch (NumberFormatException e) { // display error, and do not increment i to redo this property - System.out.println("\n" + e.getMessage()); + mSdkLog.printf("\n%s\n", e.getMessage()); } break; case DISKSIZE: @@ -404,7 +488,7 @@ class Main { break; } - System.out.println(""); // empty line + mSdkLog.printf("\n"); // empty line } return map; @@ -458,4 +542,9 @@ class Main { throw new IOException(String.format("%s is not a valid reply", reply)); } + + private void errorAndExit(String format, Object...args) { + mSdkLog.error(null, format, args); + System.exit(1); + } }
\ No newline at end of file diff --git a/sdkmanager/app/src/com/android/sdkmanager/SdkCommandLine.java b/sdkmanager/app/src/com/android/sdkmanager/SdkCommandLine.java index 918c534..39c80b1 100644 --- a/sdkmanager/app/src/com/android/sdkmanager/SdkCommandLine.java +++ b/sdkmanager/app/src/com/android/sdkmanager/SdkCommandLine.java @@ -16,6 +16,7 @@ package com.android.sdkmanager; +import com.android.sdklib.ISdkLog; import com.android.sdklib.SdkManager; @@ -38,6 +39,8 @@ public class SdkCommandLine extends CommandLineProcessor { public static final String KEY_NAME = "name"; public static final String KEY_OUT = "out"; public static final String KEY_FILTER = "filter"; + public static final String KEY_SKIN = "skin"; + public static final String KEY_SDCARD_PATH = "sdcard"; public final static String ACTION_LIST = "list"; public final static String ACTION_NEW_VM = ARG_VM; @@ -52,11 +55,11 @@ public class SdkCommandLine extends CommandLineProcessor { { ACTION_NEW_PROJECT, "Creates a new project using a template." }, { ACTION_UPDATE_PROJECT, - "Updates a new project from existing source (must have an AndroidManifest.xml)." }, + "Updates a project from existing source (must have an AndroidManifest.xml)." }, }; - public SdkCommandLine() { - super(ACTIONS); + public SdkCommandLine(ISdkLog logger) { + super(logger, ACTIONS); define(MODE.ENUM, false, ACTION_LIST, "f", KEY_FILTER, "List filter", new String[] { ARG_ALL, ARG_TARGET, ARG_VM }); @@ -67,6 +70,10 @@ public class SdkCommandLine extends CommandLineProcessor { "Name of the new VM", null); define(MODE.INTEGER, true, ACTION_NEW_VM, "t", KEY_TARGET_ID, "Target id of the new VM", null); + define(MODE.STRING, true, ACTION_NEW_VM, "s", KEY_SKIN, + "Skin of the new VM", null); + define(MODE.STRING, true, ACTION_NEW_VM, "p", KEY_SDCARD_PATH, + "Path to a shared SD card image for the new VM", null); define(MODE.ENUM, true, ACTION_NEW_PROJECT, "m", KEY_MODE, "Project mode", new String[] { ARG_ACTIVITY, ARG_ALIAS }); @@ -110,6 +117,17 @@ public class SdkCommandLine extends CommandLineProcessor { public String getNewVmName() { return ((String) getValue(ACTION_NEW_VM, KEY_NAME)); } + + /** Helper to retrieve the --skin name for the new vm action. */ + public String getNewVmSkin() { + return ((String) getValue(ACTION_NEW_VM, KEY_SKIN)); + } + + /** Helper to retrieve the --sdcard name for the new vm action. */ + public String getNewVmSdCard() { + return ((String) getValue(ACTION_NEW_VM, KEY_SDCARD_PATH)); + } + // -- some helpers for project action flags diff --git a/sdkmanager/app/tests/com/android/sdkmanager/CommandLineProcessorTest.java b/sdkmanager/app/tests/com/android/sdkmanager/CommandLineProcessorTest.java index e74cdbd..1a82151 100644 --- a/sdkmanager/app/tests/com/android/sdkmanager/CommandLineProcessorTest.java +++ b/sdkmanager/app/tests/com/android/sdkmanager/CommandLineProcessorTest.java @@ -16,11 +16,15 @@ package com.android.sdkmanager; +import com.android.sdklib.ISdkLog; + import junit.framework.TestCase; public class CommandLineProcessorTest extends TestCase { + private MockStdLogger mLog; + /** * A mock version of the {@link CommandLineProcessor} class that does not * exits and captures its stdout/stderr output. @@ -31,8 +35,9 @@ public class CommandLineProcessorTest extends TestCase { private String mStdOut = ""; private String mStdErr = ""; - public MockCommandLineProcessor() { - super(new String[][] { + public MockCommandLineProcessor(ISdkLog logger) { + super(logger, + new String[][] { { "action1", "Some action" }, { "action2", "Another action" }, }); @@ -84,9 +89,10 @@ public class CommandLineProcessorTest extends TestCase { return mStdErr; } } - + @Override protected void setUp() throws Exception { + mLog = new MockStdLogger(); super.setUp(); } @@ -96,7 +102,7 @@ public class CommandLineProcessorTest extends TestCase { } public final void testPrintHelpAndExit() { - MockCommandLineProcessor c = new MockCommandLineProcessor(); + MockCommandLineProcessor c = new MockCommandLineProcessor(mLog); assertFalse(c.wasExitCalled()); assertFalse(c.wasHelpCalled()); assertTrue(c.getStdOut().equals("")); @@ -107,7 +113,7 @@ public class CommandLineProcessorTest extends TestCase { assertTrue(c.getStdErr().equals("")); assertTrue(c.wasExitCalled()); - c = new MockCommandLineProcessor(); + c = new MockCommandLineProcessor(mLog); assertFalse(c.wasExitCalled()); assertTrue(c.getStdOut().equals("")); assertTrue(c.getStdErr().indexOf("Missing parameter") == -1); @@ -119,7 +125,7 @@ public class CommandLineProcessorTest extends TestCase { } public final void testVerbose() { - MockCommandLineProcessor c = new MockCommandLineProcessor(); + MockCommandLineProcessor c = new MockCommandLineProcessor(mLog); assertFalse(c.isVerbose()); c.parseArgs(new String[] { "-v" }); @@ -128,7 +134,7 @@ public class CommandLineProcessorTest extends TestCase { assertTrue(c.wasHelpCalled()); assertTrue(c.getStdErr().indexOf("Missing action name.") != -1); - c = new MockCommandLineProcessor(); + c = new MockCommandLineProcessor(mLog); c.parseArgs(new String[] { "--verbose" }); assertTrue(c.isVerbose()); assertTrue(c.wasExitCalled()); @@ -137,14 +143,14 @@ public class CommandLineProcessorTest extends TestCase { } public final void testHelp() { - MockCommandLineProcessor c = new MockCommandLineProcessor(); + MockCommandLineProcessor c = new MockCommandLineProcessor(mLog); c.parseArgs(new String[] { "-h" }); assertTrue(c.wasExitCalled()); assertTrue(c.wasHelpCalled()); assertTrue(c.getStdErr().indexOf("Missing action name.") == -1); - c = new MockCommandLineProcessor(); + c = new MockCommandLineProcessor(mLog); c.parseArgs(new String[] { "--help" }); assertTrue(c.wasExitCalled()); assertTrue(c.wasHelpCalled()); @@ -152,7 +158,7 @@ public class CommandLineProcessorTest extends TestCase { } public final void testMandatory() { - MockCommandLineProcessor c = new MockCommandLineProcessor(); + MockCommandLineProcessor c = new MockCommandLineProcessor(mLog); c.parseArgs(new String[] { "action1", "-1", "value1", "-2", "value2" }); assertFalse(c.wasExitCalled()); @@ -161,7 +167,7 @@ public class CommandLineProcessorTest extends TestCase { assertEquals("value1", c.getValue("action1", "first")); assertEquals("value2", c.getValue("action1", "second")); - c = new MockCommandLineProcessor(); + c = new MockCommandLineProcessor(mLog); c.parseArgs(new String[] { "action1", "-2", "value2" }); assertFalse(c.wasExitCalled()); assertFalse(c.wasHelpCalled()); @@ -169,7 +175,7 @@ public class CommandLineProcessorTest extends TestCase { assertEquals(null, c.getValue("action1", "first")); assertEquals("value2", c.getValue("action1", "second")); - c = new MockCommandLineProcessor(); + c = new MockCommandLineProcessor(mLog); c.parseArgs(new String[] { "action1" }); assertTrue(c.wasExitCalled()); assertTrue(c.wasHelpCalled()); diff --git a/sdkmanager/app/tests/com/android/sdkmanager/SdkCommandLineTest.java b/sdkmanager/app/tests/com/android/sdkmanager/SdkCommandLineTest.java index b943b98..5a2c8e1 100644 --- a/sdkmanager/app/tests/com/android/sdkmanager/SdkCommandLineTest.java +++ b/sdkmanager/app/tests/com/android/sdkmanager/SdkCommandLineTest.java @@ -16,10 +16,14 @@ package com.android.sdkmanager; +import com.android.sdklib.ISdkLog; + import junit.framework.TestCase; public class SdkCommandLineTest extends TestCase { + private MockStdLogger mLog; + /** * A mock version of the {@link SdkCommandLine} class that does not * exits and discards its stdout/stderr output. @@ -28,7 +32,8 @@ public class SdkCommandLineTest extends TestCase { private boolean mExitCalled; private boolean mHelpCalled; - public MockSdkCommandLine() { + public MockSdkCommandLine(ISdkLog logger) { + super(logger); } @Override @@ -64,6 +69,7 @@ public class SdkCommandLineTest extends TestCase { @Override protected void setUp() throws Exception { + mLog = new MockStdLogger(); super.setUp(); } @@ -74,7 +80,7 @@ public class SdkCommandLineTest extends TestCase { /** Test list with long name and verbose */ public final void testList_Long_Verbose() { - MockSdkCommandLine c = new MockSdkCommandLine(); + MockSdkCommandLine c = new MockSdkCommandLine(mLog); assertEquals("all", c.getListFilter()); c.parseArgs(new String[] { "-v", "list", "--filter", "vm" }); assertFalse(c.wasHelpCalled()); @@ -85,7 +91,7 @@ public class SdkCommandLineTest extends TestCase { /** Test list with short name and no verbose */ public final void testList_Short() { - MockSdkCommandLine c = new MockSdkCommandLine(); + MockSdkCommandLine c = new MockSdkCommandLine(mLog); assertEquals("all", c.getListFilter()); c.parseArgs(new String[] { "list", "-f", "vm" }); assertFalse(c.wasHelpCalled()); @@ -95,7 +101,7 @@ public class SdkCommandLineTest extends TestCase { /** Test list with long name and missing parameter */ public final void testList_Long_MissingParam() { - MockSdkCommandLine c = new MockSdkCommandLine(); + MockSdkCommandLine c = new MockSdkCommandLine(mLog); assertEquals("all", c.getListFilter()); c.parseArgs(new String[] { "list", "--filter" }); assertTrue(c.wasHelpCalled()); diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/AddOnTarget.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/AddOnTarget.java index 2a2efe7..ada61f7 100644 --- a/sdkmanager/libs/sdklib/src/com/android/sdklib/AddOnTarget.java +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/AddOnTarget.java @@ -17,6 +17,8 @@ package com.android.sdklib; import java.io.File; +import java.util.Arrays; +import java.util.HashSet; import java.util.Map; import java.util.Map.Entry; @@ -35,11 +37,13 @@ final class AddOnTarget implements IAndroidTarget { private final String mJarName; private final String mJarPath; private final String mName; + private final String mDescription; - OptionalLibrary(String jarName, String jarPath, String name) { + OptionalLibrary(String jarName, String jarPath, String name, String description) { mJarName = jarName; mJarPath = jarPath; mName = name; + mDescription = description; } public String getJarName() { @@ -53,6 +57,10 @@ final class AddOnTarget implements IAndroidTarget { public String getName() { return mName; } + + public String getDescription() { + return mDescription; + } } private final String mLocation; @@ -70,11 +78,11 @@ final class AddOnTarget implements IAndroidTarget { * @param vendor the vendor name of the add-on * @param description the add-on description * @param libMap A map containing the optional libraries. The map key is the fully-qualified - * library name. The value is the .jar filename + * library name. The value is a 2 string array with the .jar filename, and the description. * @param basePlatform the platform the add-on is extending. */ AddOnTarget(String location, String name, String vendor, String description, - Map<String, String> libMap, PlatformTarget basePlatform) { + Map<String, String[]> libMap, PlatformTarget basePlatform) { if (location.endsWith(File.separator) == false) { location = location + File.separator; } @@ -86,12 +94,16 @@ final class AddOnTarget implements IAndroidTarget { mBasePlatform = basePlatform; // handle the optional libraries. - mLibraries = new IOptionalLibrary[libMap.size()]; - int index = 0; - for (Entry<String, String> entry : libMap.entrySet()) { - mLibraries[index++] = new OptionalLibrary(entry.getValue(), - mLocation + SdkConstants.OS_ADDON_LIBS_FOLDER + entry.getValue(), - entry.getKey()); + if (libMap != null) { + mLibraries = new IOptionalLibrary[libMap.size()]; + int index = 0; + for (Entry<String, String[]> entry : libMap.entrySet()) { + String jarFile = entry.getValue()[0]; + String desc = entry.getValue()[1]; + mLibraries[index++] = new OptionalLibrary(jarFile, + mLocation + SdkConstants.OS_ADDON_LIBS_FOLDER + jarFile, + entry.getKey(), desc); + } } } @@ -135,6 +147,8 @@ final class AddOnTarget implements IAndroidTarget { return mLocation + SdkConstants.OS_IMAGES_FOLDER; case SKINS: return mLocation + SdkConstants.OS_SKINS_FOLDER; + case DOCS: + return mLocation + SdkConstants.FD_DOCS + File.separator; default : return mBasePlatform.getPath(pathId); } @@ -223,6 +237,11 @@ final class AddOnTarget implements IAndroidTarget { public void setSkins(String[] skins) { - mSkins = skins; + // we mix the add-on and base platform skins + HashSet<String> skinSet = new HashSet<String>(); + skinSet.addAll(Arrays.asList(skins)); + skinSet.addAll(Arrays.asList(mBasePlatform.getSkins())); + + mSkins = skinSet.toArray(new String[skinSet.size()]); } } diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/IAndroidTarget.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/IAndroidTarget.java index 0e2b109..acf1187 100644 --- a/sdkmanager/libs/sdklib/src/com/android/sdklib/IAndroidTarget.java +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/IAndroidTarget.java @@ -58,11 +58,14 @@ public interface IAndroidTarget extends Comparable<IAndroidTarget> { public static int CATEGORIES = 17; /** OS Path to the "sources" folder. */ public static int SOURCES = 18; + /** OS Path to the target specific docs */ + public static int DOCS = 19; public interface IOptionalLibrary { String getName(); String getJarName(); String getJarPath(); + String getDescription(); } /** @@ -82,7 +85,6 @@ public interface IAndroidTarget extends Comparable<IAndroidTarget> { /** * Returns the full name of the target, possibly including vendor name. - * @return */ String getFullName(); diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkConstants.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkConstants.java index ede0d86..a2de8fc 100644 --- a/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkConstants.java +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkConstants.java @@ -201,7 +201,18 @@ public final class SdkConstants { public final static String OS_ADDON_LIBS_FOLDER = FD_ADDON_LIBS + File.separator; - /* Skin default */ + /** Skin default **/ public final static String SKIN_DEFAULT = "default"; + /** Returns the appropriate name for the 'android' command, which is 'android.bat' for + * Windows and 'android' for all other platforms. */ + public static String AndroidCmdName() { + String os = System.getProperty("os.name"); + String cmd = "android"; + if (os.startsWith("Windows")) { + cmd += ".bat"; + } + return cmd; + } + } diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkManager.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkManager.java index b4de51a..83a90e6 100644 --- a/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkManager.java +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkManager.java @@ -48,6 +48,9 @@ public final class SdkManager { private final static Pattern PATTERN_PROP = Pattern.compile( "^([a-zA-Z0-9._-]+)\\s*=\\s*(.*)\\s*$"); + private final static Pattern PATTERN_LIB_DATA = Pattern.compile( + "^([a-zA-Z0-9._-]+\\.jar);(.*)$", Pattern.CASE_INSENSITIVE); + /** the location of the SDK */ private final String mSdkLocation; private IAndroidTarget[] mTargets; @@ -96,7 +99,9 @@ public final class SdkManager { /** * Returns a target from a hash that was generated by {@link IAndroidTarget#hashString()}. - * @param hash the hash + * + * @param hash the {@link IAndroidTarget} hash string. + * @return The matching {@link IAndroidTarget} or null. */ public IAndroidTarget getTargetFromHashString(String hash) { if (hash != null) { @@ -213,13 +218,12 @@ public final class SdkManager { File[] addons = addonFolder.listFiles(); for (File addon : addons) { + // Add-ons have to be folders. Ignore files and no need to warn about them. if (addon.isDirectory()) { AddOnTarget target = loadAddon(addon, list, log); if (target != null) { list.add(target); } - } else if (log != null) { - log.warning("Ignoring add-on '%1$s', not a folder.", addon.getName()); } } @@ -247,24 +251,24 @@ public final class SdkManager { File addOnManifest = new File(addon, SdkConstants.FN_MANIFEST_INI); if (addOnManifest.isFile()) { - Map<String, String> map = parsePropertyFile(addOnManifest, log); + Map<String, String> propertyMap = parsePropertyFile(addOnManifest, log); - if (map != null) { + if (propertyMap != null) { // look for some specific values in the map. // we require name, vendor, and api - String name = map.get(ADDON_NAME); + String name = propertyMap.get(ADDON_NAME); if (name == null) { displayAddonManifestError(log, addon.getName(), ADDON_NAME); return null; } - String vendor = map.get(ADDON_VENDOR); + String vendor = propertyMap.get(ADDON_VENDOR); if (vendor == null) { displayAddonManifestError(log, addon.getName(), ADDON_VENDOR); return null; } - String api = map.get(ADDON_API); + String api = propertyMap.get(ADDON_API); PlatformTarget baseTarget = null; if (api == null) { displayAddonManifestError(log, addon.getName(), ADDON_API); @@ -302,22 +306,42 @@ public final class SdkManager { } // get the optional description - String description = map.get(ADDON_DESCRIPTION); + String description = propertyMap.get(ADDON_DESCRIPTION); // get the optional libraries - String librariesValue = map.get(ADDON_LIBRARIES); + String librariesValue = propertyMap.get(ADDON_LIBRARIES); + Map<String, String[]> libMap = null; - // split in the string into the values we care about - String[] libraries = librariesValue.split(";"); - Map<String, String> libMap = null; - if (libraries.length > 0) { - libMap = new HashMap<String, String>(); - for (String lib : libraries) { - String[] values = lib.split(":"); - if (values.length == 2) { - libMap.put(values[0], values[1]); - } else { - // TODO: log error + if (librariesValue != null) { + librariesValue = librariesValue.trim(); + if (librariesValue.length() > 0) { + // split in the string into the libraries name + String[] libraries = librariesValue.split(";"); + if (libraries.length > 0) { + libMap = new HashMap<String, String[]>(); + for (String libName : libraries) { + libName = libName.trim(); + + // get the library data from the properties + String libData = propertyMap.get(libName); + + if (libData != null) { + // split the jar file from the description + Matcher m = PATTERN_LIB_DATA.matcher(libData); + if (m.matches()) { + libMap.put(libName, new String[] { + m.group(1), m.group(2) }); + } else if (log != null) { + log.error(null, + "Ignoring library '%1$s', property value has wrong format\n\t%2$s", + libName, libData); + } + } else if (log != null) { + log.error(null, + "Ignoring library '%1$s', missing property value", + libName, libData); + } + } } } } diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/project/ProjectCreator.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/project/ProjectCreator.java index 1184fc2..4cf224d 100644 --- a/sdkmanager/libs/sdklib/src/com/android/sdklib/project/ProjectCreator.java +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/project/ProjectCreator.java @@ -89,13 +89,43 @@ public class ProjectCreator { String packageName, String activityName, IAndroidTarget target, boolean isTestProject) { - // check project folder exists. + // create project folder if it does not exist File projectFolder = new File(folderPath); - if (projectFolder.isDirectory() == false) { - mLog.error(null, "Folder '%s' does not exist. Aborting...", folderPath); - return; + if (!projectFolder.exists()) { + + boolean created = false; + Throwable t = null; + try { + created = projectFolder.mkdirs(); + } catch (Exception e) { + t = e; + } + + if (created) { + println("Created project directory: %1$s", projectFolder); + } else { + mLog.error(t, "Could not create directory: %1$s", projectFolder); + return; + } + } else { + Exception e = null; + String error = null; + try { + String[] content = projectFolder.list(); + if (content == null) { + error = "Project directory %1$s is not a directory."; + } else if (content.length != 0) { + error = "Project directory %1$s is not empty. Please consider using '%2$s update' instead."; + } + } catch (Exception e1) { + e = e1; + } + + if (e != null || error != null) { + mLog.error(e, error, projectFolder, SdkConstants.AndroidCmdName()); + } } - + try { // first create the project properties. @@ -110,6 +140,11 @@ public class ProjectCreator { PropertyType.DEFAULT); defaultProperties.setAndroidTarget(target); defaultProperties.save(); + + // create an empty build.properties + ProjectProperties buildProperties = ProjectProperties.create(folderPath, + PropertyType.BUILD); + buildProperties.save(); // create the map for place-holders of values to replace in the templates final HashMap<String, String> keywords = new HashMap<String, String>(); @@ -190,7 +225,7 @@ public class ProjectCreator { * corresponding value in the created file. * * @param templateName the name of to the template file - * @param dest 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 target the Target of the project that will be providing the template. * @throws ProjectCreateException @@ -211,7 +246,7 @@ public class ProjectCreator { * corresponding value in the created file. * * @param templateName the name of to the template file - * @param dest 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. * @throws ProjectCreateException */ @@ -261,7 +296,6 @@ public class ProjectCreator { println("Added file %1$s", destFile); } - /** * Prints a message unless silence is enabled. * @param format Format for String.format diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/project/ProjectProperties.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/project/ProjectProperties.java index 473f284..938f89d 100644 --- a/sdkmanager/libs/sdklib/src/com/android/sdklib/project/ProjectProperties.java +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/project/ProjectProperties.java @@ -66,7 +66,7 @@ public final class ProjectProperties { "# This file must be checked in Version Control Systems.\n" + "# \n" + "# To customize properties used by the Ant build system use,\n" + - "# \"build.properties\", and override values to adapt the script to your" + + "# \"build.properties\", and override values to adapt the script to your\n" + "# project structure.\n" + "\n"; @@ -74,17 +74,18 @@ public final class ProjectProperties { // 1-------10--------20--------30--------40--------50--------60--------70--------80 "# This file is used to override default values used by the Ant build system.\n" + "# \n" + - "# This file must be checked in Version Control Systems, as it is" + + "# This file must be checked in Version Control Systems, as it is\n" + "# integral to the build system of your project.\n" + - "# \n" + - "# Use this file to change values like:\n" + - "# application-package\n:" + - "# the name of your application package as defined in the manifest.\n" + - "# Used by the 'uninstall' rule.\n"+ - "# source-folder\n:" + - "# the name of the source folder. Default is 'src'.\n" + - "# out-folder\n:" + - "# the name of the output folder. Default is 'bin'\n" + + "\n" + + "# The name of your application package as defined in the manifest.\n" + + "# Used by the 'uninstall' rule.\n"+ + "#application-package=com.example.myproject\n" + + "\n" + + "# The name of the source folder.\n" + + "#source-folder=src\n" + + "\n" + + "# The name of the output folder.\n" + + "#out-folder=bin\n" + "\n"; private final static Map<String, String> COMMENT_MAP = new HashMap<String, String>(); @@ -104,7 +105,9 @@ public final class ProjectProperties { /** * Loads a project properties file and return a {@link ProjectProperties} object * containing the properties + * * @param projectFolderOsPath the project folder. + * @param type One the possible {@link PropertyType}s. */ public static ProjectProperties load(String projectFolderOsPath, PropertyType type) { File projectFolder = new File(projectFolderOsPath); @@ -119,7 +122,44 @@ public final class ProjectProperties { } return null; } - + + /** + * Merges all properties from the given file into the current properties. + * <p/> + * This emulates the Ant behavior: existing properties are <em>not</em> overriden. + * Only new undefined properties become defined. + * <p/> + * Typical usage: + * <ul> + * <li>Create a ProjectProperties with {@link PropertyType#BUILD} + * <li>Merge in values using {@link PropertyType#DEFAULT} + * <li>The result is that this contains all the properties from default plus those + * overridden by the build.properties file. + * </ul> + * + * @param type One the possible {@link PropertyType}s. + * @return this object, for chaining. + */ + public ProjectProperties merge(PropertyType type) { + File projectFolder = new File(mProjectFolderOsPath); + if (projectFolder.isDirectory()) { + File defaultFile = new File(projectFolder, type.mFilename); + if (defaultFile.isFile()) { + Map<String, String> map = SdkManager.parsePropertyFile(defaultFile, null /* log */); + if (map != null) { + for(Entry<String, String> entry : map.entrySet()) { + String key = entry.getKey(); + String value = entry.getValue(); + if (!mProperties.containsKey(key) && value != null) { + mProperties.put(key, value); + } + } + } + } + } + return this; + } + /** * Creates a new project properties object, with no properties. * <p/>The file is not created until {@link #save()} is called. @@ -185,10 +225,9 @@ public final class ProjectProperties { /** * Private constructor. - * Use {@link #load(String)} or {@link #create(String)} to instantiate. - * @param projectFolderOsPath - * @param map - * @param type + * <p/> + * Use {@link #load(String, PropertyType)} or {@link #create(String, PropertyType)} + * to instantiate. */ private ProjectProperties(String projectFolderOsPath, Map<String, String> map, PropertyType type) { diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/vm/HardwareProperties.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/vm/HardwareProperties.java index cb2c8c2..98e97fe 100644 --- a/sdkmanager/libs/sdklib/src/com/android/sdklib/vm/HardwareProperties.java +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/vm/HardwareProperties.java @@ -91,8 +91,8 @@ public class HardwareProperties { } /** - * Parses the harware definition file. - * @param buildProp the property file to parse + * Parses the hardware definition file. + * @param file the property file to parse * @param log the ISdkLog object receiving warning/error from the parsing. * @return the map of (key,value) pairs, or null if the parsing failed. */ diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/vm/VmManager.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/vm/VmManager.java index a28561d..1edd8b2 100644 --- a/sdkmanager/libs/sdklib/src/com/android/sdklib/vm/VmManager.java +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/vm/VmManager.java @@ -20,7 +20,6 @@ import com.android.prefs.AndroidLocation; import com.android.prefs.AndroidLocation.AndroidLocationException; import com.android.sdklib.IAndroidTarget; import com.android.sdklib.ISdkLog; -import com.android.sdklib.SdkConstants; import com.android.sdklib.SdkManager; import java.io.File; @@ -54,20 +53,20 @@ public final class VmManager { String name; String path; IAndroidTarget target; - + public String getName() { return name; } - + public String getPath() { return path; } - + public IAndroidTarget getTarget() { return target; } } - + private final ArrayList<VmInfo> mVmList = new ArrayList<VmInfo>(); private ISdkLog mSdkLog; @@ -75,7 +74,7 @@ public final class VmManager { mSdkLog = sdkLog; buildVmList(sdk); } - + /** * Returns the existing VMs. * @return a newly allocated arrays containing all the VMs. @@ -83,7 +82,7 @@ public final class VmManager { public VmInfo[] getVms() { return mVmList.toArray(new VmInfo[mVmList.size()]); } - + /** * Returns the {@link VmInfo} matching the given <var>name</var>. * @return the matching VmInfo or <code>null</code> if none were found. @@ -99,7 +98,7 @@ public final class VmManager { } /** - * Creates a new VM. + * Creates a new VM. It is expected that there is no existing VM with this name already. * @param parentFolder the folder to contain the VM. A new folder will be created in this * folder with the name of the VM * @param name the name of the VM @@ -109,27 +108,25 @@ public final class VmManager { * @param sdcardSize the size of a local sdcard to create. Can be 0 for no local sdcard. * @param hardwareConfig the hardware setup for the VM */ - public static void createVm(String parentFolder, String name, IAndroidTarget target, + public VmInfo createVm(String parentFolder, String name, IAndroidTarget target, String skinName, String sdcardPath, int sdcardSize, Map<String,String> hardwareConfig, ISdkLog log) { - - // now write the ini file in the vmRoot folder. - // get the Android prefs location. + try { File rootDirectory = new File(parentFolder); if (rootDirectory.isDirectory() == false) { if (log != null) { - log.error(null, "%s does not exists.", parentFolder); + log.error(null, "Folder %s does not exist.", parentFolder); } - return; + return null; } File vmFolder = new File(parentFolder, name + ".avm"); if (vmFolder.exists()) { if (log != null) { - log.error(null, "%s already exists.", vmFolder.getAbsolutePath()); + log.error(null, "Folder %s is in the way.", vmFolder.getAbsolutePath()); } - return; + return null; } // create the vm folder. @@ -164,13 +161,29 @@ public final class VmManager { // Config file values.clear(); if (skinName != null) { - values.put("skin", skinName); - } else { - values.put("skin", SdkConstants.SKIN_DEFAULT); + // check that the skin name is valid + String[] skinNames = target.getSkins(); + boolean found = false; + for (String n : skinNames) { + if (n.equals(skinName)) { + values.put("skin", skinName); + found = true; + break; + } + } + + if (found == false && log != null) { + log.warning("Skin '%1$s' does not exists, using default skin.", skinName); + } } - + if (sdcardPath != null) { - values.put("sdcard", sdcardPath); + File sdcard = new File(sdcardPath); + if (sdcard.isFile()) { + values.put("sdcard", sdcardPath); + } else if (log != null) { + log.warning("sdcarad image '%1$s' does not exists.", sdcardPath); + } } else if (sdcardSize != 0) { // TODO: create sdcard image. } @@ -182,21 +195,36 @@ public final class VmManager { File configIniFile = new File(vmFolder, CONFIG_INI); createConfigIni(configIniFile, values); - if (target.isPlatform()) { - System.out.println(String.format( - "Created VM '%s' based on %s", name, target.getName())); - } else { - System.out.println(String.format( - "Created VM '%s' based on %s (%s)", name, target.getName(), - target.getVendor())); + if (log != null) { + if (target.isPlatform()) { + log.printf("Created VM '%s' based on %s\n", name, target.getName()); + } else { + log.printf( + "Created VM '%s' based on %s (%s)\n", name, target.getName(), + target.getVendor()); + } } + + // create the VmInfo object, and add it to the list + VmInfo vmInfo = new VmInfo(); + vmInfo.name = name; + vmInfo.path = vmFolder.getAbsolutePath(); + vmInfo.target = target; + + mVmList.add(vmInfo); + + return vmInfo; } catch (AndroidLocationException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + if (log != null) { + log.error(e, null); + } } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + if (log != null) { + log.error(e, null); + } } + + return null; } private void buildVmList(SdkManager sdk) throws AndroidLocationException { |