blob: 0d9ee48979787607a353cf628e4058c85de5f1a2 [file] [log] [blame]
/*
* 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.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.ide.common.xml.ManifestData;
import com.android.ide.eclipse.adt.AdtConstants;
import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.internal.build.AaptParser;
import com.android.ide.eclipse.adt.internal.build.AidlProcessor;
import com.android.ide.eclipse.adt.internal.build.Messages;
import com.android.ide.eclipse.adt.internal.build.RenderScriptLauncher;
import com.android.ide.eclipse.adt.internal.build.RsSourceChangeHandler;
import com.android.ide.eclipse.adt.internal.build.SourceProcessor;
import com.android.ide.eclipse.adt.internal.build.builders.BaseBuilder.AbortBuildException;
import com.android.ide.eclipse.adt.internal.lint.EclipseLintClient;
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.AndroidManifestHelper;
import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper;
import com.android.ide.eclipse.adt.internal.project.FixLaunchConfig;
import com.android.ide.eclipse.adt.internal.project.ProjectHelper;
import com.android.ide.eclipse.adt.internal.project.XmlErrorHandler.BasicXmlErrorListener;
import com.android.ide.eclipse.adt.internal.resources.manager.IdeScanningContext;
import com.android.ide.eclipse.adt.internal.resources.manager.ProjectResources;
import com.android.ide.eclipse.adt.internal.resources.manager.ResourceManager;
import com.android.ide.eclipse.adt.internal.sdk.AdtManifestMergeCallback;
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.IFileWrapper;
import com.android.ide.eclipse.adt.io.IFolderWrapper;
import com.android.io.StreamException;
import com.android.manifmerger.ManifestMerger;
import com.android.manifmerger.MergerLog;
import com.android.sdklib.AndroidVersion;
import com.android.sdklib.BuildToolInfo;
import com.android.sdklib.IAndroidTarget;
import com.android.sdklib.build.RenderScriptChecker;
import com.android.sdklib.build.RenderScriptProcessor;
import com.android.sdklib.internal.build.BuildConfigGenerator;
import com.android.sdklib.internal.build.SymbolLoader;
import com.android.sdklib.internal.build.SymbolWriter;
import com.android.sdklib.internal.project.ProjectProperties;
import com.android.sdklib.io.FileOp;
import com.android.sdklib.repository.FullRevision;
import com.android.utils.ILogger;
import com.android.utils.Pair;
import com.android.xml.AndroidManifest;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;
import org.eclipse.core.resources.IFile;
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.IResourceDelta;
import org.eclipse.core.resources.IWorkspaceRoot;
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.NullProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
import org.xml.sax.SAXException;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import javax.xml.parsers.ParserConfigurationException;
/**
* Pre Java Compiler.
* This incremental builder performs 2 tasks:
* <ul>
* <li>compiles the resources located in the res/ folder, along with the
* AndroidManifest.xml file into the R.java class.</li>
* <li>compiles any .aidl files into a corresponding java file.</li>
* </ul>
*
*/
public class PreCompilerBuilder extends BaseBuilder {
/** This ID is used in plugin.xml and in each project's .project file.
* It cannot be changed even if the class is renamed/moved */
public static final String ID = "com.android.ide.eclipse.adt.PreCompilerBuilder"; //$NON-NLS-1$
/** Flag to pass to PreCompiler builder that the build is a release build.
*/
public final static String RELEASE_REQUESTED = "android.releaseBuild"; //$NON-NLS-1$
private static final String PROPERTY_PACKAGE = "manifestPackage"; //$NON-NLS-1$
private static final String PROPERTY_MERGE_MANIFEST = "mergeManifest"; //$NON-NLS-1$
private static final String PROPERTY_COMPILE_RESOURCES = "compileResources"; //$NON-NLS-1$
private static final String PROPERTY_COMPILE_BUILDCONFIG = "createBuildConfig"; //$NON-NLS-1$
private static final String PROPERTY_BUILDCONFIG_MODE = "buildConfigMode"; //$NON-NLS-1$
private static final boolean MANIFEST_MERGER_ENABLED_DEFAULT = false;
private static final String MANIFEST_MERGER_PROPERTY = "manifestmerger.enabled"; //$NON-NLS-1$
/** Merge Manifest Flag. Computed from resource delta, reset after action is taken.
* Stored persistently in the project. */
private boolean mMustMergeManifest = false;
/** Resource compilation Flag. Computed from resource delta, reset after action is taken.
* Stored persistently in the project. */
private boolean mMustCompileResources = false;
/** BuildConfig Flag. Computed from resource delta, reset after action is taken.
* Stored persistently in the project. */
private boolean mMustCreateBuildConfig = false;
/** BuildConfig last more Flag. Computed from resource delta, reset after action is taken.
* Stored persistently in the project. */
private boolean mLastBuildConfigMode;
/** cache of the java package defined in the manifest */
private String mManifestPackage;
/** Output folder for generated Java File. Created on the Builder init
* @see #startupOnInitialize()
*/
private IFolder mGenFolder;
/**
* Progress monitor used at the end of every build to refresh the content of the 'gen' folder
* and set the generated files as derived.
*/
private DerivedProgressMonitor mDerivedProgressMonitor;
private AidlProcessor mAidlProcessor;
private RsSourceChangeHandler mRenderScriptSourceChangeHandler;
/**
* Progress monitor waiting the end of the process to set a persistent value
* in a file. This is typically used in conjunction with <code>IResource.refresh()</code>,
* since this call is asynchronous, and we need to wait for it to finish for the file
* to be known by eclipse, before we can call <code>resource.setPersistentProperty</code> on
* a new file.
*/
private static class DerivedProgressMonitor implements IProgressMonitor {
private boolean mCancelled = false;
private boolean mDone = false;
private final IFolder mGenFolder;
public DerivedProgressMonitor(IFolder genFolder) {
mGenFolder = genFolder;
}
void reset() {
mDone = false;
}
@Override
public void beginTask(String name, int totalWork) {
}
@Override
public void done() {
if (mDone == false) {
mDone = true;
processChildrenOf(mGenFolder);
}
}
private void processChildrenOf(IFolder folder) {
IResource[] list;
try {
list = folder.members();
} catch (CoreException e) {
return;
}
for (IResource member : list) {
if (member.exists()) {
if (member.getType() == IResource.FOLDER) {
processChildrenOf((IFolder) member);
}
try {
member.setDerived(true, new NullProgressMonitor());
} catch (CoreException e) {
// This really shouldn't happen since we check that the resource
// exist.
// Worst case scenario, the resource isn't marked as derived.
}
}
}
}
@Override
public void internalWorked(double work) {
}
@Override
public boolean isCanceled() {
return mCancelled;
}
@Override
public void setCanceled(boolean value) {
mCancelled = value;
}
@Override
public void setTaskName(String name) {
}
@Override
public void subTask(String name) {
}
@Override
public void worked(int work) {
}
}
public PreCompilerBuilder() {
super();
}
// build() returns a list of project from which this project depends for future compilation.
@Override
protected IProject[] build(
int kind,
@SuppressWarnings("rawtypes") Map args,
IProgressMonitor monitor)
throws CoreException {
// get a project object
IProject project = getProject();
if (DEBUG_LOG) {
AdtPlugin.log(IStatus.INFO, "%s BUILD(PRE)", project.getName());
}
// For the PreCompiler, only the library projects are considered Referenced projects,
// as only those projects have an impact on what is generated by this builder.
IProject[] result = null;
IFolder resOutFolder = null;
try {
assert mDerivedProgressMonitor != null;
mDerivedProgressMonitor.reset();
// get the project info
ProjectState projectState = Sdk.getProjectState(project);
// this can happen if the project has no project.properties.
if (projectState == null) {
return null;
}
boolean isLibrary = projectState.isLibrary();
IAndroidTarget projectTarget = projectState.getTarget();
// get the libraries
List<IProject> libProjects = projectState.getFullLibraryProjects();
result = libProjects.toArray(new IProject[libProjects.size()]);
IJavaProject javaProject = JavaCore.create(project);
// Top level check to make sure the build can move forward.
abortOnBadSetup(javaProject, projectState);
// now we need to get the classpath list
List<IPath> sourceFolderPathList = BaseProjectHelper.getSourceClasspaths(javaProject);
IFolder androidOutputFolder = BaseProjectHelper.getAndroidOutputFolder(project);
resOutFolder = getResOutFolder(androidOutputFolder);
setupSourceProcessors(javaProject, projectState, sourceFolderPathList,
androidOutputFolder);
PreCompilerDeltaVisitor dv = null;
String javaPackage = null;
String minSdkVersion = null;
if (kind == FULL_BUILD) {
AdtPlugin.printBuildToConsole(BuildVerbosity.VERBOSE, project,
Messages.Start_Full_Pre_Compiler);
if (DEBUG_LOG) {
AdtPlugin.log(IStatus.INFO, "%s full build!", project.getName());
}
// do some clean up.
doClean(project, monitor);
mMustMergeManifest = true;
mMustCompileResources = true;
mMustCreateBuildConfig = true;
mAidlProcessor.prepareFullBuild(project);
mRenderScriptSourceChangeHandler.prepareFullBuild();
} else {
AdtPlugin.printBuildToConsole(BuildVerbosity.VERBOSE, project,
Messages.Start_Inc_Pre_Compiler);
// Go through the resources and see if something changed.
// Even if the mCompileResources flag is true from a previously aborted
// build, we need to go through the Resource delta to get a possible
// list of aidl files to compile/remove.
IResourceDelta delta = getDelta(project);
if (delta == null) {
mMustCompileResources = true;
mAidlProcessor.prepareFullBuild(project);
mRenderScriptSourceChangeHandler.prepareFullBuild();
} else {
dv = new PreCompilerDeltaVisitor(this, sourceFolderPathList,
mAidlProcessor.getChangeHandler(),
mRenderScriptSourceChangeHandler);
delta.accept(dv);
// Check to see if Manifest.xml, Manifest.java, or R.java have changed:
mMustCompileResources |= dv.getCompileResources();
mMustMergeManifest |= dv.hasManifestChanged();
// Notify the ResourceManager:
ResourceManager resManager = ResourceManager.getInstance();
if (ResourceManager.isAutoBuilding()) {
ProjectResources projectResources = resManager.getProjectResources(project);
IdeScanningContext context = new IdeScanningContext(projectResources,
project, true);
boolean wasCleared = projectResources.ensureInitialized();
if (!wasCleared) {
resManager.processDelta(delta, context);
}
// Check whether this project or its dependencies (libraries) have
// resources that need compilation
if (wasCleared || context.needsFullAapt()) {
mMustCompileResources = true;
// Must also call markAaptRequested on the project to not just
// store "aapt required" on this project, but also on any projects
// depending on this project if it's a library project
ResourceManager.markAaptRequested(project);
}
// Update error markers in the source editor
if (!mMustCompileResources) {
context.updateMarkers(false /* async */);
}
} // else: already processed the deltas in ResourceManager's IRawDeltaListener
mAidlProcessor.doneVisiting(project);
// get the java package from the visitor
javaPackage = dv.getManifestPackage();
minSdkVersion = dv.getMinSdkVersion();
}
}
// Has anyone marked this project as needing aapt? Typically done when
// one of the library projects this project depends on has changed
mMustCompileResources |= ResourceManager.isAaptRequested(project);
// if the main manifest didn't change, then we check for the library
// ones (will trigger manifest merging too)
if (libProjects.size() > 0) {
for (IProject libProject : libProjects) {
IResourceDelta delta = getDelta(libProject);
if (delta != null) {
PatternBasedDeltaVisitor visitor = new PatternBasedDeltaVisitor(
project, libProject,
"PRE:LibManifest"); //$NON-NLS-1$
visitor.addSet(ChangedFileSetHelper.MANIFEST);
ChangedFileSet textSymbolCFS = null;
if (isLibrary == false) {
textSymbolCFS = ChangedFileSetHelper.getTextSymbols(
libProject);
visitor.addSet(textSymbolCFS);
}
delta.accept(visitor);
mMustMergeManifest |= visitor.checkSet(ChangedFileSetHelper.MANIFEST);
if (textSymbolCFS != null) {
mMustCompileResources |= visitor.checkSet(textSymbolCFS);
}
// no need to test others if we have all flags at true.
if (mMustMergeManifest &&
(mMustCompileResources || textSymbolCFS == null)) {
break;
}
}
}
}
// store the build status in the persistent storage
saveProjectBooleanProperty(PROPERTY_MERGE_MANIFEST, mMustMergeManifest);
saveProjectBooleanProperty(PROPERTY_COMPILE_RESOURCES, mMustCompileResources);
saveProjectBooleanProperty(PROPERTY_COMPILE_BUILDCONFIG, mMustCreateBuildConfig);
// if there was some XML errors, we just return w/o doing
// anything since we've put some markers in the files anyway.
if (dv != null && dv.mXmlError) {
AdtPlugin.printErrorToConsole(project, Messages.Xml_Error);
return result;
}
if (projectState.getRenderScriptSupportMode()) {
FullRevision minBuildToolsRev = new FullRevision(19,0,3);
if (mBuildToolInfo.getRevision().compareTo(minBuildToolsRev) == -1) {
String msg = "RenderScript support mode requires Build-Tools 19.0.3 or later.";
AdtPlugin.printErrorToConsole(project, msg);
markProject(AdtConstants.MARKER_ADT, msg, IMarker.SEVERITY_ERROR);
return result;
}
}
// get the manifest file
IFile manifestFile = ProjectHelper.getManifest(project);
if (manifestFile == null) {
String msg = String.format(Messages.s_File_Missing,
SdkConstants.FN_ANDROID_MANIFEST_XML);
AdtPlugin.printErrorToConsole(project, msg);
markProject(AdtConstants.MARKER_ADT, msg, IMarker.SEVERITY_ERROR);
return result;
// TODO: document whether code below that uses manifest (which is now guaranteed
// to be null) will actually be executed or not.
}
// lets check the XML of the manifest first, if that hasn't been done by the
// resource delta visitor yet.
if (dv == null || dv.getCheckedManifestXml() == false) {
BasicXmlErrorListener errorListener = new BasicXmlErrorListener();
try {
ManifestData parser = AndroidManifestHelper.parseUnchecked(
new IFileWrapper(manifestFile),
true /*gather data*/,
errorListener);
if (errorListener.mHasXmlError == true) {
// There was an error in the manifest, its file has been marked
// by the XmlErrorHandler. The stopBuild() call below will abort
// this with an exception.
String msg = String.format(Messages.s_Contains_Xml_Error,
SdkConstants.FN_ANDROID_MANIFEST_XML);
AdtPlugin.printBuildToConsole(BuildVerbosity.VERBOSE, project, msg);
markProject(AdtConstants.MARKER_ADT, msg, IMarker.SEVERITY_ERROR);
return result;
}
// Get the java package from the parser.
// This can be null if the parsing failed because the resource is out of sync,
// in which case the error will already have been logged anyway.
if (parser != null) {
javaPackage = parser.getPackage();
minSdkVersion = parser.getMinSdkVersionString();
}
} catch (StreamException e) {
handleStreamException(e);
return result;
} catch (ParserConfigurationException e) {
String msg = String.format(
"Bad parser configuration for %s: %s",
manifestFile.getFullPath(),
e.getMessage());
handleException(e, msg);
return result;
} catch (SAXException e) {
String msg = String.format(
"Parser exception for %s: %s",
manifestFile.getFullPath(),
e.getMessage());
handleException(e, msg);
return result;
} catch (IOException e) {
String msg = String.format(
"I/O error for %s: %s",
manifestFile.getFullPath(),
e.getMessage());
handleException(e, msg);
return result;
}
}
int minSdkValue = -1;
if (minSdkVersion != null) {
try {
minSdkValue = Integer.parseInt(minSdkVersion);
} catch (NumberFormatException e) {
// it's ok, it means minSdkVersion contains a (hopefully) valid codename.
}
AndroidVersion targetVersion = projectTarget.getVersion();
// remove earlier marker from the manifest
removeMarkersFromResource(manifestFile, AdtConstants.MARKER_ADT);
if (minSdkValue != -1) {
String codename = targetVersion.getCodename();
if (codename != null) {
// integer minSdk when the target is a preview => fatal error
String msg = String.format(
"Platform %1$s is a preview and requires application manifest to set %2$s to '%1$s'",
codename, AndroidManifest.ATTRIBUTE_MIN_SDK_VERSION);
AdtPlugin.printErrorToConsole(project, msg);
BaseProjectHelper.markResource(manifestFile, AdtConstants.MARKER_ADT,
msg, IMarker.SEVERITY_ERROR);
return result;
} else if (minSdkValue > targetVersion.getApiLevel()) {
// integer minSdk is too high for the target => warning
String msg = String.format(
"Attribute %1$s (%2$d) is higher than the project target API level (%3$d)",
AndroidManifest.ATTRIBUTE_MIN_SDK_VERSION,
minSdkValue, targetVersion.getApiLevel());
AdtPlugin.printBuildToConsole(BuildVerbosity.VERBOSE, project, msg);
BaseProjectHelper.markResource(manifestFile, AdtConstants.MARKER_ADT,
msg, IMarker.SEVERITY_WARNING);
}
} else {
// looks like the min sdk is a codename, check it matches the codename
// of the platform
String codename = targetVersion.getCodename();
if (codename == null) {
// platform is not a preview => fatal error
String msg = String.format(
"Manifest attribute '%1$s' is set to '%2$s'. Integer is expected.",
AndroidManifest.ATTRIBUTE_MIN_SDK_VERSION, minSdkVersion);
AdtPlugin.printErrorToConsole(project, msg);
BaseProjectHelper.markResource(manifestFile, AdtConstants.MARKER_ADT,
msg, IMarker.SEVERITY_ERROR);
return result;
} else if (codename.equals(minSdkVersion) == false) {
// platform and manifest codenames don't match => fatal error.
String msg = String.format(
"Value of manifest attribute '%1$s' does not match platform codename '%2$s'",
AndroidManifest.ATTRIBUTE_MIN_SDK_VERSION, codename);
AdtPlugin.printErrorToConsole(project, msg);
BaseProjectHelper.markResource(manifestFile, AdtConstants.MARKER_ADT,
msg, IMarker.SEVERITY_ERROR);
return result;
}
// if we get there, the minSdkVersion is a codename matching the target
// platform codename. In this case we set minSdkValue to the previous API
// level, as it's used by source processors.
minSdkValue = targetVersion.getApiLevel();
}
} else if (projectTarget.getVersion().isPreview()) {
// else the minSdkVersion is not set but we are using a preview target.
// Display an error
String codename = projectTarget.getVersion().getCodename();
String msg = String.format(
"Platform %1$s is a preview and requires application manifests to set %2$s to '%1$s'",
codename, AndroidManifest.ATTRIBUTE_MIN_SDK_VERSION);
AdtPlugin.printErrorToConsole(project, msg);
BaseProjectHelper.markResource(manifestFile, AdtConstants.MARKER_ADT, msg,
IMarker.SEVERITY_ERROR);
return result;
}
if (javaPackage == null || javaPackage.length() == 0) {
// looks like the AndroidManifest file isn't valid.
String msg = String.format(Messages.s_Doesnt_Declare_Package_Error,
SdkConstants.FN_ANDROID_MANIFEST_XML);
AdtPlugin.printErrorToConsole(project, msg);
BaseProjectHelper.markResource(manifestFile, AdtConstants.MARKER_ADT,
msg, IMarker.SEVERITY_ERROR);
return result;
} else if (javaPackage.indexOf('.') == -1) {
// The application package name does not contain 2+ segments!
String msg = String.format(
"Application package '%1$s' must have a minimum of 2 segments.",
SdkConstants.FN_ANDROID_MANIFEST_XML);
AdtPlugin.printErrorToConsole(project, msg);
BaseProjectHelper.markResource(manifestFile, AdtConstants.MARKER_ADT,
msg, IMarker.SEVERITY_ERROR);
return result;
}
// at this point we have the java package. We need to make sure it's not a different
// package than the previous one that were built.
if (javaPackage.equals(mManifestPackage) == false) {
// The manifest package has changed, the user may want to update
// the launch configuration
if (mManifestPackage != null) {
AdtPlugin.printBuildToConsole(BuildVerbosity.VERBOSE, project,
Messages.Checking_Package_Change);
FixLaunchConfig flc = new FixLaunchConfig(project, mManifestPackage,
javaPackage);
flc.start();
}
// record the new manifest package, and save it.
mManifestPackage = javaPackage;
saveProjectStringProperty(PROPERTY_PACKAGE, mManifestPackage);
// force a clean
doClean(project, monitor);
mMustMergeManifest = true;
mMustCompileResources = true;
mMustCreateBuildConfig = true;
mAidlProcessor.prepareFullBuild(project);
mRenderScriptSourceChangeHandler.prepareFullBuild();
saveProjectBooleanProperty(PROPERTY_MERGE_MANIFEST, mMustMergeManifest);
saveProjectBooleanProperty(PROPERTY_COMPILE_RESOURCES, mMustCompileResources);
saveProjectBooleanProperty(PROPERTY_COMPILE_BUILDCONFIG, mMustCreateBuildConfig);
}
try {
handleBuildConfig(args);
} catch (IOException e) {
handleException(e, "Failed to create BuildConfig class");
return result;
}
// merge the manifest
if (mMustMergeManifest) {
boolean enabled = MANIFEST_MERGER_ENABLED_DEFAULT;
String propValue = projectState.getProperty(MANIFEST_MERGER_PROPERTY);
if (propValue != null) {
enabled = Boolean.valueOf(propValue);
}
if (mergeManifest(androidOutputFolder, libProjects, enabled) == false) {
return result;
}
}
List<File> libProjectsOut = new ArrayList<File>(libProjects.size());
for (IProject libProject : libProjects) {
libProjectsOut.add(
BaseProjectHelper.getAndroidOutputFolder(libProject)
.getLocation().toFile());
}
// run the source processors
int processorStatus = SourceProcessor.COMPILE_STATUS_NONE;
try {
processorStatus |= mAidlProcessor.compileFiles(this,
project, projectTarget, sourceFolderPathList,
libProjectsOut, monitor);
} catch (Throwable t) {
handleException(t, "Failed to run aidl. Check workspace log for detail.");
return result;
}
try {
processorStatus |= compileRs(minSdkValue, projectState, androidOutputFolder,
resOutFolder, monitor);
} catch (Throwable t) {
handleException(t, "Failed to run renderscript. Check workspace log for detail.");
return result;
}
// if a processor created some resources file, force recompilation of the resources.
if ((processorStatus & SourceProcessor.COMPILE_STATUS_RES) != 0) {
mMustCompileResources = true;
// save the current state before attempting the compilation
saveProjectBooleanProperty(PROPERTY_COMPILE_RESOURCES, mMustCompileResources);
}
// handle the resources, after the processors are run since some (renderscript)
// generate resources.
boolean compiledTheResources = mMustCompileResources;
if (mMustCompileResources) {
if (DEBUG_LOG) {
AdtPlugin.log(IStatus.INFO, "%s compiling resources!", project.getName());
}
IFile proguardFile = null;
if (projectState.getProperty(ProjectProperties.PROPERTY_PROGUARD_CONFIG) != null) {
proguardFile = androidOutputFolder.getFile(AdtConstants.FN_AAPT_PROGUARD);
}
handleResources(project, javaPackage, projectTarget, manifestFile, resOutFolder,
libProjects, isLibrary, proguardFile);
}
if (processorStatus == SourceProcessor.COMPILE_STATUS_NONE &&
compiledTheResources == false) {
AdtPlugin.printBuildToConsole(BuildVerbosity.VERBOSE, project,
Messages.Nothing_To_Compile);
}
} catch (AbortBuildException e) {
return result;
} finally {
// refresh the 'gen' source folder. Once this is done with the custom progress
// monitor to mark all new files as derived
mGenFolder.refreshLocal(IResource.DEPTH_INFINITE, mDerivedProgressMonitor);
if (resOutFolder != null) {
resOutFolder.refreshLocal(IResource.DEPTH_INFINITE, mDerivedProgressMonitor);
}
}
return result;
}
private IFolder getResOutFolder(IFolder androidOutputFolder) {
return androidOutputFolder.getFolder(AdtConstants.WS_BIN_RELATIVE_BC);
}
@Override
protected void clean(IProgressMonitor monitor) throws CoreException {
super.clean(monitor);
if (DEBUG_LOG) {
AdtPlugin.log(IStatus.INFO, "%s CLEAN(PRE)", getProject().getName());
}
doClean(getProject(), monitor);
if (mGenFolder != null) {
mGenFolder.refreshLocal(IResource.DEPTH_INFINITE, monitor);
}
}
private void doClean(IProject project, IProgressMonitor monitor) throws CoreException {
AdtPlugin.printBuildToConsole(BuildVerbosity.VERBOSE, project,
Messages.Removing_Generated_Classes);
// remove all the derived resources from the 'gen' source folder.
if (mGenFolder != null && mGenFolder.exists()) {
// gen folder should not be derived, but previous version could set it to derived
// so we make sure this isn't the case (or it'll get deleted by the clean)
mGenFolder.setDerived(false, monitor);
removeDerivedResources(mGenFolder, monitor);
}
// Clear the project of the generic markers
removeMarkersFromContainer(project, AdtConstants.MARKER_AAPT_COMPILE);
removeMarkersFromContainer(project, AdtConstants.MARKER_XML);
removeMarkersFromContainer(project, AdtConstants.MARKER_AIDL);
removeMarkersFromContainer(project, AdtConstants.MARKER_RENDERSCRIPT);
removeMarkersFromContainer(project, AdtConstants.MARKER_MANIFMERGER);
removeMarkersFromContainer(project, AdtConstants.MARKER_ANDROID);
// Also clean up lint
EclipseLintClient.clearMarkers(project);
// clean the project repo
ProjectResources res = ResourceManager.getInstance().getProjectResources(project);
res.clear();
}
@Override
protected void startupOnInitialize() {
try {
super.startupOnInitialize();
IProject project = getProject();
// load the previous IFolder and java package.
mManifestPackage = loadProjectStringProperty(PROPERTY_PACKAGE);
// get the source folder in which all the Java files are created
mGenFolder = project.getFolder(SdkConstants.FD_GEN_SOURCES);
mDerivedProgressMonitor = new DerivedProgressMonitor(mGenFolder);
// Load the current compile flags. We ask for true if not found to force a recompile.
mMustMergeManifest = loadProjectBooleanProperty(PROPERTY_MERGE_MANIFEST, true);
mMustCompileResources = loadProjectBooleanProperty(PROPERTY_COMPILE_RESOURCES, true);
mMustCreateBuildConfig = loadProjectBooleanProperty(PROPERTY_COMPILE_BUILDCONFIG, true);
Boolean v = ProjectHelper.loadBooleanProperty(project, PROPERTY_BUILDCONFIG_MODE);
if (v == null) {
// no previous build config mode? force regenerate
mMustCreateBuildConfig = true;
} else {
mLastBuildConfigMode = v;
}
} catch (Throwable throwable) {
AdtPlugin.log(throwable, "Failed to finish PrecompilerBuilder#startupOnInitialize()");
}
}
private void setupSourceProcessors(@NonNull IJavaProject javaProject,
@NonNull ProjectState projectState,
@NonNull List<IPath> sourceFolderPathList,
@NonNull IFolder androidOutputFolder) {
if (mAidlProcessor == null) {
mAidlProcessor = new AidlProcessor(javaProject, mBuildToolInfo, mGenFolder);
} else {
mAidlProcessor.setBuildToolInfo(mBuildToolInfo);
}
List<File> sourceFolders = Lists.newArrayListWithCapacity(sourceFolderPathList.size());
IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
for (IPath path : sourceFolderPathList) {
IResource resource = root.findMember(path);
if (resource != null && resource.exists() && resource.getType() == IResource.FOLDER) {
IPath fullPath = resource.getLocation();
if (fullPath != null) {
sourceFolders.add(fullPath.toFile());
}
}
}
RenderScriptChecker checker = new RenderScriptChecker(sourceFolders,
androidOutputFolder.getLocation().toFile());
mRenderScriptSourceChangeHandler = new RsSourceChangeHandler(checker);
}
private int compileRs(int minSdkValue,
@NonNull ProjectState projectState,
@NonNull IFolder androidOutputFolder,
@NonNull IFolder resOutFolder,
@NonNull IProgressMonitor monitor)
throws IOException, InterruptedException {
if (!mRenderScriptSourceChangeHandler.mustCompile()) {
return SourceProcessor.COMPILE_STATUS_NONE;
}
RenderScriptChecker checker = mRenderScriptSourceChangeHandler.getChecker();
List<File> inputs = checker.findInputFiles();
List<File> importFolders = checker.getSourceFolders();
File buildFolder = androidOutputFolder.getLocation().toFile();
// get the renderscript target
int rsTarget = minSdkValue == -1 ? 11 : minSdkValue;
String rsTargetStr = projectState.getProperty(ProjectProperties.PROPERTY_RS_TARGET);
if (rsTargetStr != null) {
try {
rsTarget = Integer.parseInt(rsTargetStr);
} catch (NumberFormatException e) {
handleException(e, String.format(
"Property %s is not an integer.",
ProjectProperties.PROPERTY_RS_TARGET));
return SourceProcessor.COMPILE_STATUS_NONE;
}
}
RenderScriptProcessor processor = new RenderScriptProcessor(
inputs,
importFolders,
buildFolder,
mGenFolder.getLocation().toFile(),
resOutFolder.getLocation().toFile(),
new File(buildFolder, SdkConstants.FD_RS_OBJ),
new File(buildFolder, SdkConstants.FD_RS_LIBS),
mBuildToolInfo,
rsTarget,
false /*debugBuild, always false for now*/,
3,
projectState.getRenderScriptSupportMode());
// clean old dependency files fiest
checker.cleanDependencies();
// then clean old output files
processor.cleanOldOutput(checker.getOldOutputs());
RenderScriptLauncher launcher = new RenderScriptLauncher(
getProject(),
mGenFolder,
resOutFolder,
monitor,
AdtPrefs.getPrefs().getBuildVerbosity() == BuildVerbosity.VERBOSE /*verbose*/);
// and run the build
processor.build(launcher);
return SourceProcessor.COMPILE_STATUS_CODE | SourceProcessor.COMPILE_STATUS_RES;
}
@SuppressWarnings("deprecation")
private void handleBuildConfig(@SuppressWarnings("rawtypes") Map args)
throws IOException, CoreException {
boolean debugMode = !args.containsKey(RELEASE_REQUESTED);
BuildConfigGenerator generator = new BuildConfigGenerator(
mGenFolder.getLocation().toOSString(), mManifestPackage, debugMode);
if (mMustCreateBuildConfig == false) {
// check the file is present.
IFolder folder = getGenManifestPackageFolder();
if (folder.exists(new Path(BuildConfigGenerator.BUILD_CONFIG_NAME)) == false) {
mMustCreateBuildConfig = true;
AdtPlugin.printBuildToConsole(BuildVerbosity.VERBOSE, getProject(),
String.format("Class %1$s is missing!",
BuildConfigGenerator.BUILD_CONFIG_NAME));
} else if (debugMode != mLastBuildConfigMode) {
// else if the build mode changed, force creation
mMustCreateBuildConfig = true;
AdtPlugin.printBuildToConsole(BuildVerbosity.VERBOSE, getProject(),
String.format("Different build mode, must update %1$s!",
BuildConfigGenerator.BUILD_CONFIG_NAME));
}
}
if (mMustCreateBuildConfig) {
if (DEBUG_LOG) {
AdtPlugin.log(IStatus.INFO, "%s generating BuilderConfig!", getProject().getName());
}
AdtPlugin.printBuildToConsole(BuildVerbosity.VERBOSE, getProject(),
String.format("Generating %1$s...", BuildConfigGenerator.BUILD_CONFIG_NAME));
generator.generate();
mMustCreateBuildConfig = false;
saveProjectBooleanProperty(PROPERTY_COMPILE_BUILDCONFIG, mMustCreateBuildConfig);
saveProjectBooleanProperty(PROPERTY_BUILDCONFIG_MODE, mLastBuildConfigMode = debugMode);
}
}
private boolean mergeManifest(IFolder androidOutFolder, List<IProject> libProjects,
boolean enabled) throws CoreException {
if (DEBUG_LOG) {
AdtPlugin.log(IStatus.INFO, "%s merging manifests!", getProject().getName());
}
IFile outFile = androidOutFolder.getFile(SdkConstants.FN_ANDROID_MANIFEST_XML);
IFile manifest = getProject().getFile(SdkConstants.FN_ANDROID_MANIFEST_XML);
// remove existing markers from the manifest.
// FIXME: only remove from manifest once the markers are put there.
removeMarkersFromResource(getProject(), AdtConstants.MARKER_MANIFMERGER);
// If the merging is not enabled or if there's no library then we simply copy the
// manifest over.
if (enabled == false || libProjects.size() == 0) {
try {
new FileOp().copyFile(manifest.getLocation().toFile(),
outFile.getLocation().toFile());
outFile.refreshLocal(IResource.DEPTH_INFINITE, mDerivedProgressMonitor);
saveProjectBooleanProperty(PROPERTY_MERGE_MANIFEST, mMustMergeManifest = false);
} catch (IOException e) {
handleException(e, "Failed to copy Manifest");
return false;
}
} else {
final ArrayList<String> errors = new ArrayList<String>();
// TODO change MergerLog.wrapSdkLog by a custom IMergerLog that will create
// and maintain error markers.
ManifestMerger merger = new ManifestMerger(
MergerLog.wrapSdkLog(new ILogger() {
@Override
public void warning(@NonNull String warningFormat, Object... args) {
AdtPlugin.printToConsole(getProject(), String.format(warningFormat, args));
}
@Override
public void info(@NonNull String msgFormat, Object... args) {
AdtPlugin.printToConsole(getProject(), String.format(msgFormat, args));
}
@Override
public void verbose(@NonNull String msgFormat, Object... args) {
info(msgFormat, args);
}
@Override
public void error(@Nullable Throwable t, @Nullable String errorFormat,
Object... args) {
errors.add(String.format(errorFormat, args));
}
}),
new AdtManifestMergeCallback());
File[] libManifests = new File[libProjects.size()];
int libIndex = 0;
for (IProject lib : libProjects) {
libManifests[libIndex++] = lib.getFile(SdkConstants.FN_ANDROID_MANIFEST_XML)
.getLocation().toFile();
}
if (merger.process(
outFile.getLocation().toFile(),
manifest.getLocation().toFile(),
libManifests,
null /*injectAttributes*/, null /*packageOverride*/) == false) {
if (errors.size() > 1) {
StringBuilder sb = new StringBuilder();
for (String s : errors) {
sb.append(s).append('\n');
}
markProject(AdtConstants.MARKER_MANIFMERGER, sb.toString(),
IMarker.SEVERITY_ERROR);
} else if (errors.size() == 1) {
markProject(AdtConstants.MARKER_MANIFMERGER, errors.get(0),
IMarker.SEVERITY_ERROR);
} else {
markProject(AdtConstants.MARKER_MANIFMERGER, "Unknown error merging manifest",
IMarker.SEVERITY_ERROR);
}
return false;
}
outFile.refreshLocal(IResource.DEPTH_INFINITE, mDerivedProgressMonitor);
saveProjectBooleanProperty(PROPERTY_MERGE_MANIFEST, mMustMergeManifest = false);
}
return true;
}
/**
* Handles resource changes and regenerate whatever files need regenerating.
* @param project the main project
* @param javaPackage the app package for the main project
* @param projectTarget the target of the main project
* @param manifest the {@link IFile} representing the project manifest
* @param libProjects the library dependencies
* @param isLibrary if the project is a library project
* @throws CoreException
* @throws AbortBuildException
*/
private void handleResources(IProject project, String javaPackage, IAndroidTarget projectTarget,
IFile manifest, IFolder resOutFolder, List<IProject> libProjects, boolean isLibrary,
IFile proguardFile) throws CoreException, AbortBuildException {
// get the resource folder
IFolder resFolder = project.getFolder(AdtConstants.WS_RESOURCES);
// get the file system path
IPath outputLocation = mGenFolder.getLocation();
IPath resLocation = resFolder.getLocation();
IPath manifestLocation = manifest == null ? null : manifest.getLocation();
// those locations have to exist for us to do something!
if (outputLocation != null && resLocation != null
&& manifestLocation != null) {
String osOutputPath = outputLocation.toOSString();
String osResPath = resLocation.toOSString();
String osManifestPath = manifestLocation.toOSString();
// remove the aapt markers
removeMarkersFromResource(manifest, AdtConstants.MARKER_AAPT_COMPILE);
removeMarkersFromContainer(resFolder, AdtConstants.MARKER_AAPT_COMPILE);
AdtPlugin.printBuildToConsole(BuildVerbosity.VERBOSE, project,
Messages.Preparing_Generated_Files);
// we need to figure out where to store the R class.
// get the parent folder for R.java and update mManifestPackageSourceFolder
IFolder mainPackageFolder = getGenManifestPackageFolder();
// handle libraries
ArrayList<IFolder> libResFolders = Lists.newArrayList();
ArrayList<Pair<File, String>> libRFiles = Lists.newArrayList();
if (libProjects != null) {
for (IProject lib : libProjects) {
IFolder libResFolder = lib.getFolder(SdkConstants.FD_RES);
if (libResFolder.exists()) {
libResFolders.add(libResFolder);
}
try {
// get the package of the library, and if it's different form the
// main project, generate the R class for it too.
String libJavaPackage = AndroidManifest.getPackage(new IFolderWrapper(lib));
if (libJavaPackage.equals(javaPackage) == false) {
IFolder libOutput = BaseProjectHelper.getAndroidOutputFolder(lib);
File libOutputFolder = libOutput.getLocation().toFile();
libRFiles.add(Pair.of(
new File(libOutputFolder, "R.txt"),
libJavaPackage));
}
} catch (Exception e) {
}
}
}
String proguardFilePath = proguardFile != null ?
proguardFile.getLocation().toOSString(): null;
File resOutFile = resOutFolder.getLocation().toFile();
String resOutPath = resOutFile.isDirectory() ? resOutFile.getAbsolutePath() : null;
execAapt(project, projectTarget, osOutputPath, resOutPath, osResPath, osManifestPath,
mainPackageFolder, libResFolders, libRFiles, isLibrary, proguardFilePath);
}
}
/**
* Executes AAPT to generate R.java/Manifest.java
* @param project the main project
* @param projectTarget the main project target
* @param osOutputPath the OS output path for the generated file. This is the source folder, not
* the package folder.
* @param osResPath the OS path to the res folder for the main project
* @param osManifestPath the OS path to the manifest of the main project
* @param packageFolder the IFolder that will contain the generated file. Unlike
* <var>osOutputPath</var> this is the direct parent of the generated files.
* If <var>customJavaPackage</var> is not null, this must match the new destination triggered
* by its value.
* @param libResFolders the list of res folders for the library.
* @param libRFiles a list of R files for the libraries.
* @param isLibrary if the project is a library project
* @param proguardFile an optional path to store proguard information
* @throws AbortBuildException
*/
@SuppressWarnings("deprecation")
private void execAapt(IProject project, IAndroidTarget projectTarget, String osOutputPath,
String osBcOutPath, String osResPath, String osManifestPath, IFolder packageFolder,
ArrayList<IFolder> libResFolders, List<Pair<File, String>> libRFiles,
boolean isLibrary, String proguardFile)
throws AbortBuildException {
// We actually need to delete the manifest.java as it may become empty and
// in this case aapt doesn't generate an empty one, but instead doesn't
// touch it.
IFile manifestJavaFile = packageFolder.getFile(SdkConstants.FN_MANIFEST_CLASS);
manifestJavaFile.getLocation().toFile().delete();
// launch aapt: create the command line
ArrayList<String> array = new ArrayList<String>();
String aaptPath = mBuildToolInfo.getPath(BuildToolInfo.PathId.AAPT);
array.add(aaptPath);
array.add("package"); //$NON-NLS-1$
array.add("-m"); //$NON-NLS-1$
if (AdtPrefs.getPrefs().getBuildVerbosity() == BuildVerbosity.VERBOSE) {
array.add("-v"); //$NON-NLS-1$
}
if (isLibrary) {
array.add("--non-constant-id"); //$NON-NLS-1$
}
if (libResFolders.size() > 0) {
array.add("--auto-add-overlay"); //$NON-NLS-1$
}
// If a library or has libraries, generate a text version of the R symbols.
File outputFolder = BaseProjectHelper.getAndroidOutputFolder(project).getLocation()
.toFile();
if (isLibrary || !libRFiles.isEmpty()) {
array.add("--output-text-symbols"); //$NON-NLS-1$
array.add(outputFolder.getAbsolutePath());
}
array.add("-J"); //$NON-NLS-1$
array.add(osOutputPath);
array.add("-M"); //$NON-NLS-1$
array.add(osManifestPath);
if (osBcOutPath != null) {
array.add("-S"); //$NON-NLS-1$
array.add(osBcOutPath);
}
array.add("-S"); //$NON-NLS-1$
array.add(osResPath);
for (IFolder libResFolder : libResFolders) {
array.add("-S"); //$NON-NLS-1$
array.add(libResFolder.getLocation().toOSString());
}
array.add("-I"); //$NON-NLS-1$
array.add(projectTarget.getPath(IAndroidTarget.ANDROID_JAR));
// use the proguard file
if (proguardFile != null && proguardFile.length() > 0) {
array.add("-G");
array.add(proguardFile);
}
if (AdtPrefs.getPrefs().getBuildVerbosity() == BuildVerbosity.VERBOSE) {
StringBuilder sb = new StringBuilder();
for (String c : array) {
sb.append(c);
sb.append(' ');
}
String cmd_line = sb.toString();
AdtPlugin.printToConsole(project, cmd_line);
}
// launch
try {
// launch the command line process
Process process = Runtime.getRuntime().exec(
array.toArray(new String[array.size()]));
// list to store each line of stderr
ArrayList<String> stdErr = new ArrayList<String>();
// get the output and return code from the process
int returnCode = grabProcessOutput(process, stdErr);
// attempt to parse the error output
boolean parsingError = AaptParser.parseOutput(stdErr, project);
// if we couldn't parse the output we display it in the console.
if (parsingError) {
if (returnCode != 0) {
AdtPlugin.printErrorToConsole(project, stdErr.toArray());
} else {
AdtPlugin.printBuildToConsole(BuildVerbosity.NORMAL,
project, stdErr.toArray());
}
}
if (returnCode != 0) {
// if the exec failed, and we couldn't parse the error output
// (and therefore not all files that should have been marked,
// were marked), we put a generic marker on the project and abort.
if (parsingError) {
markProject(AdtConstants.MARKER_ADT,
Messages.Unparsed_AAPT_Errors, IMarker.SEVERITY_ERROR);
} else if (stdErr.size() == 0) {
// no parsing error because sdterr was empty. We still need to put
// a marker otherwise there's no user visible feedback.
markProject(AdtConstants.MARKER_ADT,
String.format(Messages.AAPT_Exec_Error_d, returnCode),
IMarker.SEVERITY_ERROR);
}
AdtPlugin.printBuildToConsole(BuildVerbosity.VERBOSE, project,
Messages.AAPT_Error);
// abort if exec failed.
throw new AbortBuildException();
}
// now if the project has libraries, R needs to be created for each libraries
// unless this is a library.
if (isLibrary == false && !libRFiles.isEmpty()) {
File rFile = new File(outputFolder, SdkConstants.FN_RESOURCE_TEXT);
// if the project has no resources, the file could not exist.
if (rFile.isFile()) {
// Load the full symbols from the full R.txt file.
SymbolLoader fullSymbolValues = new SymbolLoader(rFile);
fullSymbolValues.load();
Multimap<String, SymbolLoader> libMap = ArrayListMultimap.create();
// First pass processing the libraries, collecting them by packageName,
// and ignoring the ones that have the same package name as the application
// (since that R class was already created).
for (Pair<File, String> lib : libRFiles) {
String libPackage = lib.getSecond();
File rText = lib.getFirst();
if (rText.isFile()) {
// load the lib symbols
SymbolLoader libSymbols = new SymbolLoader(rText);
libSymbols.load();
// store these symbols by associating them with the package name.
libMap.put(libPackage, libSymbols);
}
}
// now loop on all the package names, merge all the symbols to write,
// and write them
for (String packageName : libMap.keySet()) {
Collection<SymbolLoader> symbols = libMap.get(packageName);
SymbolWriter writer = new SymbolWriter(osOutputPath, packageName,
fullSymbolValues);
for (SymbolLoader symbolLoader : symbols) {
writer.addSymbolsToWrite(symbolLoader);
}
writer.write();
}
}
}
} catch (IOException e1) {
// something happen while executing the process,
// mark the project and exit
String msg;
String path = array.get(0);
if (!new File(path).exists()) {
msg = String.format(Messages.AAPT_Exec_Error_s, path);
} else {
String description = e1.getLocalizedMessage();
if (e1.getCause() != null && e1.getCause() != e1) {
description = description + ": " + e1.getCause().getLocalizedMessage();
}
msg = String.format(Messages.AAPT_Exec_Error_Other_s, description);
}
markProject(AdtConstants.MARKER_ADT, msg, IMarker.SEVERITY_ERROR);
// Add workaround for the Linux problem described here:
// http://developer.android.com/sdk/installing.html#troubleshooting
// There are various posts on StackOverflow elsewhere where people are asking
// about aapt failing to run, so even though this is documented in the
// Troubleshooting section add an error message to help with this
// scenario.
if (SdkConstants.CURRENT_PLATFORM == SdkConstants.PLATFORM_LINUX
&& System.getProperty("os.arch").endsWith("64") //$NON-NLS-1$ //$NON-NLS-2$
&& new File(aaptPath).exists()
&& new File("/usr/bin/apt-get").exists()) { //$NON-NLS-1$
markProject(AdtConstants.MARKER_ADT,
"Hint: On 64-bit systems, make sure the 32-bit libraries are installed: \"sudo apt-get install ia32-libs\" or on some systems, \"sudo apt-get install lib32z1\"",
IMarker.SEVERITY_ERROR);
// Note - this uses SEVERITY_ERROR even though it's really SEVERITY_INFO because
// we want this error message to show up adjacent to the aapt error message
// (and Eclipse sorts by priority)
}
// This interrupts the build.
throw new AbortBuildException();
} catch (InterruptedException e) {
// we got interrupted waiting for the process to end...
// mark the project and exit
String msg = String.format(Messages.AAPT_Exec_Error_s, array.get(0));
markProject(AdtConstants.MARKER_ADT, msg, IMarker.SEVERITY_ERROR);
// This interrupts the build.
throw new AbortBuildException();
} finally {
// we've at least attempted to run aapt, save the fact that we don't have to
// run it again, unless there's a new resource change.
saveProjectBooleanProperty(PROPERTY_COMPILE_RESOURCES,
mMustCompileResources = false);
ResourceManager.clearAaptRequest(project);
}
}
/**
* Creates a relative {@link IPath} from a java package.
* @param javaPackageName the java package.
*/
private IPath getJavaPackagePath(String javaPackageName) {
// convert the java package into path
String[] segments = javaPackageName.split(AdtConstants.RE_DOT);
StringBuilder path = new StringBuilder();
for (String s : segments) {
path.append(AdtConstants.WS_SEP_CHAR);
path.append(s);
}
return new Path(path.toString());
}
/**
* Returns an {@link IFolder} (located inside the 'gen' source folder), that matches the
* package defined in the manifest. This {@link IFolder} may not actually exist
* (aapt will create it anyway).
* @return the {@link IFolder} that will contain the R class or null if
* the folder was not found.
* @throws CoreException
*/
private IFolder getGenManifestPackageFolder() throws CoreException {
// get the path for the package
IPath packagePath = getJavaPackagePath(mManifestPackage);
// get a folder for this path under the 'gen' source folder, and return it.
// This IFolder may not reference an actual existing folder.
return mGenFolder.getFolder(packagePath);
}
}