diff options
20 files changed, 828 insertions, 841 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtConstants.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtConstants.java index 5330e08..0e3b783 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtConstants.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtConstants.java @@ -94,9 +94,19 @@ public class AdtConstants { /** Absolute path of the resource folder, e.g. "/res".<br> This is a workspace path. */ public final static String WS_RESOURCES = WS_SEP + SdkConstants.FD_RESOURCES; - /** Absolute path of the crunch cache folder, e.g. "/bin/res".<br> This is a workspace path. */ - public final static String WS_CRUNCHCACHE = WS_SEP + SdkConstants.FD_OUTPUT - + WS_SEP + SdkConstants.FD_RESOURCES; + public final static String FD_CRUNCH = "crunch"; //$NON-NLS-1$ + public final static String FD_BC = "bc"; //$NON-NLS-1$ + + /** Path of crunch cache folder relative to the output folder.<br> This is a workspace path. */ + public final static String WS_BIN_RELATIVE_CRUNCHCACHE = SdkConstants.FD_RESOURCES + + WS_SEP + FD_CRUNCH; + + /** Path of bc output folder relative to the output folder.<br> This is a workspace path. */ + public final static String WS_BIN_RELATIVE_BC = SdkConstants.FD_RESOURCES + + WS_SEP + FD_BC; + + /** Path of rs libs output folder relative to the output folder.<br> This is a workspace path. */ + public final static String WS_BIN_RELATIVE_RS_LIBS = SdkConstants.FD_RS_LIBS; /** Absolute path of the resource folder, e.g. "/assets".<br> This is a workspace path. */ public final static String WS_ASSETS = WS_SEP + SdkConstants.FD_ASSETS; diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java index c5d95af..cbeb274 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java @@ -51,6 +51,7 @@ import com.android.io.StreamException; import com.android.resources.ResourceFolderType; import com.android.sdklib.IAndroidTarget; import com.android.utils.ILogger; +import com.google.common.collect.Sets; import com.google.common.io.Closeables; import org.eclipse.core.commands.Command; @@ -123,6 +124,7 @@ import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Set; /** * The activator class controls the plug-in life cycle @@ -153,8 +155,7 @@ public class AdtPlugin extends AbstractUIPlugin implements ILogger { private LoadStatus mSdkLoadedStatus = LoadStatus.LOADING; /** Project to update once the SDK is loaded. * Any access MUST be in a synchronized(mPostLoadProjectsToResolve) block */ - private final ArrayList<IJavaProject> mPostLoadProjectsToResolve = - new ArrayList<IJavaProject>(); + private final Set<IJavaProject> mPostLoadProjectsToResolve = Sets.newHashSet(); /** Project to check validity of cache vs actual once the SDK is loaded. * Any access MUST be in a synchronized(mPostLoadProjectsToResolve) block */ private final ArrayList<IJavaProject> mPostLoadProjectsToCheck = new ArrayList<IJavaProject>(); diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/BuildHelper.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/BuildHelper.java index cda1da0..8e831b7 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/BuildHelper.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/BuildHelper.java @@ -25,6 +25,7 @@ import com.android.ide.eclipse.adt.AndroidPrintStream; 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.sdk.ProjectState; import com.android.ide.eclipse.adt.internal.sdk.Sdk; import com.android.prefs.AndroidLocation.AndroidLocationException; import com.android.sdklib.BuildToolInfo; @@ -35,6 +36,7 @@ import com.android.sdklib.build.ApkBuilder.JarStatus; import com.android.sdklib.build.ApkBuilder.SigningInfo; import com.android.sdklib.build.ApkCreationException; import com.android.sdklib.build.DuplicateFileException; +import com.android.sdklib.build.RenderScriptProcessor; import com.android.sdklib.build.SealedApkException; import com.android.sdklib.internal.build.DebugKeyProvider; import com.android.sdklib.internal.build.DebugKeyProvider.KeytoolException; @@ -107,6 +109,8 @@ public class BuildHelper { private static final String COMMAND_PACKAGE = "package"; //$NON-NLS-1$ @NonNull + private final ProjectState mProjectState; + @NonNull private final IProject mProject; @NonNull private final BuildToolInfo mBuildToolInfo; @@ -144,13 +148,14 @@ public class BuildHelper { * @param verbose * @throws CoreException */ - public BuildHelper(@NonNull IProject project, + public BuildHelper(@NonNull ProjectState projectState, @NonNull BuildToolInfo buildToolInfo, @NonNull AndroidPrintStream outStream, @NonNull AndroidPrintStream errStream, boolean forceJumbo, boolean disableDexMerger, boolean debugMode, boolean verbose, ResourceMarker resMarker) throws CoreException { - mProject = project; + mProjectState = projectState; + mProject = projectState.getProject(); mBuildToolInfo = buildToolInfo; mOutStream = outStream; mErrStream = errStream; @@ -177,7 +182,8 @@ public class BuildHelper { resPaths.add(resFolder.getLocation().toOSString()); // Get the output folder where the cache is stored. - IFolder cacheFolder = mProject.getFolder(AdtConstants.WS_CRUNCHCACHE); + IFolder binFolder = BaseProjectHelper.getAndroidOutputFolder(mProject); + IFolder cacheFolder = binFolder.getFolder(AdtConstants.WS_BIN_RELATIVE_CRUNCHCACHE); String cachePath = cacheFolder.getLocation().toOSString(); /* For crunching, we don't need the osManifestPath, osAssetsPath, or the configFilter @@ -219,9 +225,13 @@ public class BuildHelper { } // need to figure out some path before we can execute aapt; + IFolder binFolder = BaseProjectHelper.getAndroidOutputFolder(mProject); // get the cache folder - IFolder cacheFolder = mProject.getFolder(AdtConstants.WS_CRUNCHCACHE); + IFolder cacheFolder = binFolder.getFolder(AdtConstants.WS_BIN_RELATIVE_CRUNCHCACHE); + + // get the BC folder + IFolder bcFolder = binFolder.getFolder(AdtConstants.WS_BIN_RELATIVE_BC); // get the resource folder IFolder resFolder = mProject.getFolder(AdtConstants.WS_RESOURCES); @@ -244,6 +254,7 @@ public class BuildHelper { // png cache folder first. addFolderToList(osResPaths, cacheFolder); + addFolderToList(osResPaths, bcFolder); // regular res folder next. osResPaths.add(resLocation.toOSString()); @@ -252,9 +263,14 @@ public class BuildHelper { if (libProjects != null) { for (IProject lib : libProjects) { // png cache folder first - IFolder libCacheFolder = lib.getFolder(AdtConstants.WS_CRUNCHCACHE); + IFolder libBinFolder = BaseProjectHelper.getAndroidOutputFolder(lib); + + IFolder libCacheFolder = libBinFolder.getFolder(AdtConstants.WS_BIN_RELATIVE_CRUNCHCACHE); addFolderToList(osResPaths, libCacheFolder); + IFolder libBcFolder = libBinFolder.getFolder(AdtConstants.WS_BIN_RELATIVE_BC); + addFolderToList(osResPaths, libBcFolder); + // regular res folder next. IFolder libResFolder = lib.getFolder(AdtConstants.WS_RESOURCES); addFolderToList(osResPaths, libResFolder); @@ -452,6 +468,23 @@ public class BuildHelper { apkBuilder.addNativeLibraries(libFolder.getLocation().toFile()); } + // next the native libraries for the renderscript support mode. + if (mProjectState.getRenderScriptSupportMode()) { + IFolder androidOutputFolder = BaseProjectHelper.getAndroidOutputFolder(mProject); + IResource rsLibFolder = androidOutputFolder.getFolder( + AdtConstants.WS_BIN_RELATIVE_RS_LIBS); + File rsLibFolderFile = rsLibFolder.getLocation().toFile(); + if (rsLibFolderFile.isDirectory()) { + apkBuilder.addNativeLibraries(rsLibFolderFile); + } + + File rsLibs = RenderScriptProcessor.getSupportNativeLibFolder( + mBuildToolInfo.getLocation().getAbsolutePath()); + if (rsLibs.isDirectory()) { + apkBuilder.addNativeLibraries(rsLibs); + } + } + // write the native libraries for the library projects. if (libProjects != null) { for (IProject lib : libProjects) { diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/DefaultSourceChangeHandler.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/DefaultSourceChangeHandler.java new file mode 100644 index 0000000..ea0d695 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/DefaultSourceChangeHandler.java @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2011 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; + +import org.eclipse.core.resources.IContainer; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IResourceDelta; + +import java.util.HashSet; +import java.util.Set; + +/** + * Base source change handler for the {@link SourceProcessor} classes. + * + * It can be used as is, as long as the matching {@link SourceProcessor} properly implements + * its abstract methods, and the processor does not output resource files, + * or can be extended to provide custom implementation for: + * {@link #handleSourceFile(IFile, int)} + * {@link #handleGeneratedFile(IFile, int)} + * {@link #handleResourceFile(IFile, int)} + * {@link #filterResourceFolder(IContainer)} + * + */ +public class DefaultSourceChangeHandler implements SourceChangeHandler { + + private SourceProcessor mProcessor; + + /** List of source files found that are modified or new. */ + private final Set<IFile> mToCompile = new HashSet<IFile>(); + + /** List of source files that have been removed. */ + private final Set<IFile> mRemoved = new HashSet<IFile>(); + + @Override + public boolean handleGeneratedFile(IFile file, int kind) { + if (kind == IResourceDelta.REMOVED || kind == IResourceDelta.CHANGED) { + IFile sourceFile = mProcessor.isOutput(file); + if (sourceFile != null) { + mToCompile.add(sourceFile); + return true; + } + } + + return false; + } + + @Override + public void handleSourceFile(IFile file, int kind) { + // first the file itself if this is a match for the processor's extension + if (mProcessor.getExtensions().contains(file.getFileExtension())) { + if (kind == IResourceDelta.REMOVED) { + mRemoved.add(file); + } else { + mToCompile.add(file); + } + } + + // now the dependencies. In all case we compile the files that depend on the + // added/changed/removed file. + mToCompile.addAll(mProcessor.isDependency(file)); + } + + protected void addFileToCompile(IFile file) { + mToCompile.add(file); + } + + Set<IFile> getFilesToCompile() { + return mToCompile; + } + + protected void addRemovedFile(IFile file) { + mRemoved.add(file); + } + + Set<IFile> getRemovedFiles() { + return mRemoved; + } + + public void reset() { + mToCompile.clear(); + mRemoved.clear(); + } + + protected SourceProcessor getProcessor() { + return mProcessor; + } + + void init(SourceProcessor processor) { + mProcessor = processor; + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/RenderScriptLauncher.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/RenderScriptLauncher.java new file mode 100644 index 0000000..1d3c7bd --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/RenderScriptLauncher.java @@ -0,0 +1,244 @@ +/* + * Copyright (C) 2011 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; + +import com.android.annotations.NonNull; +import com.android.ide.eclipse.adt.AdtConstants; +import com.android.ide.eclipse.adt.AdtPlugin; +import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper; +import com.android.sdklib.build.RenderScriptProcessor.CommandLineLauncher; + +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.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Path; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * A {@link SourceProcessor} for RenderScript files. + */ +public class RenderScriptLauncher implements CommandLineLauncher { + + /** + * Single line llvm-rs-cc error: {@code <path>:<line>:<col>: <error>} + */ + private static Pattern sLlvmPattern1 = Pattern.compile("^(.+?):(\\d+):(\\d+):\\s(.+)$"); //$NON-NLS-1$ + + @NonNull + private final IProject mProject; + @NonNull + private final IFolder mSourceOutFolder; + @NonNull + private final IFolder mResOutFolder; + @NonNull + private final IProgressMonitor mMonitor; + private final boolean mVerbose; + + public RenderScriptLauncher( + @NonNull IProject project, + @NonNull IFolder sourceOutFolder, + @NonNull IFolder resOutFolder, + @NonNull IProgressMonitor monitor, + boolean verbose) { + mProject = project; + mSourceOutFolder = sourceOutFolder; + mResOutFolder = resOutFolder; + mMonitor = monitor; + mVerbose = verbose; + } + + @Override + public void launch(File executable, List<String> arguments, Map<String, String> envVariableMap) + throws IOException, InterruptedException { + // do the exec + try { + if (mVerbose) { + StringBuilder sb = new StringBuilder(executable.getAbsolutePath()); + for (String c : arguments) { + sb.append(' ').append(c); + } + String cmd_line = sb.toString(); + AdtPlugin.printToConsole(mProject, cmd_line); + } + + String[] commandArray = new String[1 + arguments.size()]; + commandArray[0] = executable.getAbsolutePath(); + System.arraycopy(arguments.toArray(), 0, commandArray, 1, arguments.size()); + + ProcessBuilder processBuilder = new ProcessBuilder(commandArray); + Map<String, String> processEnvs = processBuilder.environment(); + for (Map.Entry<String, String> entry : envVariableMap.entrySet()) { + processEnvs.put(entry.getKey(), entry.getValue()); + } + + Process p = processBuilder.start(); + + // list to store each line of stderr + ArrayList<String> stdErr = new ArrayList<String>(); + + // get the output and return code from the process + int returnCode = BuildHelper.grabProcessOutput(mProject, p, stdErr); + + if (stdErr.size() > 0) { + // attempt to parse the error output + boolean parsingError = parseLlvmOutput(stdErr); + + // If the process failed and we couldn't parse the output + // we print a message, mark the project and exit + if (returnCode != 0) { + + if (parsingError || mVerbose) { + // display the message in the console. + if (parsingError) { + AdtPlugin.printErrorToConsole(mProject, stdErr.toArray()); + + // mark the project + BaseProjectHelper.markResource(mProject, + AdtConstants.MARKER_RENDERSCRIPT, + "Unparsed Renderscript error! Check the console for output.", + IMarker.SEVERITY_ERROR); + } else { + AdtPlugin.printToConsole(mProject, stdErr.toArray()); + } + } + return; + } + } else if (returnCode != 0) { + // no stderr output but exec failed. + String msg = String.format("Error executing Renderscript: Return code %1$d", + returnCode); + + BaseProjectHelper.markResource(mProject, AdtConstants.MARKER_AIDL, + msg, IMarker.SEVERITY_ERROR); + + return; + } + } catch (IOException e) { + // mark the project and exit + String msg = String.format( + "Error executing Renderscript. Please check %1$s is present at %2$s", + executable.getName(), executable.getAbsolutePath()); + AdtPlugin.log(IStatus.ERROR, msg); + BaseProjectHelper.markResource(mProject, AdtConstants.MARKER_RENDERSCRIPT, msg, + IMarker.SEVERITY_ERROR); + throw e; + } catch (InterruptedException e) { + // mark the project and exit + String msg = String.format( + "Error executing Renderscript. Please check %1$s is present at %2$s", + executable.getName(), executable.getAbsolutePath()); + AdtPlugin.log(IStatus.ERROR, msg); + BaseProjectHelper.markResource(mProject, AdtConstants.MARKER_RENDERSCRIPT, msg, + IMarker.SEVERITY_ERROR); + throw e; + } + + try { + mSourceOutFolder.refreshLocal(IResource.DEPTH_ONE, mMonitor); + mResOutFolder.refreshLocal(IResource.DEPTH_ONE, mMonitor); + } catch (CoreException e) { + AdtPlugin.log(e, "failed to refresh folders"); + } + + return; + } + + /** + * Parse the output of llvm-rs-cc and mark the file with any errors. + * @param lines The output to parse. + * @return true if the parsing failed, false if success. + */ + private boolean parseLlvmOutput(ArrayList<String> lines) { + // nothing to parse? just return false; + if (lines.size() == 0) { + return false; + } + + // get the root folder for the project as we're going to ignore everything that's + // not in the project + String rootPath = mProject.getLocation().toOSString(); + int rootPathLength = rootPath.length(); + + Matcher m; + + boolean parsing = false; + + for (int i = 0; i < lines.size(); i++) { + String p = lines.get(i); + + m = sLlvmPattern1.matcher(p); + if (m.matches()) { + // get the file path. This may, or may not be the main file being compiled. + String filePath = m.group(1); + if (filePath.startsWith(rootPath) == false) { + // looks like the error in a non-project file. Keep parsing, but + // we'll return true + parsing = true; + continue; + } + + // get the actual file. + filePath = filePath.substring(rootPathLength); + // remove starting separator since we want the path to be relative + if (filePath.startsWith(File.separator)) { + filePath = filePath.substring(1); + } + + // get the file + IFile f = mProject.getFile(new Path(filePath)); + + String lineStr = m.group(2); + // ignore group 3 for now, this is the col number + String msg = m.group(4); + + // get the line number + int line = 0; + try { + line = Integer.parseInt(lineStr); + } catch (NumberFormatException e) { + // looks like the string we extracted wasn't a valid + // file number. Parsing failed and we return true + return true; + } + + // mark the file + BaseProjectHelper.markResource(f, AdtConstants.MARKER_RENDERSCRIPT, msg, line, + IMarker.SEVERITY_ERROR); + + // success, go to the next line + continue; + } + + // invalid line format, flag as error, and keep going + parsing = true; + } + + return parsing; + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/RenderScriptProcessor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/RenderScriptProcessor.java deleted file mode 100644 index 806e4e2..0000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/RenderScriptProcessor.java +++ /dev/null @@ -1,552 +0,0 @@ -/* - * Copyright (C) 2011 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; - -import com.android.SdkConstants; -import com.android.annotations.NonNull; -import com.android.ide.eclipse.adt.AdtConstants; -import com.android.ide.eclipse.adt.AdtPlugin; -import com.android.ide.eclipse.adt.internal.build.builders.BaseBuilder; -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.resources.ResourceFolderType; -import com.android.sdklib.BuildToolInfo; -import com.android.sdklib.IAndroidTarget; -import com.google.common.collect.Sets; - -import org.eclipse.core.resources.IContainer; -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.Path; -import org.eclipse.jdt.core.IJavaProject; - -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 java.util.Set; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * A {@link SourceProcessor} for RenderScript files. - * - */ -public class RenderScriptProcessor extends SourceProcessor { - - private static final String PROPERTY_COMPILE_RS = "compileRenderScript"; //$NON-NLS-1$ - - /** - * Single line llvm-rs-cc error: {@code <path>:<line>:<col>: <error>} - */ - private static Pattern sLlvmPattern1 = Pattern.compile("^(.+?):(\\d+):(\\d+):\\s(.+)$"); //$NON-NLS-1$ - - private final static Set<String> EXTENSIONS = Sets.newHashSetWithExpectedSize(2); - static { - EXTENSIONS.add(SdkConstants.EXT_RS); - EXTENSIONS.add(SdkConstants.EXT_FS); - } - - private static class RsChangeHandler extends SourceChangeHandler { - - @Override - public boolean handleGeneratedFile(IFile file, int kind) { - boolean r = super.handleGeneratedFile(file, kind); - if (r == false && - kind == IResourceDelta.REMOVED && - SdkConstants.EXT_DEP.equalsIgnoreCase(file.getFileExtension())) { - // This looks to be a dependency file. - // For future-proofness let's make sure this dependency file was generated by - // this processor even if it's the only processor using them for now. - - // look for the original file. - // We know we are in the gen folder, so make a path to the dependency file - // relative to the gen folder. Convert this into a Renderscript source file, - // and look to see if this file exists. - SourceProcessor processor = getProcessor(); - IFolder genFolder = processor.getGenFolder(); - IPath relative = file.getFullPath().makeRelativeTo(genFolder.getFullPath()); - // remove the file name segment - relative = relative.removeLastSegments(1); - // add the file name of a Renderscript file. - relative = relative.append(file.getName().replaceAll( - AdtConstants.RE_DEP_EXT, SdkConstants.DOT_RS)); - - if (!findInSourceFolders(processor, genFolder, relative)) { - // could be a FilterScript file? - relative = file.getFullPath().makeRelativeTo(genFolder.getFullPath()); - // remove the file name segment - relative = relative.removeLastSegments(1); - // add the file name of a FilterScript file. - relative = relative.append(file.getName().replaceAll( - AdtConstants.RE_DEP_EXT, SdkConstants.DOT_FS)); - - return findInSourceFolders(processor, genFolder, relative); - } - - return true; - } - - return r; - } - - private boolean findInSourceFolders(SourceProcessor processor, IFolder genFolder, - IPath relative) { - // now look for a match in the source folders. - List<IPath> sourceFolders = BaseProjectHelper.getSourceClasspaths( - processor.getJavaProject()); - IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); - - for (IPath sourceFolderPath : sourceFolders) { - IFolder sourceFolder = root.getFolder(sourceFolderPath); - // we don't look in the 'gen' source folder as there will be no source in there. - if (sourceFolder.exists() && sourceFolder.equals(genFolder) == false) { - IFile sourceFile = sourceFolder.getFile(relative); - SourceFileData data = processor.getFileData(sourceFile); - if (data != null) { - addFileToCompile(sourceFile); - return true; - } - } - } - - return false; - } - - @Override - protected boolean filterResourceFolder(IContainer folder) { - return ResourceFolderType.RAW.getName().equals(folder.getName()); - } - } - - private int mTargetApi = 11; - - public RenderScriptProcessor(@NonNull IJavaProject javaProject, - @NonNull BuildToolInfo buildToolInfo, @NonNull IFolder genFolder) { - super(javaProject, buildToolInfo, genFolder, new RsChangeHandler()); - } - - public void setTargetApi(int targetApi) { - // make sure the target api value is good. Must be 11+ or llvm-rs-cc complains. - mTargetApi = targetApi < 11 ? 11 : targetApi; - } - - @Override - protected Set<String> getExtensions() { - return EXTENSIONS; - } - - @Override - protected String getSavePropertyName() { - return PROPERTY_COMPILE_RS; - } - - @Override - protected void doCompileFiles(List<IFile> sources, BaseBuilder builder, - IProject project, IAndroidTarget projectTarget, - List<IPath> sourceFolders, List<IFile> notCompiledOut, List<File> libraryProjectsOut, - IProgressMonitor monitor) throws CoreException { - - IFolder genFolder = getGenFolder(); - - IFolder rawFolder = project.getFolder( - new Path(SdkConstants.FD_RES).append(SdkConstants.FD_RES_RAW)); - - int depIndex; - - BuildToolInfo buildToolInfo = getBuildToolInfo(); - - // create the command line - String[] command = new String[15]; - int index = 0; - command[index++] = buildToolInfo.getPath(BuildToolInfo.PathId.LLVM_RS_CC); - command[index++] = "-I"; //$NON-NLS-1$ - command[index++] = buildToolInfo.getPath(BuildToolInfo.PathId.ANDROID_RS_CLANG); - command[index++] = "-I"; //$NON-NLS-1$ - command[index++] = buildToolInfo.getPath(BuildToolInfo.PathId.ANDROID_RS); - command[index++] = "-p"; //$NON-NLS-1$ - command[index++] = genFolder.getLocation().toOSString(); - command[index++] = "-o"; //$NON-NLS-1$ - command[index++] = rawFolder.getLocation().toOSString(); - - command[index++] = "-target-api"; //$NON-NLS-1$ - command[index++] = Integer.toString(mTargetApi); - - command[index++] = "-d"; //$NON-NLS-1$ - command[depIndex = index++] = null; - command[index++] = "-MD"; //$NON-NLS-1$ - - boolean verbose = AdtPrefs.getPrefs().getBuildVerbosity() == BuildVerbosity.VERBOSE; - boolean someSuccess = false; - - // remove the generic marker from the project - builder.removeMarkersFromResource(project, AdtConstants.MARKER_RENDERSCRIPT); - - // loop until we've compile them all - for (IFile sourceFile : sources) { - if (verbose) { - String name = sourceFile.getName(); - IPath sourceFolderPath = getSourceFolderFor(sourceFile); - if (sourceFolderPath != null) { - // make a path to the source file relative to the source folder. - IPath relative = sourceFile.getFullPath().makeRelativeTo(sourceFolderPath); - name = relative.toString(); - } - AdtPlugin.printToConsole(project, "RenderScript: " + name); - } - - // Remove the RS error markers from the source file and the dependencies - builder.removeMarkersFromResource(sourceFile, AdtConstants.MARKER_RENDERSCRIPT); - SourceFileData data = getFileData(sourceFile); - if (data != null) { - for (IFile dep : data.getDependencyFiles()) { - builder.removeMarkersFromResource(dep, AdtConstants.MARKER_RENDERSCRIPT); - } - } - - // get the path of the source file. - IPath sourcePath = sourceFile.getLocation(); - String osSourcePath = sourcePath.toOSString(); - - // finish to set the command line. - command[depIndex] = quote(getDependencyFolder(sourceFile).getLocation().toOSString()); - command[index] = quote(osSourcePath); - - // launch the process - if (!execLlvmRsCc(builder, project, command, sourceFile, buildToolInfo.getLocation(), - verbose)) { - // llvm-rs-cc failed. File should be marked. We add the file to the list - // of file that will need compilation again. - notCompiledOut.add(sourceFile); - } else { - // Success. we'll return that we generated code and resources. - setCompilationStatus(COMPILE_STATUS_CODE | COMPILE_STATUS_RES); - - // need to parse the .d file to figure out the dependencies and the generated file - parseDependencyFileFor(sourceFile); - someSuccess = true; - } - } - - if (someSuccess) { - rawFolder.refreshLocal(IResource.DEPTH_ONE, monitor); - } - } - - private boolean execLlvmRsCc(BaseBuilder builder, IProject project, String[] command, - IFile sourceFile, File buildToolRoot, boolean verbose) { - // do the exec - try { - if (verbose) { - StringBuilder sb = new StringBuilder(); - for (String c : command) { - sb.append(c); - sb.append(' '); - } - String cmd_line = sb.toString(); - AdtPlugin.printToConsole(project, cmd_line); - } - - ProcessBuilder processBuilder = new ProcessBuilder(command); - Map<String, String> env = processBuilder.environment(); - if (SdkConstants.CURRENT_PLATFORM == SdkConstants.PLATFORM_DARWIN) { - env.put("DYLD_LIBRARY_PATH", buildToolRoot.getAbsolutePath()); - } else if (SdkConstants.CURRENT_PLATFORM == SdkConstants.PLATFORM_LINUX) { - env.put("LD_LIBRARY_PATH", buildToolRoot.getAbsolutePath()); - } - - Process p = processBuilder.start(); - - // list to store each line of stderr - ArrayList<String> stdErr = new ArrayList<String>(); - - // get the output and return code from the process - int returnCode = BuildHelper.grabProcessOutput(project, p, stdErr); - - if (stdErr.size() > 0) { - // attempt to parse the error output - boolean parsingError = parseLlvmOutput(stdErr); - - // If the process failed and we couldn't parse the output - // we print a message, mark the project and exit - if (returnCode != 0) { - - if (parsingError || verbose) { - // display the message in the console. - if (parsingError) { - AdtPlugin.printErrorToConsole(project, stdErr.toArray()); - - // mark the project - BaseProjectHelper.markResource(project, - AdtConstants.MARKER_RENDERSCRIPT, - "Unparsed Renderscript error! Check the console for output.", - IMarker.SEVERITY_ERROR); - } else { - AdtPlugin.printToConsole(project, stdErr.toArray()); - } - } - return false; - } - } else if (returnCode != 0) { - // no stderr output but exec failed. - String msg = String.format("Error executing Renderscript: Return code %1$d", - returnCode); - - BaseProjectHelper.markResource(project, AdtConstants.MARKER_AIDL, - msg, IMarker.SEVERITY_ERROR); - - return false; - } - } catch (IOException e) { - // mark the project and exit - String msg = String.format( - "Error executing Renderscript. Please check llvm-rs-cc is present at %1$s", - command[0]); - BaseProjectHelper.markResource(project, AdtConstants.MARKER_RENDERSCRIPT, msg, - IMarker.SEVERITY_ERROR); - return false; - } catch (InterruptedException e) { - // mark the project and exit - String msg = String.format( - "Error executing Renderscript. Please check llvm-rs-cc is present at %1$s", - command[0]); - BaseProjectHelper.markResource(project, AdtConstants.MARKER_RENDERSCRIPT, msg, - IMarker.SEVERITY_ERROR); - return false; - } - - return true; - } - - /** - * Parse the output of llvm-rs-cc and mark the file with any errors. - * @param lines The output to parse. - * @return true if the parsing failed, false if success. - */ - private boolean parseLlvmOutput(ArrayList<String> lines) { - // nothing to parse? just return false; - if (lines.size() == 0) { - return false; - } - - // get the root folder for the project as we're going to ignore everything that's - // not in the project - IProject project = getJavaProject().getProject(); - String rootPath = project.getLocation().toOSString(); - int rootPathLength = rootPath.length(); - - Matcher m; - - boolean parsing = false; - - for (int i = 0; i < lines.size(); i++) { - String p = lines.get(i); - - m = sLlvmPattern1.matcher(p); - if (m.matches()) { - // get the file path. This may, or may not be the main file being compiled. - String filePath = m.group(1); - if (filePath.startsWith(rootPath) == false) { - // looks like the error in a non-project file. Keep parsing, but - // we'll return true - parsing = true; - continue; - } - - // get the actual file. - filePath = filePath.substring(rootPathLength); - // remove starting separator since we want the path to be relative - if (filePath.startsWith(File.separator)) { - filePath = filePath.substring(1); - } - - // get the file - IFile f = project.getFile(new Path(filePath)); - - String lineStr = m.group(2); - // ignore group 3 for now, this is the col number - String msg = m.group(4); - - // get the line number - int line = 0; - try { - line = Integer.parseInt(lineStr); - } catch (NumberFormatException e) { - // looks like the string we extracted wasn't a valid - // file number. Parsing failed and we return true - return true; - } - - // mark the file - BaseProjectHelper.markResource(f, AdtConstants.MARKER_RENDERSCRIPT, msg, line, - IMarker.SEVERITY_ERROR); - - // success, go to the next line - continue; - } - - // invalid line format, flag as error, and keep going - parsing = true; - } - - return parsing; - } - - - @Override - protected void doRemoveFiles(SourceFileData bundle) throws CoreException { - // call the super implementation, it will remove the output files - super.doRemoveFiles(bundle); - - // now remove the dependency file. - IFile depFile = getDependencyFileFor(bundle.getSourceFile()); - if (depFile.exists()) { - depFile.getLocation().toFile().delete(); - } - } - - @Override - protected void loadOutputAndDependencies() { - Collection<SourceFileData> dataList = getAllFileData(); - for (SourceFileData data : dataList) { - // parse the dependency file. If this fails, force compilation of the file. - if (parseDependencyFileFor(data.getSourceFile()) == false) { - addFileToCompile(data.getSourceFile()); - } - } - } - - private boolean parseDependencyFileFor(IFile sourceFile) { - IFile depFile = getDependencyFileFor(sourceFile); - File f = depFile.getLocation().toFile(); - if (f.exists()) { - SourceFileData data = getFileData(sourceFile); - if (data == null) { - data = new SourceFileData(sourceFile); - addData(data); - } - parseDependencyFile(data, f); - return true; - } - - return false; - } - - private IFolder getDependencyFolder(IFile sourceFile) { - IPath sourceFolderPath = getSourceFolderFor(sourceFile); - - // this really shouldn't happen since the sourceFile must be in a source folder - // since it comes from the delta visitor - if (sourceFolderPath != null) { - // make a path to the source file relative to the source folder. - IPath relative = sourceFile.getFullPath().makeRelativeTo(sourceFolderPath); - // remove the file name. This is now the destination folder. - relative = relative.removeLastSegments(1); - - return getGenFolder().getFolder(relative); - } - - return null; - } - - private IFile getDependencyFileFor(IFile sourceFile) { - IFolder depFolder = getDependencyFolder(sourceFile); - return depFolder.getFile(sourceFile.getName().replaceAll(AdtConstants.RE_RS_EXT, - SdkConstants.DOT_DEP)); - } - - /** - * Parses the given dependency file and fills the given {@link SourceFileData} with it. - * - * @param data the bundle to fill. - * @param file the dependency file - */ - private void parseDependencyFile(SourceFileData data, File dependencyFile) { - //contents = file.getContents(); - String content = AdtPlugin.readFile(dependencyFile); - - // we're going to be pretty brutal here. - // The format is something like: - // output1 output2 [...]: dep1 dep2 [...] - // expect it's likely split on several lines. So let's move it back on a single line - // first - String[] lines = content.split("\n"); //$NON-NLS-1$ - StringBuilder sb = new StringBuilder(); - for (String line : lines) { - line = line.trim(); - if (line.endsWith("\\")) { //$NON-NLS-1$ - line = line.substring(0, line.length() - 1); - } - - sb.append(line); - } - - // split the left and right part - String[] files = sb.toString().split(":"); //$NON-NLS-1$ - - // get the output files: - String[] outputs = files[0].trim().split(" "); //$NON-NLS-1$ - - // and the dependency files: - String[] dependencies = files[1].trim().split(" "); //$NON-NLS-1$ - - List<IFile> outputFiles = new ArrayList<IFile>(); - List<IFile> dependencyFiles = new ArrayList<IFile>(); - - fillList(outputs, outputFiles); - fillList(dependencies, dependencyFiles); - - data.setOutputFiles(outputFiles); - data.setDependencyFiles(dependencyFiles); - } - - private void fillList(String[] paths, List<IFile> list) { - // get the root folder for the project as we're going to ignore everything that's - // not in the project - IProject project = getJavaProject().getProject(); - String rootPath = project.getLocation().toOSString(); - int rootPathLength = rootPath.length(); - - // all those should really be in the project - for (String p : paths) { - - if (p.startsWith(rootPath)) { - p = p.substring(rootPathLength); - // remove starting separator since we want the path to be relative - if (p.startsWith(File.separator)) { - p = p.substring(1); - } - - // get the file - IFile f = project.getFile(new Path(p)); - list.add(f); - } - } - } -} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/RsSourceChangeHandler.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/RsSourceChangeHandler.java new file mode 100644 index 0000000..715895a --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/RsSourceChangeHandler.java @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2013 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; + +import com.android.SdkConstants; +import com.android.annotations.NonNull; +import com.android.ide.eclipse.adt.AdtPlugin; +import com.android.sdklib.build.RenderScriptChecker; + +import org.eclipse.core.resources.IFile; + +import java.io.File; +import java.io.IOException; +import java.util.Set; + +public class RsSourceChangeHandler implements SourceChangeHandler { + + private final RenderScriptChecker mChecker; + private boolean mIsCheckerLoaded = false; + + private boolean mMustCompile = false; + + public RsSourceChangeHandler(@NonNull RenderScriptChecker checker) { + mChecker = checker; + } + + @Override + public boolean handleGeneratedFile(IFile file, int kind) { + if (mMustCompile) { + return false; + } + + if (!mIsCheckerLoaded) { + try { + mChecker.loadDependencies(); + } catch (IOException e) { + // failed to load the dependency files, force a compilation, log the error. + AdtPlugin.log(e, "Failed to read dependency files"); + mMustCompile = true; + return false; + } + } + + Set<File> oldOutputs = mChecker.getOldOutputs(); + // mustCompile is always false here. + mMustCompile = oldOutputs.contains(file.getLocation().toFile()); + return mMustCompile; + } + + @Override + public void handleSourceFile(IFile file, int kind) { + if (mMustCompile) { + return; + } + + String ext = file.getFileExtension(); + if (SdkConstants.EXT_RS.equals(ext) || + SdkConstants.EXT_FS.equals(ext) || + SdkConstants.EXT_RSH.equals(ext)) { + mMustCompile = true; + } + } + + public boolean mustCompile() { + return mMustCompile; + } + + @NonNull + public RenderScriptChecker getChecker() { + return mChecker; + } + + public void prepareFullBuild() { + mMustCompile = true; + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/SourceChangeHandler.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/SourceChangeHandler.java index 5436798..12a0551 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/SourceChangeHandler.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/SourceChangeHandler.java @@ -16,103 +16,10 @@ package com.android.ide.eclipse.adt.internal.build; -import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IFile; -import org.eclipse.core.resources.IResourceDelta; -import java.util.HashSet; -import java.util.Set; +public interface SourceChangeHandler { -/** - * Base source change handler for the {@link SourceProcessor} classes. - * - * It can be used as is, as long as the matching {@link SourceProcessor} properly implements - * its abstract methods, and the processor does not output resource files, - * or can be extended to provide custom implementation for: - * {@link #handleSourceFile(IFile, int)} - * {@link #handleGeneratedFile(IFile, int)} - * {@link #handleResourceFile(IFile, int)} - * {@link #filterResourceFolder(IContainer)} - * - */ -public class SourceChangeHandler { - - private SourceProcessor mProcessor; - - /** List of source files found that are modified or new. */ - private final Set<IFile> mToCompile = new HashSet<IFile>(); - - /** List of source files that have been removed. */ - private final Set<IFile> mRemoved = new HashSet<IFile>(); - - public boolean handleGeneratedFile(IFile file, int kind) { - if (kind == IResourceDelta.REMOVED || kind == IResourceDelta.CHANGED) { - IFile sourceFile = mProcessor.isOutput(file); - if (sourceFile != null) { - mToCompile.add(sourceFile); - return true; - } - } - - return false; - } - - public void handleSourceFile(IFile file, int kind) { - // first the file itself if this is a match for the processor's extension - if (mProcessor.getExtensions().contains(file.getFileExtension())) { - if (kind == IResourceDelta.REMOVED) { - mRemoved.add(file); - } else { - mToCompile.add(file); - } - } - - // now the dependencies. In all case we compile the files that depend on the - // added/changed/removed file. - mToCompile.addAll(mProcessor.isDependency(file)); - } - - public void handleResourceFile(IFile file, int kind) { - if (filterResourceFolder(file.getParent())) { - handleGeneratedFile(file, kind); - } - } - - /** - * Called to restrict {@link #handleResourceFile(IFile, int)} on selected resource folders. - * @param folder - * @return - */ - protected boolean filterResourceFolder(IContainer folder) { - return false; - } - - protected void addFileToCompile(IFile file) { - mToCompile.add(file); - } - - Set<IFile> getFilesToCompile() { - return mToCompile; - } - - protected void addRemovedFile(IFile file) { - mRemoved.add(file); - } - - Set<IFile> getRemovedFiles() { - return mRemoved; - } - - public void reset() { - mToCompile.clear(); - mRemoved.clear(); - } - - protected SourceProcessor getProcessor() { - return mProcessor; - } - - void init(SourceProcessor processor) { - mProcessor = processor; - } + boolean handleGeneratedFile(IFile file, int kind); + void handleSourceFile(IFile file, int kind); } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/SourceProcessor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/SourceProcessor.java index 7dd92aa..1fc3e4e 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/SourceProcessor.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/SourceProcessor.java @@ -63,7 +63,7 @@ public abstract class SourceProcessor { private final IJavaProject mJavaProject; private BuildToolInfo mBuildToolInfo; private final IFolder mGenFolder; - private final SourceChangeHandler mDeltaVisitor; + private final DefaultSourceChangeHandler mDeltaVisitor; /** List of source files pending compilation at the next build */ private final List<IFile> mToCompile = new ArrayList<IFile>(); @@ -89,9 +89,11 @@ public abstract class SourceProcessor { return path; } - protected SourceProcessor(@NonNull IJavaProject javaProject, - @NonNull BuildToolInfo buildToolInfo, @NonNull IFolder genFolder, - @NonNull SourceChangeHandler deltaVisitor) { + protected SourceProcessor( + @NonNull IJavaProject javaProject, + @NonNull BuildToolInfo buildToolInfo, + @NonNull IFolder genFolder, + @NonNull DefaultSourceChangeHandler deltaVisitor) { mJavaProject = javaProject; mBuildToolInfo = buildToolInfo; mGenFolder = genFolder; @@ -117,9 +119,11 @@ public abstract class SourceProcessor { } } - protected SourceProcessor(@NonNull IJavaProject javaProject, - @NonNull BuildToolInfo buildToolInfo, @NonNull IFolder genFolder) { - this(javaProject, buildToolInfo, genFolder, new SourceChangeHandler()); + protected SourceProcessor( + @NonNull IJavaProject javaProject, + @NonNull BuildToolInfo buildToolInfo, + @NonNull IFolder genFolder) { + this(javaProject, buildToolInfo, genFolder, new DefaultSourceChangeHandler()); } public void setBuildToolInfo(BuildToolInfo buildToolInfo) { @@ -173,7 +177,7 @@ public abstract class SourceProcessor { return mFiles.values(); } - public final SourceChangeHandler getChangeHandler() { + public final DefaultSourceChangeHandler getChangeHandler() { return mDeltaVisitor; } @@ -414,7 +418,7 @@ public abstract class SourceProcessor { * delta visitor * @param visitor the delta visitor. */ - private void mergeFileModifications(SourceChangeHandler visitor) { + private void mergeFileModifications(DefaultSourceChangeHandler visitor) { Set<IFile> toRemove = visitor.getRemovedFiles(); Set<IFile> toCompile = visitor.getFilesToCompile(); diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/ChangedFileSetHelper.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/ChangedFileSetHelper.java index 529dad2..9fc19a7 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/ChangedFileSetHelper.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/ChangedFileSetHelper.java @@ -23,6 +23,7 @@ import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper; import org.eclipse.core.resources.IFolder; import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.IPath; import java.util.ArrayList; @@ -81,13 +82,16 @@ class ChangedFileSetHelper { * @return a ChangeFileSet */ static ChangedFileSet getResCfs(@NonNull IProject project) { + // generated res is inside the project's android output folder + String path = getRelativeAndroidOut(project); + ChangedFileSet set = new ChangedFileSet( "resources", //$NON-NLS-1$ SdkConstants.FD_RES + "/**", //$NON-NLS-1$ - SdkConstants.FD_ASSETS + "/**"); //$NON-NLS-1$ + SdkConstants.FD_ASSETS + "/**", //$NON-NLS-1$ + path + '/' + AdtConstants.WS_BIN_RELATIVE_BC + "/**"); //$NON-NLS-1$ // output file is based on the project's android output folder - String path = getRelativeAndroidOut(project); set.setOutput(path + '/' + AdtConstants.FN_RESOURCES_AP_); return set; @@ -180,14 +184,17 @@ class ChangedFileSetHelper { return set; } + private static String getRelativePath(@NonNull IProject project, @NonNull IResource resource) { + return resource.getFullPath().makeRelativeTo(project.getFullPath()).toString(); + } + private static String getRelativeAndroidOut(@NonNull IProject project) { IFolder folder = BaseProjectHelper.getAndroidOutputFolder(project); - return folder.getFullPath().makeRelativeTo(project.getFullPath()).toString(); + return getRelativePath(project, folder); } private static String getRelativeJavaCOut(@NonNull IProject project) { IFolder folder = BaseProjectHelper.getJavaOutputFolder(project); - return folder.getFullPath().makeRelativeTo(project.getFullPath()).toString(); + return getRelativePath(project, folder); } - } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/PatternBasedDeltaVisitor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/PatternBasedDeltaVisitor.java index 81fceb1..b52ede9 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/PatternBasedDeltaVisitor.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/PatternBasedDeltaVisitor.java @@ -90,11 +90,11 @@ class PatternBasedDeltaVisitor implements IResourceDeltaVisitor { } else if (resource.getType() == IResource.FILE) { IPath path = resource.getFullPath().makeRelativeTo(mDeltaProject.getFullPath()); + String pathStr = path.toString(); // FIXME: no need to loop through all the sets once they have all said they need something (return false below and above) for (ChangedFileSet set : mSets) { // FIXME: should ignore sets that have already returned true. - String pathStr = path.toString(); if (set.isInput(pathStr, path)) { mResults.put(set, Boolean.TRUE); diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/PostCompilerBuilder.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/PostCompilerBuilder.java index 093072b..8aacb44 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/PostCompilerBuilder.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/PostCompilerBuilder.java @@ -371,7 +371,9 @@ public class PostCompilerBuilder extends BaseBuilder { if (DEBUG_LOG) { AdtPlugin.log(IStatus.INFO, "%s running crunch!", project.getName()); } - BuildHelper helper = new BuildHelper(project, mBuildToolInfo, + BuildHelper helper = new BuildHelper( + projectState, + mBuildToolInfo, mOutStream, mErrStream, false /*jumbo mode doesn't matter here*/, false /*dex merger doesn't matter here*/, @@ -486,7 +488,9 @@ public class PostCompilerBuilder extends BaseBuilder { AdtConstants.DEX_OPTIONS_DISABLE_MERGER); Boolean dexMerger = Boolean.valueOf(dexMergerStr); - BuildHelper helper = new BuildHelper(project, mBuildToolInfo, + BuildHelper helper = new BuildHelper( + projectState, + mBuildToolInfo, mOutStream, mErrStream, jumbo.booleanValue(), dexMerger.booleanValue(), diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/PreCompilerBuilder.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/PreCompilerBuilder.java index ae6b3f5..2554c79 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/PreCompilerBuilder.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/PreCompilerBuilder.java @@ -25,7 +25,8 @@ 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.RenderScriptProcessor; +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.lint.EclipseLintClient; import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs; @@ -49,6 +50,8 @@ 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; @@ -67,6 +70,8 @@ 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; @@ -128,8 +133,6 @@ public class PreCompilerBuilder extends BaseBuilder { * Stored persistently in the project. */ private boolean mLastBuildConfigMode; - private final List<SourceProcessor> mProcessors = new ArrayList<SourceProcessor>(2); - /** cache of the java package defined in the manifest */ private String mManifestPackage; @@ -145,7 +148,7 @@ public class PreCompilerBuilder extends BaseBuilder { private DerivedProgressMonitor mDerivedProgressMonitor; private AidlProcessor mAidlProcessor; - private RenderScriptProcessor mRenderScriptProcessor; + private RsSourceChangeHandler mRenderScriptSourceChangeHandler; /** * Progress monitor waiting the end of the process to set a persistent value @@ -253,6 +256,8 @@ public class PreCompilerBuilder extends BaseBuilder { // as only those projects have an impact on what is generated by this builder. IProject[] result = null; + IFolder resOutFolder = null; + try { assert mDerivedProgressMonitor != null; @@ -279,13 +284,16 @@ public class PreCompilerBuilder extends BaseBuilder { // Top level check to make sure the build can move forward. abortOnBadSetup(javaProject, projectState); - setupSourceProcessors(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; @@ -305,9 +313,8 @@ public class PreCompilerBuilder extends BaseBuilder { mMustCompileResources = true; mMustCreateBuildConfig = true; - for (SourceProcessor processor : mProcessors) { - processor.prepareFullBuild(project); - } + mAidlProcessor.prepareFullBuild(project); + mRenderScriptSourceChangeHandler.prepareFullBuild(); } else { AdtPlugin.printBuildToConsole(BuildVerbosity.VERBOSE, project, Messages.Start_Inc_Pre_Compiler); @@ -320,11 +327,12 @@ public class PreCompilerBuilder extends BaseBuilder { if (delta == null) { mMustCompileResources = true; - for (SourceProcessor processor : mProcessors) { - processor.prepareFullBuild(project); - } + mAidlProcessor.prepareFullBuild(project); + mRenderScriptSourceChangeHandler.prepareFullBuild(); } else { - dv = new PreCompilerDeltaVisitor(this, sourceFolderPathList, mProcessors); + dv = new PreCompilerDeltaVisitor(this, sourceFolderPathList, + mAidlProcessor.getChangeHandler(), + mRenderScriptSourceChangeHandler); delta.accept(dv); // Check to see if Manifest.xml, Manifest.java, or R.java have changed: @@ -363,9 +371,7 @@ public class PreCompilerBuilder extends BaseBuilder { } } // else: already processed the deltas in ResourceManager's IRawDeltaListener - for (SourceProcessor processor : mProcessors) { - processor.doneVisiting(project); - } + mAidlProcessor.doneVisiting(project); // get the java package from the visitor javaPackage = dv.getManifestPackage(); @@ -622,9 +628,8 @@ public class PreCompilerBuilder extends BaseBuilder { mMustMergeManifest = true; mMustCompileResources = true; mMustCreateBuildConfig = true; - for (SourceProcessor processor : mProcessors) { - processor.prepareFullBuild(project); - } + mAidlProcessor.prepareFullBuild(project); + mRenderScriptSourceChangeHandler.prepareFullBuild(); saveProjectBooleanProperty(PROPERTY_MERGE_MANIFEST, mMustMergeManifest); saveProjectBooleanProperty(PROPERTY_COMPILE_RESOURCES, mMustCompileResources); @@ -661,33 +666,22 @@ public class PreCompilerBuilder extends BaseBuilder { // run the source processors int processorStatus = SourceProcessor.COMPILE_STATUS_NONE; - // 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 result; - } - } - mRenderScriptProcessor.setTargetApi(rsTarget); + 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; + } - for (SourceProcessor processor : mProcessors) { - try { - processorStatus |= processor.compileFiles(this, - project, projectTarget, sourceFolderPathList, - libProjectsOut, monitor); - } catch (Throwable t) { - handleException(t, String.format( - "Failed to run %s. Check workspace log for detail.", - processor.getClass().getName())); - 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. @@ -710,7 +704,7 @@ public class PreCompilerBuilder extends BaseBuilder { proguardFile = androidOutputFolder.getFile(AdtConstants.FN_AAPT_PROGUARD); } - handleResources(project, javaPackage, projectTarget, manifestFile, + handleResources(project, javaPackage, projectTarget, manifestFile, resOutFolder, libProjects, isLibrary, proguardFile); } @@ -725,11 +719,18 @@ public class PreCompilerBuilder extends BaseBuilder { // 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); @@ -805,17 +806,95 @@ public class PreCompilerBuilder extends BaseBuilder { } private void setupSourceProcessors(@NonNull IJavaProject javaProject, - @NonNull ProjectState projectState) { + @NonNull ProjectState projectState, + @NonNull List<IPath> sourceFolderPathList, + @NonNull IFolder androidOutputFolder) { if (mAidlProcessor == null) { mAidlProcessor = new AidlProcessor(javaProject, mBuildToolInfo, mGenFolder); - mRenderScriptProcessor = new RenderScriptProcessor(javaProject, mBuildToolInfo, - mGenFolder); - mProcessors.add(mAidlProcessor); - mProcessors.add(mRenderScriptProcessor); } else { mAidlProcessor.setBuildToolInfo(mBuildToolInfo); - mRenderScriptProcessor.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") @@ -965,8 +1044,8 @@ public class PreCompilerBuilder extends BaseBuilder { * @throws AbortBuildException */ private void handleResources(IProject project, String javaPackage, IAndroidTarget projectTarget, - IFile manifest, List<IProject> libProjects, boolean isLibrary, IFile proguardFile) - throws CoreException, AbortBuildException { + 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); @@ -1025,7 +1104,10 @@ public class PreCompilerBuilder extends BaseBuilder { String proguardFilePath = proguardFile != null ? proguardFile.getLocation().toOSString(): null; - execAapt(project, projectTarget, osOutputPath, osResPath, osManifestPath, + File resOutFile = resOutFolder.getLocation().toFile(); + String resOutPath = resOutFile.isDirectory() ? resOutFile.getAbsolutePath() : null; + + execAapt(project, projectTarget, osOutputPath, resOutPath, osResPath, osManifestPath, mainPackageFolder, libResFolders, libRFiles, isLibrary, proguardFilePath); } } @@ -1050,7 +1132,7 @@ public class PreCompilerBuilder extends BaseBuilder { */ @SuppressWarnings("deprecation") private void execAapt(IProject project, IAndroidTarget projectTarget, String osOutputPath, - String osResPath, String osManifestPath, IFolder packageFolder, + String osBcOutPath, String osResPath, String osManifestPath, IFolder packageFolder, ArrayList<IFolder> libResFolders, List<Pair<File, String>> libRFiles, boolean isLibrary, String proguardFile) throws AbortBuildException { @@ -1094,6 +1176,10 @@ public class PreCompilerBuilder extends BaseBuilder { 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) { diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/PreCompilerDeltaVisitor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/PreCompilerDeltaVisitor.java index f868224..57316f5 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/PreCompilerDeltaVisitor.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/builders/PreCompilerDeltaVisitor.java @@ -22,12 +22,12 @@ 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.build.SourceChangeHandler; -import com.android.ide.eclipse.adt.internal.build.SourceProcessor; import com.android.ide.eclipse.adt.internal.build.builders.BaseBuilder.BaseDeltaVisitor; 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.io.IFileWrapper; +import com.google.common.collect.Lists; import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IFile; @@ -40,7 +40,7 @@ import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; -import java.util.ArrayList; +import java.util.Arrays; import java.util.List; /** @@ -93,22 +93,18 @@ class PreCompilerDeltaVisitor extends BaseDeltaVisitor implements IResourceDelta private final List<IPath> mSourceFolders; private boolean mIsGenSourceFolder = false; - private final List<SourceChangeHandler> mSourceChangeHandlers = - new ArrayList<SourceChangeHandler>(); + private final List<SourceChangeHandler> mSourceChangeHandlers = Lists.newArrayList(); private final IWorkspaceRoot mRoot; private IFolder mAndroidOutputFolder; public PreCompilerDeltaVisitor(BaseBuilder builder, List<IPath> sourceFolders, - List<SourceProcessor> processors) { + SourceChangeHandler... handlers) { super(builder); mSourceFolders = sourceFolders; mRoot = ResourcesPlugin.getWorkspace().getRoot(); - for (SourceProcessor processor : processors) { - SourceChangeHandler handler = processor.getChangeHandler(); - mSourceChangeHandlers.add(handler); - } + mSourceChangeHandlers.addAll(Arrays.asList(handlers)); mAndroidOutputFolder = BaseProjectHelper.getAndroidOutputFolder(builder.getProject()); } @@ -296,7 +292,7 @@ class PreCompilerDeltaVisitor extends BaseDeltaVisitor implements IResourceDelta if (outputWarning) { if (kind == IResourceDelta.REMOVED) { - // We pring an error just so that it's red, but it's just a warning really. + // We print an error just so that it's red, but it's just a warning really. String msg = String.format(Messages.s_Removed_Recreating_s, fileName); AdtPlugin.printErrorToConsole(mBuilder.getProject(), msg); } else if (kind == IResourceDelta.CHANGED) { @@ -354,9 +350,6 @@ class PreCompilerDeltaVisitor extends BaseDeltaVisitor implements IResourceDelta mBuilder.getProject(), message); } - for (SourceChangeHandler handler : mSourceChangeHandlers) { - handler.handleResourceFile((IFile)resource, kind); - } // If it's an XML resource, check the syntax if (SdkConstants.EXT_XML.equalsIgnoreCase(ext) && kind != IResourceDelta.REMOVED) { // check xml Validity diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/AndroidClasspathContainerInitializer.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/AndroidClasspathContainerInitializer.java index c526edf..f9382c5 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/AndroidClasspathContainerInitializer.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/AndroidClasspathContainerInitializer.java @@ -185,64 +185,71 @@ public class AndroidClasspathContainerInitializer extends BaseClasspathContainer // if we are loaded and the target is non null, we create a valid // ClassPathContainer if (sdkIsLoaded && target != null) { - // first make sure the target has loaded its data - Sdk.getCurrent().checkAndLoadTargetData(target, null /*project*/); - - String targetName = target.getClasspathName(); - - return new AndroidClasspathContainer( - createClasspathEntries(iProject, target, targetName), - new Path(AdtConstants.CONTAINER_FRAMEWORK), - targetName, - IClasspathContainer.K_DEFAULT_SYSTEM); - } - - // In case of error, we'll try different thing to provide the best error message - // possible. - // Get the project's target's hash string (if it exists) - String hashString = state.getTargetHashString(); - - if (hashString == null || hashString.length() == 0) { - // if there is no hash string we only show this if the SDK is loaded. - // For a project opened at start-up with no target, this would be displayed - // twice, once when the project is opened, and once after the SDK has - // finished loading. - // By testing the sdk is loaded, we only show this once in the console. - if (sdkIsLoaded) { - markerMessage = String.format( - "Project has no target set. Edit the project properties to set one."); + // check the renderscript support mode. If support mode is enabled, + // target API must be 18+ + if (!state.getRenderScriptSupportMode() || + target.getVersion().getApiLevel() >= 18) { + // first make sure the target has loaded its data + Sdk.getCurrent().checkAndLoadTargetData(target, null /*project*/); + + String targetName = target.getClasspathName(); + + return new AndroidClasspathContainer( + createClasspathEntries(iProject, target, targetName), + new Path(AdtConstants.CONTAINER_FRAMEWORK), + targetName, + IClasspathContainer.K_DEFAULT_SYSTEM); + } else { + markerMessage = "Renderscript support mode requires compilation target API to be 18+."; } - } else if (sdkIsLoaded) { - markerMessage = String.format( - "Unable to resolve target '%s'", hashString); } else { - // this is the case where there is a hashString but the SDK is not yet - // loaded and therefore we can't get the target yet. - // We check if there is a cache of the needed information. - AndroidClasspathContainer container = getContainerFromCache(iProject, - target); - - if (container == null) { - // either the cache was wrong (ie folder does not exists anymore), or - // there was no cache. In this case we need to make sure the project - // is resolved again after the SDK is loaded. - plugin.setProjectToResolve(javaProject); - + // In case of error, we'll try different thing to provide the best error message + // possible. + // Get the project's target's hash string (if it exists) + String hashString = state.getTargetHashString(); + + if (hashString == null || hashString.length() == 0) { + // if there is no hash string we only show this if the SDK is loaded. + // For a project opened at start-up with no target, this would be displayed + // twice, once when the project is opened, and once after the SDK has + // finished loading. + // By testing the sdk is loaded, we only show this once in the console. + if (sdkIsLoaded) { + markerMessage = String.format( + "Project has no target set. Edit the project properties to set one."); + } + } else if (sdkIsLoaded) { markerMessage = String.format( - "Unable to resolve target '%s' until the SDK is loaded.", - hashString); - - // let's not log this one to the console as it will happen at - // every boot, and it's expected. (we do keep the error marker though). - outputToConsole = false; - + "Unable to resolve target '%s'", hashString); } else { - // we created a container from the cache, so we register the project - // to be checked for cache validity once the SDK is loaded - plugin.setProjectToCheck(javaProject); - - // and return the container - return container; + // this is the case where there is a hashString but the SDK is not yet + // loaded and therefore we can't get the target yet. + // We check if there is a cache of the needed information. + AndroidClasspathContainer container = getContainerFromCache(iProject, + target); + + if (container == null) { + // either the cache was wrong (ie folder does not exists anymore), or + // there was no cache. In this case we need to make sure the project + // is resolved again after the SDK is loaded. + plugin.setProjectToResolve(javaProject); + + markerMessage = String.format( + "Unable to resolve target '%s' until the SDK is loaded.", + hashString); + + // let's not log this one to the console as it will happen at + // every boot, and it's expected. (we do keep the error marker though). + outputToConsole = false; + + } else { + // we created a container from the cache, so we register the project + // to be checked for cache validity once the SDK is loaded + plugin.setProjectToCheck(javaProject); + + // and return the container + return container; + } } } } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/BaseProjectHelper.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/BaseProjectHelper.java index a7db983..57632ea 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/BaseProjectHelper.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/BaseProjectHelper.java @@ -21,6 +21,7 @@ import com.android.annotations.NonNull; import com.android.annotations.Nullable; import com.android.ide.eclipse.adt.AdtConstants; import com.android.ide.eclipse.adt.AdtPlugin; +import com.google.common.collect.Lists; import org.eclipse.core.resources.IFolder; import org.eclipse.core.resources.IMarker; @@ -77,8 +78,9 @@ public final class BaseProjectHelper { * @param javaProject * @return a list of path relative to the workspace root. */ + @NonNull public static List<IPath> getSourceClasspaths(IJavaProject javaProject) { - ArrayList<IPath> sourceList = new ArrayList<IPath>(); + List<IPath> sourceList = Lists.newArrayList(); IClasspathEntry[] classpaths = javaProject.readRawClasspath(); if (classpaths != null) { for (IClasspathEntry e : classpaths) { @@ -87,6 +89,7 @@ public final class BaseProjectHelper { } } } + return sourceList; } @@ -507,6 +510,7 @@ public final class BaseProjectHelper { * @param project the {@link IProject} * @return an IFolder item or null. */ + @Nullable public final static IFolder getAndroidOutputFolder(IProject project) { try { if (project.isOpen() && project.hasNature(JavaCore.NATURE_ID)) { diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/ExportHelper.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/ExportHelper.java index 52870a4..8168590 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/ExportHelper.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/ExportHelper.java @@ -143,7 +143,9 @@ public final class ExportHelper { "No Build Tools installed in the SDK.")); } - BuildHelper helper = new BuildHelper(project, buildToolInfo, + BuildHelper helper = new BuildHelper( + projectState, + buildToolInfo, fakeStream, fakeStream, jumbo.booleanValue(), dexMerger.booleanValue(), diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/LibraryClasspathContainerInitializer.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/LibraryClasspathContainerInitializer.java index 3beb181..1e0ada0 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/LibraryClasspathContainerInitializer.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/LibraryClasspathContainerInitializer.java @@ -19,14 +19,17 @@ package com.android.ide.eclipse.adt.internal.project; import static com.android.ide.eclipse.adt.AdtConstants.CONTAINER_DEPENDENCIES; import com.android.SdkConstants; +import com.android.ide.common.sdk.LoadStatus; import com.android.ide.eclipse.adt.AdtConstants; import com.android.ide.eclipse.adt.AdtPlugin; import com.android.ide.eclipse.adt.AndroidPrintStream; 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.build.JarListSanitizer; import com.android.sdklib.build.JarListSanitizer.DifferentLibException; import com.android.sdklib.build.JarListSanitizer.Sha1Exception; +import com.android.sdklib.build.RenderScriptProcessor; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IFolder; @@ -306,60 +309,88 @@ public class LibraryClasspathContainerInitializer extends BaseClasspathContainer final Set<File> jarFiles = new HashSet<File>(); final IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot(); - // check if the project has a valid target. - final ProjectState state = Sdk.getProjectState(iProject); - if (state == null) { - // getProjectState should already have logged an error. Just bail out. + AdtPlugin plugin = AdtPlugin.getDefault(); + if (plugin == null) { // This is totally weird, but I've seen it happen! return null; } - // annotations support for older version of android - if (state.getTarget() != null && state.getTarget().getVersion().getApiLevel() <= 15) { - File annotationsJar = new File(Sdk.getCurrent().getSdkLocation(), - SdkConstants.FD_TOOLS + File.separator + SdkConstants.FD_SUPPORT + - File.separator + SdkConstants.FN_ANNOTATIONS_JAR); + synchronized (Sdk.getLock()) { + boolean sdkIsLoaded = plugin.getSdkLoadStatus() == LoadStatus.LOADED; - jarFiles.add(annotationsJar); - } + // check if the project has a valid target. + final ProjectState state = Sdk.getProjectState(iProject); + if (state == null) { + // getProjectState should already have logged an error. Just bail out. + return null; + } - // process all the libraries + // annotations support for older version of android + if (state.getTarget() != null && state.getTarget().getVersion().getApiLevel() <= 15) { + File annotationsJar = new File(Sdk.getCurrent().getSdkLocation(), + SdkConstants.FD_TOOLS + File.separator + SdkConstants.FD_SUPPORT + + File.separator + SdkConstants.FN_ANNOTATIONS_JAR); - List<IProject> libProjects = state.getFullLibraryProjects(); - for (IProject libProject : libProjects) { - // get the project output - IFolder outputFolder = BaseProjectHelper.getAndroidOutputFolder(libProject); - - if (outputFolder != null) { // can happen when closing/deleting a library) - IFile jarIFile = outputFolder.getFile(libProject.getName().toLowerCase() + - SdkConstants.DOT_JAR); - - // get the source folder for the library project - List<IPath> srcs = BaseProjectHelper.getSourceClasspaths(libProject); - // find the first non-derived source folder. - IPath sourceFolder = null; - for (IPath src : srcs) { - IFolder srcFolder = workspaceRoot.getFolder(src); - if (srcFolder.isDerived() == false) { - sourceFolder = src; - break; + jarFiles.add(annotationsJar); + } + + if (state.getRenderScriptSupportMode()) { + if (!sdkIsLoaded) { + return null; + } + BuildToolInfo buildToolInfo = state.getBuildToolInfo(); + if (buildToolInfo == null) { + buildToolInfo = Sdk.getCurrent().getLatestBuildTool(); + + if (buildToolInfo == null) { + return null; } + + File renderScriptSupportJar = RenderScriptProcessor.getSupportJar( + buildToolInfo.getLocation().getAbsolutePath()); + + jarFiles.add(renderScriptSupportJar); } + } - // we can directly add a CPE for this jar as there's no risk of a duplicate. - IClasspathEntry entry = JavaCore.newLibraryEntry( - jarIFile.getLocation(), - sourceFolder, // source attachment path - null, // default source attachment root path. - true /*isExported*/); + // process all the libraries + + List<IProject> libProjects = state.getFullLibraryProjects(); + for (IProject libProject : libProjects) { + // get the project output + IFolder outputFolder = BaseProjectHelper.getAndroidOutputFolder(libProject); + + if (outputFolder != null) { // can happen when closing/deleting a library) + IFile jarIFile = outputFolder.getFile(libProject.getName().toLowerCase() + + SdkConstants.DOT_JAR); + + // get the source folder for the library project + List<IPath> srcs = BaseProjectHelper.getSourceClasspaths(libProject); + // find the first non-derived source folder. + IPath sourceFolder = null; + for (IPath src : srcs) { + IFolder srcFolder = workspaceRoot.getFolder(src); + if (srcFolder.isDerived() == false) { + sourceFolder = src; + break; + } + } - entries.add(entry); + // we can directly add a CPE for this jar as there's no risk of a duplicate. + IClasspathEntry entry = JavaCore.newLibraryEntry( + jarIFile.getLocation(), + sourceFolder, // source attachment path + null, // default source attachment root path. + true /*isExported*/); + + entries.add(entry); + } } - } - entries.addAll(convertJarsToClasspathEntries(iProject, jarFiles)); + entries.addAll(convertJarsToClasspathEntries(iProject, jarFiles)); - return allocateContainer(javaProject, entries, new Path(CONTAINER_DEPENDENCIES), - "Android Dependencies"); + return allocateContainer(javaProject, entries, new Path(CONTAINER_DEPENDENCIES), + "Android Dependencies"); + } } private static IClasspathContainer allocateContainer(IJavaProject javaProject, diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/ProjectState.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/ProjectState.java index 4628509..74c9857 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/ProjectState.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/ProjectState.java @@ -259,6 +259,15 @@ public final class ProjectState { return mProperties.getProperty(ProjectProperties.PROPERTY_BUILD_TOOLS); } + public boolean getRenderScriptSupportMode() { + String supportModeValue = mProperties.getProperty(ProjectProperties.PROPERTY_RS_SUPPORT); + if (supportModeValue != null) { + return Boolean.parseBoolean(supportModeValue); + } + + return false; + } + public static class LibraryDifference { public boolean removed = false; public boolean added = false; diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/Sdk.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/Sdk.java index ca6cc57..c8faa5e 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/Sdk.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/Sdk.java @@ -1187,6 +1187,7 @@ public final class Sdk { // get the current target and build tools IAndroidTarget oldTarget = state.getTarget(); + boolean oldRsSupportMode = state.getRenderScriptSupportMode(); // get the current library flag boolean wasLibrary = state.isLibrary(); @@ -1216,7 +1217,8 @@ public final class Sdk { } // apply the new target if needed. - if (newTarget != oldTarget) { + if (newTarget != oldTarget || + oldRsSupportMode != state.getRenderScriptSupportMode()) { IJavaProject javaProject = BaseProjectHelper.getJavaProject( file.getProject()); if (javaProject != null) { |
