/** * Returns the lastest build tools that's at least the passed version. * * @param fullRevision the minimum required build tools version. * @return the latest build tools. * @throws RuntimeException if the latest build tools is older than fullRevision. */ static File getAapt(FullRevision fullRevision) { ILogger logger = new StdLogger(StdLogger.Level.VERBOSE); SdkManager sdkManager = SdkManager.createManager(getSdkDir().getAbsolutePath(), logger); assert sdkManager != null; BuildToolInfo buildToolInfo = sdkManager.getLatestBuildTool(); if (buildToolInfo == null || buildToolInfo.getRevision().compareTo(fullRevision) < 0) { throw new RuntimeException("Test requires build-tools " + fullRevision.toShortString()); } return new File(buildToolInfo.getPath(BuildToolInfo.PathId.AAPT)); }
/** Does the basic SDK parsing required for all actions */ private void parseSdk() { mSdkManager = SdkManager.createManager(mOsSdkFolder, mSdkLog); if (mSdkManager == null) { errorAndExit("Unable to parse SDK content."); } }
/** * Converts a symbolic target name (such as those accepted by --target on the command-line) to an * internal target index id. A valid target name is either a numeric target id (> 0) or a target * hash string. * * <p>If the given target can't be mapped, {@link #INVALID_TARGET_ID} (0) is returned. It's up to * the caller to output an error. * * <p>On success, returns a value > 0. */ private int resolveTargetName(String targetName) { if (targetName == null) { return INVALID_TARGET_ID; } targetName = targetName.trim(); // Case of an integer number if (targetName.matches("[0-9]*")) { try { int n = Integer.parseInt(targetName); return n < 1 ? INVALID_TARGET_ID : n; } catch (NumberFormatException e) { // Ignore. Should not happen. } } // Let's try to find a platform or addon name. IAndroidTarget[] targets = mSdkManager.getTargets(); for (int i = 0; i < targets.length; i++) { if (targetName.equals(targets[i].hashString())) { return i + 1; } } return INVALID_TARGET_ID; }
/** * Updates an existing Android project based on command-line parameters * * @param library whether the project is a library project. */ private void updateProject(boolean library) { // get the target and try to resolve it. IAndroidTarget target = null; String targetStr = mSdkCommandLine.getParamTargetId(); // For "update project" the target parameter is optional so having null is acceptable. // However if there's a value, it must be valid. if (targetStr != null) { IAndroidTarget[] targets = mSdkManager.getTargets(); int targetId = resolveTargetName(targetStr); if (targetId == INVALID_TARGET_ID || targetId > targets.length) { errorAndExit( "Target id '%1$s' is not valid. Use '%2$s list targets' to get the target ids.", targetStr, SdkConstants.androidCmdName()); } target = targets[targetId - 1]; // target id is 1-based } ProjectCreator creator = getProjectCreator(); String projectDir = getProjectLocation(mSdkCommandLine.getParamLocationPath()); String libraryPath = library ? null : mSdkCommandLine.getParamProjectLibrary(SdkCommandLine.OBJECT_PROJECT); creator.updateProject(projectDir, target, mSdkCommandLine.getParamName(), libraryPath); if (library == false) { 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(), null /*libraryPath*/); } 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.\n", SdkCommandLine.KEY_SUBPROJECTS); } } }
@NotNull public String getLocation() { String location = mySdkManager.getLocation(); if (location.length() > 0) { char lastChar = location.charAt(location.length() - 1); if (lastChar == '/' || lastChar == File.separatorChar) { return location.substring(0, location.length() - 1); } } return location; }
/** * Initializes the {@link SdkManager} and the {@link AvdManager}. Extracted so that we can * override this in unit tests. */ @VisibleForTesting(visibility = Visibility.PRIVATE) protected void initSdk() { setSdkManager(SdkManager.createManager(mOsSdkRoot, mSdkLog)); try { mAvdManager = null; mAvdManager = AvdManager.getInstance(mSdkManager.getLocalSdk(), mSdkLog); } catch (AndroidLocationException e) { mSdkLog.error(e, "Unable to read AVDs: " + e.getMessage()); // $NON-NLS-1$ // Note: we used to continue here, but the thing is that // mAvdManager==null so nothing is really going to work as // expected. Let's just display an error later in checkIfInitFailed() // and abort right there. This step is just too early in the SWT // setup process to display a message box yet. mAvdManagerInitError = e; } // notify listeners. broadcastOnSdkReload(); }
@NotNull public IAndroidTarget[] getTargets() { if (myTargets == null) { IAndroidTarget[] targets = mySdkManager.getTargets(); if (targets != null) { myTargets = new IAndroidTarget[targets.length]; for (int i = 0; i < targets.length; i++) { myTargets[i] = new MyTargetWrapper(targets[i]); } } } return myTargets; }
/** Creates a new Android project based on command-line parameters */ private void createProject(boolean library) { String directObject = library ? SdkCommandLine.OBJECT_LIB_PROJECT : SdkCommandLine.OBJECT_PROJECT; // get the target and try to resolve it. int targetId = resolveTargetName(mSdkCommandLine.getParamTargetId()); IAndroidTarget[] targets = mSdkManager.getTargets(); if (targetId == INVALID_TARGET_ID || targetId > targets.length) { errorAndExit( "Target id is not valid. Use '%s list targets' to get the target ids.", SdkConstants.androidCmdName()); } IAndroidTarget target = targets[targetId - 1]; // target id is 1-based ProjectCreator creator = getProjectCreator(); String projectDir = getProjectLocation(mSdkCommandLine.getParamLocationPath()); String projectName = mSdkCommandLine.getParamName(); String packageName = mSdkCommandLine.getParamProjectPackage(directObject); String activityName = null; if (library == false) { activityName = mSdkCommandLine.getParamProjectActivity(); } if (projectName != null && !ProjectCreator.RE_PROJECT_NAME.matcher(projectName).matches()) { errorAndExit( "Project name '%1$s' contains invalid characters.\nAllowed characters are: %2$s", projectName, ProjectCreator.CHARS_PROJECT_NAME); return; } if (activityName != null && !ProjectCreator.RE_ACTIVITY_NAME.matcher(activityName).matches()) { errorAndExit( "Activity name '%1$s' contains invalid characters.\nAllowed characters are: %2$s", activityName, ProjectCreator.CHARS_ACTIVITY_NAME); return; } if (packageName != null && !ProjectCreator.RE_PACKAGE_NAME.matcher(packageName).matches()) { errorAndExit( "Package name '%1$s' contains invalid characters.\n" + "A package name must be constitued of two Java identifiers.\n" + "Each identifier allowed characters are: %2$s", packageName, ProjectCreator.CHARS_PACKAGE_NAME); return; } creator.createProject( projectDir, projectName, packageName, activityName, target, library, null /*pathToMain*/); }
/** Updates adb with the USB devices declared in the SDK add-ons. */ private void updateAdb() { try { mSdkManager.updateAdb(); mSdkLog.printf( "adb has been updated. You must restart adb with the following commands\n" + "\tadb kill-server\n" + "\tadb start-server\n"); } catch (AndroidLocationException e) { errorAndExit(e.getMessage()); } catch (IOException e) { errorAndExit(e.getMessage()); } }
/** * Reloads the SDK content (targets). * * <p>This also reloads the AVDs in case their status changed. * * <p>This does not notify the listeners ({@link ISdkChangeListener}). */ public void reloadSdk() { // reload SDK mSdkManager.reloadSdk(mSdkLog); // reload AVDs if (mAvdManager != null) { try { mAvdManager.reloadAvds(mSdkLog); } catch (AndroidLocationException e) { // FIXME } } mLocalSdkParser.clearPackages(); // notify listeners broadcastOnSdkReload(); }
/** Displays the list of available Targets (Platforms and Add-ons) */ private void displayTargetList() { mSdkLog.printf("Available Android targets:\n"); int index = 1; for (IAndroidTarget target : mSdkManager.getTargets()) { mSdkLog.printf("id: %1$d or \"%2$s\"\n", index, target.hashString()); mSdkLog.printf(" Name: %s\n", target.getName()); if (target.isPlatform()) { mSdkLog.printf(" Type: Platform\n"); mSdkLog.printf(" API level: %s\n", target.getVersion().getApiString()); mSdkLog.printf(" Revision: %d\n", target.getRevision()); } else { mSdkLog.printf(" Type: Add-On\n"); mSdkLog.printf(" Vendor: %s\n", target.getVendor()); mSdkLog.printf(" Revision: %d\n", target.getRevision()); if (target.getDescription() != null) { mSdkLog.printf(" Description: %s\n", target.getDescription()); } mSdkLog.printf( " Based on Android %s (API level %s)\n", target.getVersionName(), target.getVersion().getApiString()); // display the optional libraries. IOptionalLibrary[] libraries = target.getOptionalLibraries(); if (libraries != null) { mSdkLog.printf(" Libraries:\n"); for (IOptionalLibrary library : libraries) { mSdkLog.printf(" * %1$s (%2$s)\n", library.getName(), library.getJarName()); mSdkLog.printf(String.format(" %1$s\n", library.getDescription())); } } } // get the target skins displaySkinList(target, " Skins: "); if (target.getUsbVendorId() != IAndroidTarget.NO_USB_ID) { mSdkLog.printf( " Adds USB support for devices (Vendor: 0x%04X)\n", target.getUsbVendorId()); } index++; } }
/** Creates a new AVD. This is a text based creation with command line prompt. */ private void createAvd() { // find a matching target int targetId = resolveTargetName(mSdkCommandLine.getParamTargetId()); IAndroidTarget[] targets = mSdkManager.getTargets(); if (targetId == INVALID_TARGET_ID || targetId > targets.length) { errorAndExit( "Target id is not valid. Use '%s list targets' to get the target ids.", SdkConstants.androidCmdName()); } IAndroidTarget target = targets[targetId - 1]; // target id is 1-based try { boolean removePrevious = mSdkCommandLine.getFlagForce(); AvdManager avdManager = new AvdManager(mSdkManager, mSdkLog); String avdName = mSdkCommandLine.getParamName(); if (!AvdManager.RE_AVD_NAME.matcher(avdName).matches()) { errorAndExit( "AVD name '%1$s' contains invalid characters.\nAllowed characters are: %2$s", avdName, AvdManager.CHARS_AVD_NAME); return; } AvdInfo info = avdManager.getAvd(avdName, false /*validAvdOnly*/); if (info != null) { if (removePrevious) { mSdkLog.warning( "Android Virtual Device '%s' already exists and will be replaced.", avdName); } else { errorAndExit( "Android Virtual Device '%s' already exists.\n" + "Use --force if you want to replace it.", avdName); return; } } String paramFolderPath = mSdkCommandLine.getParamLocationPath(); File avdFolder = null; if (paramFolderPath != null) { avdFolder = new File(paramFolderPath); } else { avdFolder = AvdManager.AvdInfo.getAvdFolder(avdName); } // Validate skin is either default (empty) or NNNxMMM or a valid skin name. Map<String, String> skinHardwareConfig = null; String skin = mSdkCommandLine.getParamSkin(); if (skin != null && skin.length() == 0) { skin = null; } if (skin != null && target != null) { boolean valid = false; // Is it a know skin name for this target? for (String s : target.getSkins()) { if (skin.equalsIgnoreCase(s)) { skin = s; // Make skin names case-insensitive. valid = true; // get the hardware properties for this skin File skinFolder = avdManager.getSkinPath(skin, target); FileWrapper skinHardwareFile = new FileWrapper(skinFolder, AvdManager.HARDWARE_INI); if (skinHardwareFile.isFile()) { skinHardwareConfig = ProjectProperties.parsePropertyFile(skinHardwareFile, mSdkLog); } break; } } // Is it NNNxMMM? if (!valid) { valid = AvdManager.NUMERIC_SKIN_SIZE.matcher(skin).matches(); } if (!valid) { displaySkinList(target, "Valid skins: "); errorAndExit("'%s' is not a valid skin name or size (NNNxMMM)", skin); return; } } Map<String, String> hardwareConfig = null; if (target != null && target.isPlatform()) { try { hardwareConfig = promptForHardware(target, skinHardwareConfig); } catch (IOException e) { errorAndExit(e.getMessage()); } } @SuppressWarnings("unused") // oldAvdInfo is never read, yet useful for debugging AvdInfo oldAvdInfo = null; if (removePrevious) { oldAvdInfo = avdManager.getAvd(avdName, false /*validAvdOnly*/); } @SuppressWarnings("unused") // newAvdInfo is never read, yet useful for debugging AvdInfo newAvdInfo = avdManager.createAvd( avdFolder, avdName, target, skin, mSdkCommandLine.getParamSdCard(), hardwareConfig, removePrevious, mSdkCommandLine.getFlagSnapshot(), mSdkLog); } catch (AndroidLocationException e) { errorAndExit(e.getMessage()); } }
/** Creates a new Android test project based on command-line parameters */ private void createTestProject() { String projectDir = getProjectLocation(mSdkCommandLine.getParamLocationPath()); // first check the path of the parent project, and make sure it's valid. String pathToMainProject = mSdkCommandLine.getParamTestProjectMain(); File parentProject = new File(pathToMainProject); if (parentProject.isAbsolute() == false) { // if the path is not absolute, we need to resolve it based on the // destination path of the project try { parentProject = new File(projectDir, pathToMainProject).getCanonicalFile(); } catch (IOException e) { errorAndExit("Unable to resolve Main project's directory: %1$s", pathToMainProject); return; // help Eclipse static analyzer understand we'll never execute the rest. } } if (parentProject.isDirectory() == false) { errorAndExit("Main project's directory does not exist: %1$s", pathToMainProject); return; } // now look for a manifest in there File manifest = new File(parentProject, SdkConstants.FN_ANDROID_MANIFEST_XML); if (manifest.isFile() == false) { errorAndExit( "No AndroidManifest.xml file found in the main project directory: %1$s", parentProject.getAbsolutePath()); return; } // now query the manifest for the package file. XPath xpath = AndroidXPathFactory.newXPath(); String packageName, activityName; try { packageName = xpath.evaluate("/manifest/@package", new InputSource(new FileInputStream(manifest))); mSdkLog.printf("Found main project package: %1$s\n", packageName); // now get the name of the first activity we find activityName = xpath.evaluate( "/manifest/application/activity[1]/@android:name", new InputSource(new FileInputStream(manifest))); // xpath will return empty string when there's no match if (activityName == null || activityName.length() == 0) { activityName = null; } else { mSdkLog.printf("Found main project activity: %1$s\n", activityName); } } catch (FileNotFoundException e) { // this shouldn't happen as we test it above. errorAndExit("No AndroidManifest.xml file found in main project."); return; // this is not strictly needed because errorAndExit will stop the execution, // but this makes the java compiler happy, wrt to uninitialized variables. } catch (XPathExpressionException e) { // looks like the main manifest is not valid. errorAndExit("Unable to parse main project manifest to get information."); return; // this is not strictly needed because errorAndExit will stop the execution, // but this makes the java compiler happy, wrt to uninitialized variables. } // now get the target hash ProjectProperties p = ProjectProperties.load(parentProject.getAbsolutePath(), PropertyType.DEFAULT); if (p == null) { errorAndExit("Unable to load the main project's %1$s", PropertyType.DEFAULT.getFilename()); return; } String targetHash = p.getProperty(ProjectProperties.PROPERTY_TARGET); if (targetHash == null) { errorAndExit("Couldn't find the main project target"); return; } // and resolve it. IAndroidTarget target = mSdkManager.getTargetFromHashString(targetHash); if (target == null) { errorAndExit( "Unable to resolve main project target '%1$s'. You may want to install the platform in your SDK.", targetHash); return; } mSdkLog.printf("Found main project target: %1$s\n", target.getFullName()); ProjectCreator creator = getProjectCreator(); String projectName = mSdkCommandLine.getParamName(); if (projectName != null && !ProjectCreator.RE_PROJECT_NAME.matcher(projectName).matches()) { errorAndExit( "Project name '%1$s' contains invalid characters.\nAllowed characters are: %2$s", projectName, ProjectCreator.CHARS_PROJECT_NAME); return; } creator.createProject( projectDir, projectName, packageName, activityName, target, false /* library*/, pathToMainProject); }
@Nullable public IAndroidTarget findTargetByHashString(@NotNull String hashString) { final IAndroidTarget target = mySdkManager.getTargetFromHashString(hashString); return target != null ? new MyTargetWrapper(target) : null; }
@Override public void execute() throws BuildException { Project antProject = getProject(); // get the SDK location String sdkLocation = antProject.getProperty(ProjectProperties.PROPERTY_SDK); // check if it's valid and exists if (sdkLocation == null || sdkLocation.length() == 0) { throw new BuildException("SDK Location is not set."); } File sdk = new File(sdkLocation); if (sdk.isDirectory() == false) { throw new BuildException(String.format("SDK Location '%s' is not valid.", sdkLocation)); } // get the target property value String targetHashString = antProject.getProperty(ProjectProperties.PROPERTY_TARGET); if (targetHashString == null) { throw new BuildException("Android Target is not set."); } // load up the sdk targets. final ArrayList<String> messages = new ArrayList<String>(); SdkManager manager = SdkManager.createManager( sdkLocation, new ISdkLog() { public void error(Throwable t, String errorFormat, Object... args) { if (errorFormat != null) { messages.add(String.format("Error: " + errorFormat, args)); } if (t != null) { messages.add("Error: " + t.getMessage()); } } public void printf(String msgFormat, Object... args) { messages.add(String.format(msgFormat, args)); } public void warning(String warningFormat, Object... args) { messages.add(String.format("Warning: " + warningFormat, args)); } }); if (manager == null) { // since we failed to parse the SDK, lets display the parsing output. for (String msg : messages) { System.out.println(msg); } throw new BuildException("Failed to parse SDK content."); } // resolve it IAndroidTarget androidTarget = manager.getTargetFromHashString(targetHashString); if (androidTarget == null) { throw new BuildException(String.format("Unable to resolve target '%s'", targetHashString)); } // display it System.out.println("Project Target: " + androidTarget.getName()); if (androidTarget.isPlatform() == false) { System.out.println("Vendor: " + androidTarget.getVendor()); } System.out.println("Platform Version: " + androidTarget.getApiVersionName()); System.out.println("API level: " + androidTarget.getApiVersionNumber()); // 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); // make sure the file exists. File templates = new File(templateFolder); if (templates.isDirectory() == false) { throw new BuildException( String.format("Template directory '%s' is missing.", templateFolder)); } // now check the rules file exists. File rules = new File(templateFolder, ANDROID_RULES); if (rules.isFile() == false) { throw new BuildException(String.format("Build rules file '%s' is missing.", templateFolder)); } // set the file location to import setFile(rules.getAbsolutePath()); // and import it super.execute(); }