OSDN Git Service

original
[gb-231r1-is01/Gingerbread_2.3.3_r1_IS01.git] / sdk / eclipse / plugins / com.android.ide.eclipse.adt / src / com / android / ide / eclipse / adt / internal / actions / MultiApkExportAction.java
1 /*
2  * Copyright (C) 2010 The Android Open Source Project
3  *
4  * Licensed under the Eclipse Public License, Version 1.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.eclipse.org/org/documents/epl-v10.php
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package com.android.ide.eclipse.adt.internal.actions;
18
19 import com.android.ide.eclipse.adt.AdtPlugin;
20 import com.android.ide.eclipse.adt.internal.build.PostCompilerHelper;
21 import com.android.ide.eclipse.adt.internal.project.ProjectHelper;
22 import com.android.ide.eclipse.adt.internal.sdk.ProjectState;
23 import com.android.ide.eclipse.adt.internal.sdk.Sdk;
24 import com.android.ide.eclipse.adt.io.IFolderWrapper;
25 import com.android.sdklib.SdkConstants;
26 import com.android.sdklib.internal.export.ApkData;
27 import com.android.sdklib.internal.export.MultiApkExportHelper;
28 import com.android.sdklib.internal.export.ProjectConfig;
29 import com.android.sdklib.internal.export.MultiApkExportHelper.ExportException;
30 import com.android.sdklib.internal.export.MultiApkExportHelper.Target;
31 import com.android.sdklib.internal.project.ProjectProperties;
32 import com.android.sdklib.internal.project.ProjectProperties.PropertyType;
33
34 import org.eclipse.core.resources.IFile;
35 import org.eclipse.core.resources.IFolder;
36 import org.eclipse.core.resources.IProject;
37 import org.eclipse.core.resources.IResource;
38 import org.eclipse.core.resources.IWorkspace;
39 import org.eclipse.core.resources.IWorkspaceRoot;
40 import org.eclipse.core.resources.IncrementalProjectBuilder;
41 import org.eclipse.core.resources.ResourcesPlugin;
42 import org.eclipse.core.runtime.CoreException;
43 import org.eclipse.core.runtime.IAdaptable;
44 import org.eclipse.core.runtime.IPath;
45 import org.eclipse.core.runtime.IProgressMonitor;
46 import org.eclipse.jdt.core.IJavaProject;
47 import org.eclipse.jdt.core.JavaCore;
48 import org.eclipse.jface.action.IAction;
49 import org.eclipse.jface.operation.IRunnableWithProgress;
50 import org.eclipse.jface.viewers.ISelection;
51 import org.eclipse.jface.viewers.IStructuredSelection;
52 import org.eclipse.ui.IObjectActionDelegate;
53 import org.eclipse.ui.IWorkbench;
54 import org.eclipse.ui.IWorkbenchPart;
55 import org.eclipse.ui.PlatformUI;
56
57 import java.io.File;
58 import java.lang.reflect.InvocationTargetException;
59 import java.util.HashMap;
60 import java.util.Iterator;
61 import java.util.List;
62 import java.util.Map;
63 import java.util.Map.Entry;
64
65 /**
66  * Multiple APK export Action.
67  * The action is triggered on a project selection, and performs a full APK export based on the
68  * content of the export.properties file.
69  */
70 public class MultiApkExportAction implements IObjectActionDelegate {
71
72     private ISelection mSelection;
73
74     public void setActivePart(IAction action, IWorkbenchPart targetPart) {
75         // pass
76     }
77
78     public void run(IAction action) {
79         if (mSelection instanceof IStructuredSelection) {
80             for (Iterator<?> it = ((IStructuredSelection)mSelection).iterator(); it.hasNext();) {
81                 Object element = it.next();
82                 IProject project = null;
83                 if (element instanceof IProject) {
84                     project = (IProject)element;
85                 } else if (element instanceof IAdaptable) {
86                     project = (IProject)((IAdaptable)element).getAdapter(IProject.class);
87                 }
88                 if (project != null) {
89                     IWorkbench workbench = PlatformUI.getWorkbench();
90                     final IProject fProject = project;
91                     try {
92                         workbench.getProgressService().busyCursorWhile(new IRunnableWithProgress() {
93                             /**
94                              * Run the export.
95                              * @throws InvocationTargetException
96                              * @throws InterruptedException
97                              */
98                             public void run(IProgressMonitor monitor)
99                                     throws InvocationTargetException, InterruptedException {
100                                 try {
101                                     runMultiApkExport(fProject, monitor);
102                                 } catch (Exception e) {
103                                     AdtPlugin.logAndPrintError(e, fProject.getName(),
104                                             "Failed to export project: %1$s", e.getMessage());
105                                 } finally {
106                                     monitor.done();
107                                 }
108                             }
109                         });
110                     } catch (Exception e) {
111                         AdtPlugin.logAndPrintError(e, project.getName(),
112                                 "Failed to export project: %1$s",
113                                 e.getMessage());
114                     }
115                 }
116             }
117         }
118     }
119
120     public void selectionChanged(IAction action, ISelection selection) {
121         mSelection = selection;
122     }
123
124     /**
125      * Runs the multi-apk export.
126      * @param exportProject the main "export" project.
127      * @param monitor the progress monitor.
128      * @throws ExportException
129      * @throws CoreException
130      */
131     private void runMultiApkExport(IProject exportProject, IProgressMonitor monitor)
132             throws ExportException, CoreException {
133
134         ProjectProperties props = ProjectProperties.load(new IFolderWrapper(exportProject),
135                 PropertyType.EXPORT);
136
137         // get some props and make sure their values are valid.
138
139         String appPackage = props.getProperty(ProjectProperties.PROPERTY_PACKAGE);
140         if (appPackage == null || appPackage.length() == 0) {
141             throw new IllegalArgumentException("Invalid 'package' property values.");
142         }
143
144         String version = props.getProperty(ProjectProperties.PROPERTY_VERSIONCODE);
145         int versionCode;
146         try {
147             versionCode = Integer.parseInt(version);
148         } catch (NumberFormatException e) {
149             throw new IllegalArgumentException("version value is not a valid integer.", e);
150         }
151
152         String projects = props.getProperty(ProjectProperties.PROPERTY_PROJECTS);
153         if (projects == null || projects.length() == 0) {
154             throw new IllegalArgumentException("Missing project list.");
155         }
156
157         // create the multi apk helper to get the list of apk to export.
158         MultiApkExportHelper helper = new MultiApkExportHelper(
159                 exportProject.getLocation().toOSString(),
160                 appPackage, versionCode, Target.RELEASE, System.out);
161
162         List<ApkData> apks = helper.getApkData(projects);
163
164         // list of projects that have been resolved (ie the IProject has been found from the
165         // ProjectConfig) and compiled.
166         HashMap<ProjectConfig, ProjectState> resolvedProjects =
167                 new HashMap<ProjectConfig, ProjectState>();
168
169         IWorkspace ws = ResourcesPlugin.getWorkspace();
170         IWorkspaceRoot wsRoot = ws.getRoot();
171
172         // bin folder for the export project
173         IFolder binFolder = exportProject.getFolder(SdkConstants.FD_OUTPUT);
174         if (binFolder.exists() == false) {
175             binFolder.create(true, true, monitor);
176         }
177
178         for (ApkData apk : apks) {
179             // find the IProject object for this apk.
180             ProjectConfig projectConfig = apk.getProjectConfig();
181             ProjectState projectState = resolvedProjects.get(projectConfig);
182             if (projectState == null) {
183                 // first time? resolve the project and compile it.
184                 IPath path = exportProject.getFullPath().append(projectConfig.getRelativePath());
185
186                 IResource res = wsRoot.findMember(path);
187                 if (res.getType() != IResource.PROJECT) {
188                     throw new IllegalArgumentException(String.format(
189                             "%1$s does not resolve to a project.",
190                             projectConfig.getRelativePath()));
191                 }
192
193                 IProject project = (IProject)res;
194
195                 projectState = Sdk.getProjectState(project);
196                 if (projectState == null) {
197                     throw new IllegalArgumentException(String.format(
198                             "State for project %1$s could not be loaded.",
199                             project.getName()));
200                 }
201
202                 if (projectState.isLibrary()) {
203                     throw new IllegalArgumentException(String.format(
204                             "Project %1$s is a library and cannot be part of a multi-apk export.",
205                             project.getName()));
206                 }
207
208                 // build the project, mainly for the java compilation. The rest is handled below.
209                 project.build(IncrementalProjectBuilder.INCREMENTAL_BUILD, monitor);
210
211                 // store the resolved project in the map.
212                 resolvedProjects.put(projectConfig, projectState);
213             }
214
215             Map<String, String> variantMap = apk.getSoftVariantMap();
216
217             if (variantMap.size() > 0) {
218                 // if there are soft variants, only export those.
219                 for (Entry<String, String> entry : variantMap.entrySet()) {
220                     buildVariant(wsRoot, projectState, appPackage, versionCode, apk, entry,
221                             binFolder);
222                 }
223             } else {
224                 buildVariant(wsRoot, projectState, appPackage, versionCode, apk,
225                         null /*soft variant*/, binFolder);
226             }
227         }
228
229         helper.writeLogs();
230     }
231
232     /**
233      * Builds a particular variant of an APK
234      * @param wsRoot the workspace root
235      * @param projectState the project to export
236      * @param appPackage the application package
237      * @param versionCode the major version code.
238      * @param apk the {@link ApkData} describing how the export should happen.
239      * @param softVariant an optional soft variant info. The entry contains (name, resource filter).
240      * @param binFolder the binFolder where the file must be created.
241      * @throws CoreException
242      */
243     private void buildVariant(IWorkspaceRoot wsRoot, ProjectState projectState, String appPackage,
244             int versionCode, ApkData apk, Entry<String, String> softVariant, IFolder binFolder)
245             throws CoreException {
246         // get the libraries for this project
247         IProject[] libProjects = projectState.getFullLibraryProjects();
248
249         IProject project = projectState.getProject();
250         IJavaProject javaProject = JavaCore.create(project);
251
252         int compositeVersionCode = apk.getCompositeVersionCode(versionCode);
253
254         // figure out the file names
255         String pkgName = project.getName() + "-" + apk.getBuildInfo();
256         String finalNameRoot = appPackage + "-" + compositeVersionCode;
257         if (softVariant != null) {
258             String tmp = "-" + softVariant.getKey();
259             pkgName += tmp;
260             finalNameRoot += tmp;
261         }
262
263         pkgName += ".ap_";
264         String outputName = finalNameRoot + "-unsigned.apk";
265
266         PostCompilerHelper helper = new PostCompilerHelper(project, System.out, System.err);
267
268         // get the manifest file
269         IFile manifestFile = project.getFile(SdkConstants.FN_ANDROID_MANIFEST_XML);
270         // get the project bin folder
271         IFolder projectBinFolder = wsRoot.getFolder(javaProject.getOutputLocation());
272         String projectBinFolderPath = projectBinFolder.getLocation().toOSString();
273
274         // package the resources
275         if (helper.packageResources(manifestFile, libProjects,
276                 softVariant != null ? softVariant.getValue() : null, compositeVersionCode,
277                 projectBinFolderPath, pkgName) == false) {
278             return;
279         }
280
281         apk.setOutputName(softVariant != null ? softVariant.getKey() : null, outputName);
282
283         // do the final export.
284         IFile dexFile = projectBinFolder.getFile(SdkConstants.FN_APK_CLASSES_DEX);
285         String outputFile = binFolder.getFile(outputName).getLocation().toOSString();
286
287         // get the list of referenced projects.
288         IProject[] javaRefs = ProjectHelper.getReferencedProjects(project);
289         IJavaProject[] referencedJavaProjects = PostCompilerHelper.getJavaProjects(javaRefs);
290
291         helper.finalPackage(
292                 new File(projectBinFolderPath, pkgName).getAbsolutePath(),
293                 dexFile.getLocation().toOSString(),
294                 outputFile,
295                 false /*debugSign */,
296                 javaProject,
297                 libProjects,
298                 referencedJavaProjects,
299                 apk.getAbi(),
300                 false /*debuggable*/);
301
302     }
303 }