| /* |
| * Copyright (C) 2007 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.build.builders; |
| |
| import com.android.SdkConstants; |
| import com.android.ide.eclipse.adt.AdtConstants; |
| import com.android.ide.eclipse.adt.AdtPlugin; |
| import com.android.ide.eclipse.adt.internal.build.Messages; |
| import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs; |
| import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs.BuildVerbosity; |
| import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper; |
| 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.sdklib.BuildToolInfo; |
| import com.android.sdklib.IAndroidTarget; |
| import com.android.utils.Pair; |
| |
| import org.eclipse.core.resources.IFolder; |
| import org.eclipse.core.resources.IMarker; |
| import org.eclipse.core.resources.IProject; |
| import org.eclipse.core.resources.IResource; |
| 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.IPath; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.core.runtime.SubProgressMonitor; |
| import org.eclipse.core.runtime.jobs.Job; |
| import org.eclipse.jdt.core.IClasspathEntry; |
| import org.eclipse.jdt.core.IJavaProject; |
| import org.eclipse.jdt.core.JavaCore; |
| |
| import java.util.List; |
| import java.util.Map; |
| |
| /** |
| * Resource manager builder whose only purpose is to refresh the resource folder |
| * so that the other builder use an up to date version. |
| */ |
| public class ResourceManagerBuilder extends BaseBuilder { |
| |
| public static final String ID = "com.android.ide.eclipse.adt.ResourceManagerBuilder"; //$NON-NLS-1$ |
| |
| public ResourceManagerBuilder() { |
| super(); |
| } |
| |
| @Override |
| protected void clean(IProgressMonitor monitor) throws CoreException { |
| super.clean(monitor); |
| |
| // Get the project. |
| IProject project = getProject(); |
| |
| // Clear the project of the generic markers |
| removeMarkersFromContainer(project, AdtConstants.MARKER_ADT); |
| } |
| |
| // build() returns a list of project from which this project depends for future compilation. |
| @SuppressWarnings("unchecked") |
| @Override |
| protected IProject[] build(int kind, Map args, IProgressMonitor monitor) |
| throws CoreException { |
| // Get the project. |
| final IProject project = getProject(); |
| IJavaProject javaProject = JavaCore.create(project); |
| |
| // Clear the project of the generic markers |
| removeMarkersFromContainer(project, AdtConstants.MARKER_ADT); |
| |
| // check for existing target marker, in which case we abort. |
| // (this means: no SDK, no target, or unresolvable target.) |
| try { |
| abortOnBadSetup(javaProject, null); |
| } catch (AbortBuildException e) { |
| return null; |
| } |
| |
| // Check the compiler compliance level, displaying the error message |
| // since this is the first builder. |
| Pair<Integer, String> result = ProjectHelper.checkCompilerCompliance(project); |
| String errorMessage = null; |
| switch (result.getFirst().intValue()) { |
| case ProjectHelper.COMPILER_COMPLIANCE_LEVEL: |
| errorMessage = Messages.Requires_Compiler_Compliance_s; |
| break; |
| case ProjectHelper.COMPILER_COMPLIANCE_SOURCE: |
| errorMessage = Messages.Requires_Source_Compatibility_s; |
| break; |
| case ProjectHelper.COMPILER_COMPLIANCE_CODEGEN_TARGET: |
| errorMessage = Messages.Requires_Class_Compatibility_s; |
| break; |
| } |
| |
| if (errorMessage != null) { |
| errorMessage = String.format(errorMessage, |
| result.getSecond() == null ? "(no value)" : result.getSecond()); |
| |
| if (JavaCore.VERSION_1_7.equals(result.getSecond())) { |
| // If the user is trying to target 1.7 but compiling with something older, |
| // the error message can be a bit misleading; instead point them in the |
| // direction of updating the project's build target. |
| Sdk currentSdk = Sdk.getCurrent(); |
| if (currentSdk != null) { |
| IAndroidTarget target = currentSdk.getTarget(project.getProject()); |
| if (target != null && target.getVersion().getApiLevel() < 19) { |
| errorMessage = "Using 1.7 requires compiling with Android 4.4 " + |
| "(KitKat); currently using " + target.getVersion(); |
| } |
| |
| ProjectState projectState = Sdk.getProjectState(project); |
| if (projectState != null) { |
| BuildToolInfo buildToolInfo = projectState.getBuildToolInfo(); |
| if (buildToolInfo == null) { |
| buildToolInfo = currentSdk.getLatestBuildTool(); |
| } |
| if (buildToolInfo != null && buildToolInfo.getRevision().getMajor() < 19) { |
| errorMessage = "Using 1.7 requires using Android Build Tools " + |
| "version 19 or later; currently using " + |
| buildToolInfo.getRevision(); |
| } |
| } |
| } |
| } |
| |
| markProject(AdtConstants.MARKER_ADT, errorMessage, IMarker.SEVERITY_ERROR); |
| AdtPlugin.printErrorToConsole(project, errorMessage); |
| |
| return null; |
| } |
| |
| // Check that the SDK directory has been setup. |
| String osSdkFolder = AdtPlugin.getOsSdkFolder(); |
| |
| if (osSdkFolder == null || osSdkFolder.length() == 0) { |
| AdtPlugin.printErrorToConsole(project, Messages.No_SDK_Setup_Error); |
| markProject(AdtConstants.MARKER_ADT, Messages.No_SDK_Setup_Error, |
| IMarker.SEVERITY_ERROR); |
| |
| return null; |
| } |
| |
| // check the 'gen' source folder is present |
| boolean hasGenSrcFolder = false; // whether the project has a 'gen' source folder setup |
| |
| IClasspathEntry[] classpaths = javaProject.readRawClasspath(); |
| if (classpaths != null) { |
| for (IClasspathEntry e : classpaths) { |
| if (e.getEntryKind() == IClasspathEntry.CPE_SOURCE) { |
| IPath path = e.getPath(); |
| if (path.segmentCount() == 2 && |
| path.segment(1).equals(SdkConstants.FD_GEN_SOURCES)) { |
| hasGenSrcFolder = true; |
| break; |
| } |
| } |
| } |
| } |
| |
| boolean genFolderPresent = false; // whether the gen folder actually exists |
| IResource resource = project.findMember(SdkConstants.FD_GEN_SOURCES); |
| genFolderPresent = resource != null && resource.exists(); |
| |
| if (hasGenSrcFolder == false && genFolderPresent) { |
| // No source folder setup for 'gen' in the project, but there's already a |
| // 'gen' resource (file or folder). |
| String message; |
| if (resource.getType() == IResource.FOLDER) { |
| // folder exists already! This is an error. If the folder had been created |
| // by the NewProjectWizard, it'd be a source folder. |
| message = String.format("%1$s already exists but is not a source folder. Convert to a source folder or rename it.", |
| resource.getFullPath().toString()); |
| } else { |
| // resource exists but is not a folder. |
| message = String.format( |
| "Resource %1$s is in the way. ADT needs a source folder called 'gen' to work. Rename or delete resource.", |
| resource.getFullPath().toString()); |
| } |
| |
| AdtPlugin.printErrorToConsole(project, message); |
| markProject(AdtConstants.MARKER_ADT, message, IMarker.SEVERITY_ERROR); |
| |
| return null; |
| } else if (hasGenSrcFolder == false || genFolderPresent == false) { |
| // either there is no 'gen' source folder in the project (older SDK), |
| // or the folder does not exist (was deleted, or was a fresh svn checkout maybe.) |
| |
| // In case we are migrating from an older SDK, we go through the current source |
| // folders and delete the generated Java files. |
| List<IPath> sourceFolders = BaseProjectHelper.getSourceClasspaths(javaProject); |
| IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); |
| for (IPath path : sourceFolders) { |
| IResource member = root.findMember(path); |
| if (member != null) { |
| removeDerivedResources(member, monitor); |
| } |
| } |
| |
| // create the new source folder, if needed |
| IFolder genFolder = project.getFolder(SdkConstants.FD_GEN_SOURCES); |
| if (genFolderPresent == false) { |
| AdtPlugin.printBuildToConsole(BuildVerbosity.VERBOSE, project, |
| "Creating 'gen' source folder for generated Java files"); |
| genFolder.create(true /* force */, true /* local */, |
| new SubProgressMonitor(monitor, 10)); |
| } |
| |
| // add it to the source folder list, if needed only (or it will throw) |
| if (hasGenSrcFolder == false) { |
| IClasspathEntry[] entries = javaProject.getRawClasspath(); |
| entries = ProjectHelper.addEntryToClasspath(entries, |
| JavaCore.newSourceEntry(genFolder.getFullPath())); |
| javaProject.setRawClasspath(entries, new SubProgressMonitor(monitor, 10)); |
| } |
| |
| // refresh specifically the gen folder first, as it may break the build |
| // if it doesn't arrive in time then refresh the whole project as usual. |
| genFolder.refreshLocal(IResource.DEPTH_ZERO, new SubProgressMonitor(monitor, 10)); |
| project.refreshLocal(IResource.DEPTH_INFINITE, new SubProgressMonitor(monitor, 10)); |
| |
| // it seems like doing this fails to properly rebuild the project. the Java builder |
| // running right after this builder will not see the gen folder, and will not be |
| // restarted after this build. Therefore in this particular case, we start another |
| // build asynchronously so that it's rebuilt after this build. |
| launchJob(new Job("rebuild") { |
| @Override |
| protected IStatus run(IProgressMonitor m) { |
| try { |
| project.build(IncrementalProjectBuilder.INCREMENTAL_BUILD, m); |
| return Status.OK_STATUS; |
| } catch (CoreException e) { |
| return e.getStatus(); |
| } |
| } |
| }); |
| |
| } |
| |
| // convert older projects which use bin as the eclipse output folder into projects |
| // using bin/classes |
| IFolder androidOutput = BaseProjectHelper.getAndroidOutputFolder(project); |
| IFolder javaOutput = BaseProjectHelper.getJavaOutputFolder(project); |
| if (androidOutput.exists() == false || javaOutput == null || |
| javaOutput.getParent().equals(androidOutput) == false) { |
| // get what we want as the new java output. |
| IFolder newJavaOutput = androidOutput.getFolder(SdkConstants.FD_CLASSES_OUTPUT); |
| |
| if (androidOutput.exists() == false) { |
| androidOutput.create(true /*force*/, true /*local*/, monitor); |
| } |
| |
| if (newJavaOutput.exists() == false) { |
| newJavaOutput.create(true /*force*/, true /*local*/, monitor); |
| } |
| |
| // set the java output to this project. |
| javaProject.setOutputLocation(newJavaOutput.getFullPath(), monitor); |
| |
| // need to do a full build. Can't build while we're already building, so launch a |
| // job to build it right after this build |
| launchJob(new Job("rebuild") { |
| @Override |
| protected IStatus run(IProgressMonitor jobMonitor) { |
| try { |
| project.build(IncrementalProjectBuilder.CLEAN_BUILD, jobMonitor); |
| return Status.OK_STATUS; |
| } catch (CoreException e) { |
| return e.getStatus(); |
| } |
| } |
| }); |
| } |
| |
| // check that we have bin/res/ |
| IFolder binResFolder = androidOutput.getFolder(SdkConstants.FD_RESOURCES); |
| if (binResFolder.exists() == false) { |
| binResFolder.create(true /* force */, true /* local */, |
| new SubProgressMonitor(monitor, 10)); |
| project.refreshLocal(IResource.DEPTH_ONE, new SubProgressMonitor(monitor, 10)); |
| } |
| |
| // Check the preference to be sure we are supposed to refresh |
| // the folders. |
| if (AdtPrefs.getPrefs().getBuildForceResResfresh()) { |
| AdtPlugin.printBuildToConsole(BuildVerbosity.VERBOSE, project, Messages.Refreshing_Res); |
| |
| // refresh the res folder. |
| IFolder resFolder = project.getFolder(AdtConstants.WS_RESOURCES); |
| resFolder.refreshLocal(IResource.DEPTH_INFINITE, monitor); |
| |
| // Also refresh the assets folder to make sure the ApkBuilder |
| // will now it's changed and will force a new resource packaging. |
| IFolder assetsFolder = project.getFolder(AdtConstants.WS_ASSETS); |
| assetsFolder.refreshLocal(IResource.DEPTH_INFINITE, monitor); |
| } |
| |
| return null; |
| } |
| } |