/*
* 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.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.AndroidConstants;
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.SourceProcessor;
import com.android.ide.eclipse.adt.internal.build.Messages;
import com.android.ide.eclipse.adt.internal.build.RenderScriptProcessor;
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.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.sdklib.AndroidVersion;
import com.android.sdklib.IAndroidTarget;
import com.android.sdklib.SdkConstants;
import com.android.sdklib.xml.AndroidManifest;
import com.android.sdklib.xml.ManifestData;
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.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* 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$
private static final String PROPERTY_PACKAGE = "manifestPackage"; //$NON-NLS-1$
private static final String PROPERTY_COMPILE_RESOURCES = "compileResources"; //$NON-NLS-1$
/**
* Resource Compile flag. This flag is reset to false after each successful compilation, and
* stored in the project persistent properties. This allows the builder to remember its state
* when the project is closed/opened.
*/
private boolean mMustCompileResources = false;
private final List<SourceProcessor> mProcessors = new ArrayList<SourceProcessor>();
/** 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;
/**
* 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;
}
public void beginTask(String name, int totalWork) {
}
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);
} 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.
}
}
}
}
public void internalWorked(double work) {
}
public boolean isCanceled() {
return mCancelled;
}
public void setCanceled(boolean value) {
mCancelled = value;
}
public void setTaskName(String name) {
}
public void subTask(String name) {
}
public void worked(int work) {
}
}
public PreCompilerBuilder() {
super();
}
// 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 a project object
IProject project = getProject();
// 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;
try {
mDerivedProgressMonitor.reset();
// get the project info
ProjectState projectState = Sdk.getProjectState(project);
// this can happen if the project has no default.properties.
if (projectState == null) {
return null;
}
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);
// now we need to get the classpath list
List<IPath> sourceFolderPathList = BaseProjectHelper.getSourceClasspaths(javaProject);
PreCompilerDeltaVisitor dv = null;
String javaPackage = null;
String minSdkVersion = null;
if (kind == FULL_BUILD) {
AdtPlugin.printBuildToConsole(BuildVerbosity.VERBOSE, project,
Messages.Start_Full_Pre_Compiler);
// do some clean up.
doClean(project, monitor);
mMustCompileResources = true;
for (SourceProcessor processor : mProcessors) {
processor.prepareFullBuild(project);
}
} 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;
for (SourceProcessor processor : mProcessors) {
processor.prepareFullBuild(project);
}
} else {
dv = new PreCompilerDeltaVisitor(this, sourceFolderPathList, mProcessors);
delta.accept(dv);
// record the state
mMustCompileResources |= dv.getCompileResources();
for (SourceProcessor processor : mProcessors) {
processor.doneVisiting(project);
}
// get the java package from the visitor
javaPackage = dv.getManifestPackage();
minSdkVersion = dv.getMinSdkVersion();
// if the main resources didn't change, then we check for the library
// ones (will trigger resource recompilation too)
if (mMustCompileResources == false && libProjects.size() > 0) {
for (IProject libProject : libProjects) {
delta = getDelta(libProject);
if (delta != null) {
LibraryDeltaVisitor visitor = new LibraryDeltaVisitor();
delta.accept(visitor);
mMustCompileResources = visitor.getResChange();
if (mMustCompileResources) {
break;
}
}
}
}
}
}
// store the build status in the persistent storage
saveProjectBooleanProperty(PROPERTY_COMPILE_RESOURCES , mMustCompileResources);
// 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;
}
// 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(AndroidConstants.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();
ManifestData parser = AndroidManifestHelper.parse(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);
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();
}
}
if (minSdkVersion != null) {
int minSdkValue = -1;
try {
minSdkValue = Integer.parseInt(minSdkVersion);
} catch (NumberFormatException e) {
// it's ok, it means minSdkVersion contains a (hopefully) valid codename.
}
AndroidVersion projectVersion = projectTarget.getVersion();
// remove earlier marker from the manifest
removeMarkersFromResource(manifestFile, AndroidConstants.MARKER_ADT);
if (minSdkValue != -1) {
String codename = projectVersion.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, AndroidConstants.MARKER_ADT,
msg, IMarker.SEVERITY_ERROR);
return result;
} else if (minSdkValue < projectVersion.getApiLevel()) {
// integer minSdk is not high enough for the target => warning
String msg = String.format(
"Attribute %1$s (%2$d) is lower than the project target API level (%3$d)",
AndroidManifest.ATTRIBUTE_MIN_SDK_VERSION,
minSdkValue, projectVersion.getApiLevel());
AdtPlugin.printBuildToConsole(BuildVerbosity.VERBOSE, project, msg);
BaseProjectHelper.markResource(manifestFile, AndroidConstants.MARKER_ADT,
msg, IMarker.SEVERITY_WARNING);
} else if (minSdkValue > projectVersion.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, projectVersion.getApiLevel());
AdtPlugin.printBuildToConsole(BuildVerbosity.VERBOSE, project, msg);
BaseProjectHelper.markResource(manifestFile, AndroidConstants.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 = projectVersion.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, AndroidConstants.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, AndroidConstants.MARKER_ADT,
msg, IMarker.SEVERITY_ERROR);
return result;
}
}
} 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, AndroidConstants.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, AndroidConstants.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, AndroidConstants.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);
mMustCompileResources = true;
for (SourceProcessor processor : mProcessors) {
processor.prepareFullBuild(project);
}
saveProjectBooleanProperty(PROPERTY_COMPILE_RESOURCES , mMustCompileResources);
}
// run the source processors
int processorStatus = SourceProcessor.COMPILE_STATUS_NONE;
for (SourceProcessor processor : mProcessors) {
try {
processorStatus |= processor.compileFiles(this,
project, projectTarget, sourceFolderPathList, monitor);
} catch (Throwable t) {
AdtPlugin.log(t, "Failed to run one of the source processor");
}
}
// 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) {
handleResources(project, javaPackage, projectTarget, manifestFile, libProjects);
saveProjectBooleanProperty(PROPERTY_COMPILE_RESOURCES , false);
}
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);
}
return result;
}
@Override
protected void clean(IProgressMonitor monitor) throws CoreException {
super.clean(monitor);
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) {
removeDerivedResources(mGenFolder, monitor);
}
// Clear the project of the generic markers
removeMarkersFromContainer(project, AndroidConstants.MARKER_AAPT_COMPILE);
removeMarkersFromContainer(project, AndroidConstants.MARKER_XML);
removeMarkersFromContainer(project, AndroidConstants.MARKER_AIDL);
removeMarkersFromContainer(project, AndroidConstants.MARKER_RENDERSCRIPT);
removeMarkersFromContainer(project, AndroidConstants.MARKER_ANDROID);
}
@Override
protected void startupOnInitialize() {
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);
// Load the current compile flags. We ask for true if not found to force a recompile.
mMustCompileResources = loadProjectBooleanProperty(PROPERTY_COMPILE_RESOURCES, true);
IJavaProject javaProject = JavaCore.create(project);
// load the source processors
SourceProcessor aidlProcessor = new AidlProcessor(javaProject, mGenFolder);
mProcessors.add(aidlProcessor);
SourceProcessor renderScriptProcessor = new RenderScriptProcessor(javaProject, mGenFolder);
mProcessors.add(renderScriptProcessor);
mDerivedProgressMonitor = new DerivedProgressMonitor(mGenFolder);
}
/**
* 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
* @throws CoreException
* @throws AbortBuildException
*/
private void handleResources(IProject project, String javaPackage, IAndroidTarget projectTarget,
IFile manifest, List<IProject> libProjects) throws CoreException, AbortBuildException {
// get the resource folder
IFolder resFolder = project.getFolder(AndroidConstants.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, AndroidConstants.MARKER_AAPT_COMPILE);
removeMarkersFromContainer(resFolder, AndroidConstants.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 = new ArrayList<IFolder>();
ArrayList<IFolder> libOutputFolders = new ArrayList<IFolder>();
ArrayList<String> libJavaPackages = new ArrayList<String>();
if (libProjects != null) {
for (IProject lib : libProjects) {
IFolder libResFolder = lib.getFolder(SdkConstants.FD_RES);
if (libResFolder.exists()) {
libResFolders.add(libResFolder);
}
try {
String libJavaPackage = AndroidManifest.getPackage(new IFolderWrapper(lib));
if (libJavaPackage.equals(javaPackage) == false) {
libJavaPackages.add(libJavaPackage);
libOutputFolders.add(getGenManifestPackageFolder(libJavaPackage));
}
} catch (Exception e) {
}
}
}
execAapt(project, projectTarget, osOutputPath, osResPath, osManifestPath,
mainPackageFolder, libResFolders, null /* custom java package */);
final int count = libOutputFolders.size();
if (count > 0) {
for (int i = 0 ; i < count ; i++) {
IFolder libFolder = libOutputFolders.get(i);
String libJavaPackage = libJavaPackages.get(i);
execAapt(project, projectTarget, osOutputPath, osResPath, osManifestPath,
libFolder, libResFolders, libJavaPackage);
}
}
}
}
/**
* 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 customJavaPackage an optional javapackage to replace the main project java package.
* can be null.
* @throws AbortBuildException
*/
private void execAapt(IProject project, IAndroidTarget projectTarget, String osOutputPath,
String osResPath, String osManifestPath, IFolder packageFolder,
ArrayList<IFolder> libResFolders, String customJavaPackage) 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(AndroidConstants.FN_MANIFEST_CLASS);
manifestJavaFile.getLocation().toFile().delete();
// launch aapt: create the command line
ArrayList<String> array = new ArrayList<String>();
array.add(projectTarget.getPath(IAndroidTarget.AAPT));
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 (libResFolders.size() > 0) {
array.add("--auto-add-overlay"); //$NON-NLS-1$
}
if (customJavaPackage != null) {
array.add("--custom-package"); //$NON-NLS-1$
array.add(customJavaPackage);
}
array.add("-J"); //$NON-NLS-1$
array.add(osOutputPath);
array.add("-M"); //$NON-NLS-1$
array.add(osManifestPath);
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));
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
int execError = 1;
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> results = new ArrayList<String>();
// get the output and return code from the process
execError = grabProcessOutput(process, results);
// attempt to parse the error output
boolean parsingError = AaptParser.parseOutput(results, project);
// if we couldn't parse the output we display it in the console.
if (parsingError) {
if (execError != 0) {
AdtPlugin.printErrorToConsole(project, results.toArray());
} else {
AdtPlugin.printBuildToConsole(BuildVerbosity.NORMAL,
project, results.toArray());
}
}
if (execError != 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(AndroidConstants.MARKER_ADT,
Messages.Unparsed_AAPT_Errors, IMarker.SEVERITY_ERROR);
}
AdtPlugin.printBuildToConsole(BuildVerbosity.VERBOSE, project,
Messages.AAPT_Error);
// abort if exec failed.
throw new AbortBuildException();
}
} catch (IOException e1) {
// something happen while executing the process,
// mark the project and exit
String msg = String.format(Messages.AAPT_Exec_Error, array.get(0));
markProject(AndroidConstants.MARKER_ADT, msg, IMarker.SEVERITY_ERROR);
// 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, array.get(0));
markProject(AndroidConstants.MARKER_ADT, msg, IMarker.SEVERITY_ERROR);
// This interrupts the build.
throw new AbortBuildException();
}
// if the return code was OK, we refresh the folder that
// contains R.java to force a java recompile.
if (execError == 0) {
// build has been done. reset the state of the builder
mMustCompileResources = false;
// and store it
saveProjectBooleanProperty(PROPERTY_COMPILE_RESOURCES,
mMustCompileResources);
}
}
/**
* 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(AndroidConstants.RE_DOT);
StringBuilder path = new StringBuilder();
for (String s : segments) {
path.append(AndroidConstants.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);
}
/**
* Returns an {@link IFolder} (located inside the 'gen' source folder), that matches the
* given package. This {@link IFolder} may not actually exist
* (aapt will create it anyway).
* @param javaPackage the java package that must match the folder.
* @return the {@link IFolder} that will contain the R class or null if
* the folder was not found.
* @throws CoreException
*/
private IFolder getGenManifestPackageFolder(String javaPackage) throws CoreException {
// get the path for the package
IPath packagePath = getJavaPackagePath(javaPackage);
// 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);
}
}