OSDN Git Service

original
[gb-231r1-is01/GB_2.3_IS01.git] / sdk / eclipse / plugins / com.android.ide.eclipse.adt / src / com / android / ide / eclipse / adt / internal / actions / MultiApkExportAction.java
diff --git a/sdk/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/actions/MultiApkExportAction.java b/sdk/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/actions/MultiApkExportAction.java
new file mode 100644 (file)
index 0000000..10ec15f
--- /dev/null
@@ -0,0 +1,319 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.eclipse.org/org/documents/epl-v10.php
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ide.eclipse.adt.internal.actions;
+
+import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.AndroidPrintStream;
+import com.android.ide.eclipse.adt.internal.build.BuildHelper;
+import com.android.ide.eclipse.adt.internal.project.ProjectHelper;
+import com.android.ide.eclipse.adt.internal.sdk.ProjectState;
+import com.android.ide.eclipse.adt.internal.sdk.Sdk;
+import com.android.ide.eclipse.adt.io.IFolderWrapper;
+import com.android.sdklib.SdkConstants;
+import com.android.sdklib.internal.export.ApkData;
+import com.android.sdklib.internal.export.MultiApkExportHelper;
+import com.android.sdklib.internal.export.ProjectConfig;
+import com.android.sdklib.internal.export.MultiApkExportHelper.ExportException;
+import com.android.sdklib.internal.export.MultiApkExportHelper.Target;
+import com.android.sdklib.internal.project.ProjectProperties;
+import com.android.sdklib.internal.project.ProjectProperties.PropertyType;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IFolder;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IWorkspace;
+import org.eclipse.core.resources.IWorkspaceRoot;
+import org.eclipse.core.resources.IncrementalProjectBuilder;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.ui.IObjectActionDelegate;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchPart;
+import org.eclipse.ui.PlatformUI;
+
+import java.io.File;
+import java.lang.reflect.InvocationTargetException;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+/**
+ * Multiple APK export Action.
+ * The action is triggered on a project selection, and performs a full APK export based on the
+ * content of the export.properties file.
+ */
+public class MultiApkExportAction implements IObjectActionDelegate {
+
+    private ISelection mSelection;
+
+    public void setActivePart(IAction action, IWorkbenchPart targetPart) {
+        // pass
+    }
+
+    public void run(IAction action) {
+        if (mSelection instanceof IStructuredSelection) {
+            for (Iterator<?> it = ((IStructuredSelection)mSelection).iterator(); it.hasNext();) {
+                Object element = it.next();
+                IProject project = null;
+                if (element instanceof IProject) {
+                    project = (IProject)element;
+                } else if (element instanceof IAdaptable) {
+                    project = (IProject)((IAdaptable)element).getAdapter(IProject.class);
+                }
+                if (project != null) {
+                    IWorkbench workbench = PlatformUI.getWorkbench();
+                    final IProject fProject = project;
+                    try {
+                        workbench.getProgressService().busyCursorWhile(new IRunnableWithProgress() {
+                            /**
+                             * Run the export.
+                             * @throws InvocationTargetException
+                             * @throws InterruptedException
+                             */
+                            public void run(IProgressMonitor monitor)
+                                    throws InvocationTargetException, InterruptedException {
+                                try {
+                                    runMultiApkExport(fProject, monitor);
+                                } catch (Exception e) {
+                                    AdtPlugin.logAndPrintError(e, fProject.getName(),
+                                            "Failed to export project: %1$s", e.getMessage());
+                                } finally {
+                                    monitor.done();
+                                }
+                            }
+                        });
+                    } catch (Exception e) {
+                        AdtPlugin.logAndPrintError(e, project.getName(),
+                                "Failed to export project: %1$s",
+                                e.getMessage());
+                    }
+                }
+            }
+        }
+    }
+
+    public void selectionChanged(IAction action, ISelection selection) {
+        mSelection = selection;
+    }
+
+    /**
+     * Runs the multi-apk export.
+     * @param exportProject the main "export" project.
+     * @param monitor the progress monitor.
+     * @throws ExportException
+     * @throws CoreException
+     */
+    private void runMultiApkExport(IProject exportProject, IProgressMonitor monitor)
+            throws ExportException, CoreException {
+
+        ProjectProperties props = ProjectProperties.load(new IFolderWrapper(exportProject),
+                PropertyType.EXPORT);
+
+        // get some props and make sure their values are valid.
+
+        String appPackage = props.getProperty(ProjectProperties.PROPERTY_PACKAGE);
+        if (appPackage == null || appPackage.length() == 0) {
+            throw new IllegalArgumentException("Invalid 'package' property values.");
+        }
+
+        String version = props.getProperty(ProjectProperties.PROPERTY_VERSIONCODE);
+        int versionCode;
+        try {
+            versionCode = Integer.parseInt(version);
+        } catch (NumberFormatException e) {
+            throw new IllegalArgumentException("version value is not a valid integer.", e);
+        }
+
+        String projects = props.getProperty(ProjectProperties.PROPERTY_PROJECTS);
+        if (projects == null || projects.length() == 0) {
+            throw new IllegalArgumentException("Missing project list.");
+        }
+
+        // create the multi apk helper to get the list of apk to export.
+        MultiApkExportHelper helper = new MultiApkExportHelper(
+                exportProject.getLocation().toOSString(),
+                appPackage, versionCode, Target.RELEASE, System.out);
+
+        List<ApkData> apks = helper.getApkData(projects);
+
+        // list of projects that have been resolved (ie the IProject has been found from the
+        // ProjectConfig) and compiled.
+        HashMap<ProjectConfig, ProjectState> resolvedProjects =
+                new HashMap<ProjectConfig, ProjectState>();
+
+        IWorkspace ws = ResourcesPlugin.getWorkspace();
+        IWorkspaceRoot wsRoot = ws.getRoot();
+
+        // bin folder for the export project
+        IFolder binFolder = exportProject.getFolder(SdkConstants.FD_OUTPUT);
+        if (binFolder.exists() == false) {
+            binFolder.create(true, true, monitor);
+        }
+
+        AndroidPrintStream stdout = new AndroidPrintStream(exportProject, null /*prefix*/,
+                System.out);
+        AndroidPrintStream stderr = new AndroidPrintStream(exportProject, null /*prefix*/,
+                System.err);
+
+        for (ApkData apk : apks) {
+            // find the IProject object for this apk.
+            ProjectConfig projectConfig = apk.getProjectConfig();
+            ProjectState projectState = resolvedProjects.get(projectConfig);
+            if (projectState == null) {
+                // first time? resolve the project and compile it.
+                IPath path = exportProject.getFullPath().append(projectConfig.getRelativePath());
+
+                IResource res = wsRoot.findMember(path);
+                if (res.getType() != IResource.PROJECT) {
+                    throw new IllegalArgumentException(String.format(
+                            "%1$s does not resolve to a project.",
+                            projectConfig.getRelativePath()));
+                }
+
+                IProject project = (IProject)res;
+
+                projectState = Sdk.getProjectState(project);
+                if (projectState == null) {
+                    throw new IllegalArgumentException(String.format(
+                            "State for project %1$s could not be loaded.",
+                            project.getName()));
+                }
+
+                if (projectState.isLibrary()) {
+                    throw new IllegalArgumentException(String.format(
+                            "Project %1$s is a library and cannot be part of a multi-apk export.",
+                            project.getName()));
+                }
+
+                // build the project, mainly for the java compilation. The rest is handled below.
+                project.build(IncrementalProjectBuilder.INCREMENTAL_BUILD, monitor);
+
+                // store the resolved project in the map.
+                resolvedProjects.put(projectConfig, projectState);
+            }
+
+            Map<String, String> variantMap = apk.getSoftVariantMap();
+
+            if (variantMap.size() > 0) {
+                // if there are soft variants, only export those.
+                for (Entry<String, String> entry : variantMap.entrySet()) {
+                    buildVariant(wsRoot, projectState, appPackage, versionCode, apk, entry,
+                            binFolder, stdout, stderr);
+                }
+            } else {
+                buildVariant(wsRoot, projectState, appPackage, versionCode, apk,
+                        null /*soft variant*/, binFolder, stdout, stderr);
+            }
+        }
+
+        helper.writeLogs();
+    }
+
+    /**
+     * Builds a particular variant of an APK
+     * @param wsRoot the workspace root
+     * @param projectState the project to export
+     * @param appPackage the application package
+     * @param versionCode the major version code.
+     * @param apk the {@link ApkData} describing how the export should happen.
+     * @param softVariant an optional soft variant info. The entry contains (name, resource filter).
+     * @param binFolder the binFolder where the file must be created.
+     * @throws CoreException
+     */
+    private void buildVariant(IWorkspaceRoot wsRoot, ProjectState projectState, String appPackage,
+            int versionCode, ApkData apk, Entry<String, String> softVariant, IFolder binFolder,
+            AndroidPrintStream stdout, AndroidPrintStream stderr)
+            throws CoreException {
+        try {
+            // get the libraries for this project
+            List<IProject> libProjects = projectState.getFullLibraryProjects();
+
+            IProject project = projectState.getProject();
+            IJavaProject javaProject = JavaCore.create(project);
+
+            int compositeVersionCode = apk.getCompositeVersionCode(versionCode);
+
+            // figure out the file names
+            String pkgName = project.getName() + "-" + apk.getBuildInfo();
+            String finalNameRoot = appPackage + "-" + compositeVersionCode;
+            if (softVariant != null) {
+                String tmp = "-" + softVariant.getKey();
+                pkgName += tmp;
+                finalNameRoot += tmp;
+            }
+
+            pkgName += ".ap_";
+            String outputName = finalNameRoot + "-unsigned.apk";
+
+            BuildHelper helper = new BuildHelper(project, stdout, stderr,
+                    false /*debugMode*/, false/*verbose*/);
+
+            // get the manifest file
+            IFile manifestFile = project.getFile(SdkConstants.FN_ANDROID_MANIFEST_XML);
+            // get the project bin folder
+            IFolder projectBinFolder = wsRoot.getFolder(javaProject.getOutputLocation());
+            String projectBinFolderPath = projectBinFolder.getLocation().toOSString();
+
+            // package the resources
+            helper.packageResources(manifestFile, libProjects,
+                    softVariant != null ? softVariant.getValue() : null,
+                    compositeVersionCode, projectBinFolderPath, pkgName);
+
+            apk.setOutputName(softVariant != null ? softVariant.getKey() : null, outputName);
+
+            // do the final export.
+            IFile dexFile = projectBinFolder.getFile(SdkConstants.FN_APK_CLASSES_DEX);
+            String outputFile = binFolder.getFile(outputName).getLocation().toOSString();
+
+            // get the list of referenced projects.
+            List<IProject> javaRefs = ProjectHelper.getReferencedProjects(project);
+            List<IJavaProject> referencedJavaProjects = BuildHelper.getJavaProjects(javaRefs);
+
+            helper.finalPackage(
+                    new File(projectBinFolderPath, pkgName).getAbsolutePath(),
+                    dexFile.getLocation().toOSString(),
+                    outputFile,
+                    javaProject,
+                    libProjects,
+                    referencedJavaProjects,
+                    apk.getAbi(),
+                    null, //key
+                    null, //certificate
+                    null); //ResourceMarker
+        } catch (CoreException e) {
+            throw e;
+        } catch (Exception e) {
+            throw new CoreException(new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID,
+                    e.getMessage(), e));
+        }
+
+    }
+}