• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2007 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.build.builders;
18 
19 import com.android.SdkConstants;
20 import com.android.ide.eclipse.adt.AdtConstants;
21 import com.android.ide.eclipse.adt.AdtPlugin;
22 import com.android.ide.eclipse.adt.internal.build.Messages;
23 import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs;
24 import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs.BuildVerbosity;
25 import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper;
26 import com.android.ide.eclipse.adt.internal.project.ProjectHelper;
27 import com.android.ide.eclipse.adt.internal.sdk.Sdk;
28 import com.android.sdklib.IAndroidTarget;
29 import com.android.utils.Pair;
30 
31 import org.eclipse.core.resources.IFolder;
32 import org.eclipse.core.resources.IMarker;
33 import org.eclipse.core.resources.IProject;
34 import org.eclipse.core.resources.IResource;
35 import org.eclipse.core.resources.IWorkspaceRoot;
36 import org.eclipse.core.resources.IncrementalProjectBuilder;
37 import org.eclipse.core.resources.ResourcesPlugin;
38 import org.eclipse.core.runtime.CoreException;
39 import org.eclipse.core.runtime.IPath;
40 import org.eclipse.core.runtime.IProgressMonitor;
41 import org.eclipse.core.runtime.IStatus;
42 import org.eclipse.core.runtime.Status;
43 import org.eclipse.core.runtime.SubProgressMonitor;
44 import org.eclipse.core.runtime.jobs.Job;
45 import org.eclipse.jdt.core.IClasspathEntry;
46 import org.eclipse.jdt.core.IJavaProject;
47 import org.eclipse.jdt.core.JavaCore;
48 
49 import java.util.List;
50 import java.util.Map;
51 
52 /**
53  * Resource manager builder whose only purpose is to refresh the resource folder
54  * so that the other builder use an up to date version.
55  */
56 public class ResourceManagerBuilder extends BaseBuilder {
57 
58     public static final String ID = "com.android.ide.eclipse.adt.ResourceManagerBuilder"; //$NON-NLS-1$
59 
ResourceManagerBuilder()60     public ResourceManagerBuilder() {
61         super();
62     }
63 
64     @Override
clean(IProgressMonitor monitor)65     protected void clean(IProgressMonitor monitor) throws CoreException {
66         super.clean(monitor);
67 
68         // Get the project.
69         IProject project = getProject();
70 
71         // Clear the project of the generic markers
72         removeMarkersFromContainer(project, AdtConstants.MARKER_ADT);
73     }
74 
75     // build() returns a list of project from which this project depends for future compilation.
76     @SuppressWarnings("unchecked")
77     @Override
build(int kind, Map args, IProgressMonitor monitor)78     protected IProject[] build(int kind, Map args, IProgressMonitor monitor)
79             throws CoreException {
80         // Get the project.
81         final IProject project = getProject();
82         IJavaProject javaProject = JavaCore.create(project);
83 
84         // Clear the project of the generic markers
85         removeMarkersFromContainer(project, AdtConstants.MARKER_ADT);
86 
87         // check for existing target marker, in which case we abort.
88         // (this means: no SDK, no target, or unresolvable target.)
89         try {
90             abortOnBadSetup(javaProject);
91         } catch (AbortBuildException e) {
92             return null;
93         }
94 
95         // Check the compiler compliance level, displaying the error message
96         // since this is the first builder.
97         Pair<Integer, String> result = ProjectHelper.checkCompilerCompliance(project);
98         String errorMessage = null;
99         switch (result.getFirst().intValue()) {
100             case ProjectHelper.COMPILER_COMPLIANCE_LEVEL:
101                 errorMessage = Messages.Requires_Compiler_Compliance_s;
102                 break;
103             case ProjectHelper.COMPILER_COMPLIANCE_SOURCE:
104                 errorMessage = Messages.Requires_Source_Compatibility_s;
105                 break;
106             case ProjectHelper.COMPILER_COMPLIANCE_CODEGEN_TARGET:
107                 errorMessage = Messages.Requires_Class_Compatibility_s;
108                 break;
109         }
110 
111         if (errorMessage != null) {
112             errorMessage = String.format(errorMessage,
113                     result.getSecond() == null ? "(no value)" : result.getSecond());
114 
115             markProject(AdtConstants.MARKER_ADT, errorMessage, IMarker.SEVERITY_ERROR);
116             AdtPlugin.printErrorToConsole(project, errorMessage);
117 
118             return null;
119         }
120 
121         // Check that the SDK directory has been setup.
122         String osSdkFolder = AdtPlugin.getOsSdkFolder();
123 
124         if (osSdkFolder == null || osSdkFolder.length() == 0) {
125             AdtPlugin.printErrorToConsole(project, Messages.No_SDK_Setup_Error);
126             markProject(AdtConstants.MARKER_ADT, Messages.No_SDK_Setup_Error,
127                     IMarker.SEVERITY_ERROR);
128 
129             return null;
130         }
131 
132         // check the project has a target
133         IAndroidTarget projectTarget = Sdk.getCurrent().getTarget(project);
134         if (projectTarget == null) {
135             // no target. marker has been set by the container initializer: exit silently.
136             return null;
137         }
138 
139         // check the 'gen' source folder is present
140         boolean hasGenSrcFolder = false; // whether the project has a 'gen' source folder setup
141 
142         IClasspathEntry[] classpaths = javaProject.readRawClasspath();
143         if (classpaths != null) {
144             for (IClasspathEntry e : classpaths) {
145                 if (e.getEntryKind() == IClasspathEntry.CPE_SOURCE) {
146                     IPath path = e.getPath();
147                     if (path.segmentCount() == 2 &&
148                             path.segment(1).equals(SdkConstants.FD_GEN_SOURCES)) {
149                         hasGenSrcFolder = true;
150                         break;
151                     }
152                 }
153             }
154         }
155 
156         boolean genFolderPresent = false; // whether the gen folder actually exists
157         IResource resource = project.findMember(SdkConstants.FD_GEN_SOURCES);
158         genFolderPresent = resource != null && resource.exists();
159 
160         if (hasGenSrcFolder == false && genFolderPresent) {
161             // No source folder setup for 'gen' in the project, but there's already a
162             // 'gen' resource (file or folder).
163             String message;
164             if (resource.getType() == IResource.FOLDER) {
165                 // folder exists already! This is an error. If the folder had been created
166                 // by the NewProjectWizard, it'd be a source folder.
167                 message = String.format("%1$s already exists but is not a source folder. Convert to a source folder or rename it.",
168                         resource.getFullPath().toString());
169             } else {
170                 // resource exists but is not a folder.
171                 message = String.format(
172                         "Resource %1$s is in the way. ADT needs a source folder called 'gen' to work. Rename or delete resource.",
173                         resource.getFullPath().toString());
174             }
175 
176             AdtPlugin.printErrorToConsole(project, message);
177             markProject(AdtConstants.MARKER_ADT, message, IMarker.SEVERITY_ERROR);
178 
179             return null;
180         } else if (hasGenSrcFolder == false || genFolderPresent == false) {
181             // either there is no 'gen' source folder in the project (older SDK),
182             // or the folder does not exist (was deleted, or was a fresh svn checkout maybe.)
183 
184             // In case we are migrating from an older SDK, we go through the current source
185             // folders and delete the generated Java files.
186             List<IPath> sourceFolders = BaseProjectHelper.getSourceClasspaths(javaProject);
187             IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
188             for (IPath path : sourceFolders) {
189                 IResource member = root.findMember(path);
190                 if (member != null) {
191                     removeDerivedResources(member, monitor);
192                 }
193             }
194 
195             // create the new source folder, if needed
196             IFolder genFolder = project.getFolder(SdkConstants.FD_GEN_SOURCES);
197             if (genFolderPresent == false) {
198                 AdtPlugin.printBuildToConsole(BuildVerbosity.VERBOSE, project,
199                         "Creating 'gen' source folder for generated Java files");
200                 genFolder.create(true /* force */, true /* local */,
201                         new SubProgressMonitor(monitor, 10));
202             }
203 
204             // add it to the source folder list, if needed only (or it will throw)
205             if (hasGenSrcFolder == false) {
206                 IClasspathEntry[] entries = javaProject.getRawClasspath();
207                 entries = ProjectHelper.addEntryToClasspath(entries,
208                         JavaCore.newSourceEntry(genFolder.getFullPath()));
209                 javaProject.setRawClasspath(entries, new SubProgressMonitor(monitor, 10));
210             }
211 
212             // refresh specifically the gen folder first, as it may break the build
213             // if it doesn't arrive in time then refresh the whole project as usual.
214             genFolder.refreshLocal(IResource.DEPTH_ZERO, new SubProgressMonitor(monitor, 10));
215             project.refreshLocal(IResource.DEPTH_INFINITE, new SubProgressMonitor(monitor, 10));
216 
217             // it seems like doing this fails to properly rebuild the project. the Java builder
218             // running right after this builder will not see the gen folder, and will not be
219             // restarted after this build. Therefore in this particular case, we start another
220             // build asynchronously so that it's rebuilt after this build.
221             launchJob(new Job("rebuild") {
222                 @Override
223                 protected IStatus run(IProgressMonitor m) {
224                     try {
225                         project.build(IncrementalProjectBuilder.INCREMENTAL_BUILD, m);
226                         return Status.OK_STATUS;
227                     } catch (CoreException e) {
228                         return e.getStatus();
229                     }
230                 }
231             });
232 
233         }
234 
235         // convert older projects which use bin as the eclipse output folder into projects
236         // using bin/classes
237         IFolder androidOutput = BaseProjectHelper.getAndroidOutputFolder(project);
238         IFolder javaOutput = BaseProjectHelper.getJavaOutputFolder(project);
239         if (androidOutput.exists() == false || javaOutput == null ||
240                 javaOutput.getParent().equals(androidOutput) == false) {
241             // get what we want as the new java output.
242             IFolder newJavaOutput = androidOutput.getFolder(SdkConstants.FD_CLASSES_OUTPUT);
243 
244             if (androidOutput.exists() == false) {
245                 androidOutput.create(true /*force*/, true /*local*/, monitor);
246             }
247 
248             if (newJavaOutput.exists() == false) {
249                 newJavaOutput.create(true /*force*/, true /*local*/, monitor);
250             }
251 
252             // set the java output to this project.
253             javaProject.setOutputLocation(newJavaOutput.getFullPath(), monitor);
254 
255             // need to do a full build. Can't build while we're already building, so launch a
256             // job to build it right after this build
257             launchJob(new Job("rebuild") {
258                 @Override
259                 protected IStatus run(IProgressMonitor jobMonitor) {
260                     try {
261                         project.build(IncrementalProjectBuilder.CLEAN_BUILD, jobMonitor);
262                         return Status.OK_STATUS;
263                     } catch (CoreException e) {
264                         return e.getStatus();
265                     }
266                 }
267             });
268         }
269 
270         // check that we have bin/res/
271         IFolder binResFolder = androidOutput.getFolder(SdkConstants.FD_RESOURCES);
272         if (binResFolder.exists() == false) {
273             binResFolder.create(true /* force */, true /* local */,
274                     new SubProgressMonitor(monitor, 10));
275             project.refreshLocal(IResource.DEPTH_ONE, new SubProgressMonitor(monitor, 10));
276         }
277 
278         // Check the preference to be sure we are supposed to refresh
279         // the folders.
280         if (AdtPrefs.getPrefs().getBuildForceResResfresh()) {
281             AdtPlugin.printBuildToConsole(BuildVerbosity.VERBOSE, project, Messages.Refreshing_Res);
282 
283             // refresh the res folder.
284             IFolder resFolder = project.getFolder(AdtConstants.WS_RESOURCES);
285             resFolder.refreshLocal(IResource.DEPTH_INFINITE, monitor);
286 
287             // Also refresh the assets folder to make sure the ApkBuilder
288             // will now it's changed and will force a new resource packaging.
289             IFolder assetsFolder = project.getFolder(AdtConstants.WS_ASSETS);
290             assetsFolder.refreshLocal(IResource.DEPTH_INFINITE, monitor);
291         }
292 
293         return null;
294     }
295 }
296