| /* |
| * Copyright (c) 2012, the Dart project authors. |
| * |
| * Licensed under the Eclipse Public License v1.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/legal/epl-v10.html |
| * |
| * 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.google.dart.tools.core.pub; |
| |
| import com.google.common.annotations.VisibleForTesting; |
| import com.google.dart.tools.core.DartCore; |
| import com.google.dart.tools.core.DartCoreDebug; |
| import com.google.dart.tools.core.MessageConsole; |
| import com.google.dart.tools.core.analysis.model.PubFolder; |
| import com.google.dart.tools.core.builder.BuildEvent; |
| import com.google.dart.tools.core.builder.BuildParticipant; |
| import com.google.dart.tools.core.builder.BuildVisitor; |
| import com.google.dart.tools.core.builder.CleanEvent; |
| import com.google.dart.tools.core.internal.builder.DartBuilder; |
| import com.google.dart.tools.core.utilities.yaml.PubYamlUtils; |
| |
| import org.eclipse.core.resources.IContainer; |
| import org.eclipse.core.resources.IFile; |
| import org.eclipse.core.resources.IProject; |
| import org.eclipse.core.resources.IResource; |
| import org.eclipse.core.resources.IResourceDelta; |
| import org.eclipse.core.resources.IResourceProxy; |
| import org.eclipse.core.resources.IWorkspaceRoot; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.Path; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.util.HashSet; |
| import java.util.Map; |
| |
| /** |
| * This build participant has a higher priority and should be called by {@link DartBuilder} before |
| * the dart project is analyzed or build.dart is run. It will run pub install on any pubspec file |
| * that has been added or changed. |
| * |
| * @coverage dart.tools.core.pub |
| */ |
| public class PubBuildParticipant implements BuildParticipant, BuildVisitor { |
| |
| /** |
| * Flag indicating whether the sources are being reanalyzed and pub does NOT need to be run. |
| */ |
| private boolean reanalyze = false; |
| |
| /** |
| * The set of containers on which pub is currently running. Synchronize against this collection |
| * before accessing it. |
| */ |
| private static final HashSet<IContainer> currentContainers = new HashSet<IContainer>(); |
| |
| @VisibleForTesting |
| public static boolean isPubContainersEmpty() { |
| return currentContainers.isEmpty(); |
| } |
| |
| @Override |
| public void build(BuildEvent event, IProgressMonitor monitor) throws CoreException { |
| if (reanalyze) { |
| reanalyze = false; |
| } else { |
| event.traverse(this, false); |
| } |
| } |
| |
| @Override |
| public void clean(CleanEvent event, IProgressMonitor monitor) { |
| reanalyze = true; |
| } |
| |
| /** |
| * Find the pubspec associated with the specified resource, and if necessary run pub install |
| */ |
| public void runPubFor(IResource res, IProgressMonitor monitor) { |
| if (res == null) { |
| return; |
| } |
| IWorkspaceRoot root = res.getWorkspace().getRoot(); |
| IContainer container = res.getType() == IResource.FILE ? res.getParent() : (IContainer) res; |
| while (container != root) { |
| IFile pubFile = container.getFile(new Path(DartCore.PUBSPEC_FILE_NAME)); |
| if (pubFile.exists()) { |
| runPub(container, monitor); |
| return; |
| } |
| container = container.getParent(); |
| } |
| } |
| |
| @Override |
| public boolean visit(IResourceDelta delta, IProgressMonitor monitor) { |
| final IResource resource = delta.getResource(); |
| |
| if (resource.getType() == IResource.FILE) { |
| if (delta.getKind() == IResourceDelta.CHANGED) { |
| if (resource.getName().equals(DartCore.PUBSPEC_FILE_NAME)) { |
| runPub(resource.getParent(), monitor); |
| processPubspecContents(resource, resource.getProject(), monitor); |
| IResource lockFile = resource.getParent().findMember(DartCore.PUBSPEC_LOCK_FILE_NAME); |
| if (lockFile != null) { |
| processLockFileContents(lockFile, resource.getProject(), monitor); |
| } |
| } |
| if (resource.getName().equals(DartCore.PUBSPEC_LOCK_FILE_NAME)) { |
| processLockFileContents(resource, resource.getProject(), monitor); |
| } |
| } |
| } |
| |
| return true; |
| } |
| |
| @Override |
| public boolean visit(IResourceProxy proxy, IProgressMonitor monitor) { |
| if (proxy.getType() == IResource.FILE) { |
| if (proxy.getName().equals(DartCore.PUBSPEC_FILE_NAME)) { |
| runPub(proxy.requestResource().getParent(), monitor); |
| processPubspecContents( |
| proxy.requestResource(), |
| proxy.requestResource().getProject(), |
| monitor); |
| IResource lockFile = proxy.requestResource().getParent().findMember( |
| DartCore.PUBSPEC_LOCK_FILE_NAME); |
| if (lockFile != null) { |
| processLockFileContents(lockFile, proxy.requestResource().getProject(), monitor); |
| } |
| } |
| if (proxy.getName().equals(DartCore.PUBSPEC_LOCK_FILE_NAME)) { |
| processLockFileContents( |
| proxy.requestResource(), |
| proxy.requestResource().getProject(), |
| monitor); |
| } |
| } |
| |
| return true; |
| } |
| |
| /** |
| * Process the lockfile to extract the version information, and save the information in the |
| * resource property DartCore.PUB_PACKAGE_VERSION |
| * |
| * @param lockFile the pubspec.lock file |
| * @param project containing the pubspec.lock file |
| * @param monitor the progress monitor |
| */ |
| protected void processLockFileContents(IResource lockFile, IProject project, |
| IProgressMonitor monitor) { |
| |
| Map<String, String> versionMap = PubYamlUtils.getPackageVersionMap(lockFile); |
| if (versionMap != null && !versionMap.isEmpty()) { |
| for (String key : versionMap.keySet()) { |
| IResource folder = lockFile.getParent().findMember( |
| DartCore.PACKAGES_DIRECTORY_NAME + "/" + key); |
| if (folder != null) { |
| try { |
| folder.setPersistentProperty(DartCore.PUB_PACKAGE_VERSION, versionMap.get(key)); |
| } catch (CoreException e) { |
| DartCore.logError(e); |
| } |
| } |
| } |
| if (DartCoreDebug.ENABLE_ANALYSIS_SERVER) { |
| PubCacheManager_NEW.getInstance().updatePackagesList(0); |
| } else { |
| PubCacheManager_OLD.getInstance().updatePackagesList(0, versionMap); |
| } |
| PubManager.getInstance().notifyListeners(lockFile.getParent()); |
| } |
| } |
| |
| /** |
| * Process the pubspec file to extract name and dependencies and save in model (DartProjectImpl) |
| * |
| * @param pubspec the pubspec.yaml file |
| * @param project IProject project for the pubspec file |
| * @param monitor the progress monitor |
| */ |
| protected void processPubspecContents(IResource pubspec, IProject project, |
| IProgressMonitor monitor) { |
| |
| if (!DartCoreDebug.ENABLE_ANALYSIS_SERVER) { |
| try { |
| PubFolder pubFolder = DartCore.getProjectManager().getPubFolder(pubspec); |
| if (pubFolder != null) { |
| pubFolder.invalidatePubspec(); |
| } |
| } catch (CoreException e) { |
| DartCore.logError(e); |
| } catch (IOException e) { |
| DartCore.logError(e); |
| } |
| } |
| |
| } |
| |
| /** |
| * Execute the pub operation. This is overridden when testing this class to record the intent to |
| * run pub but prevent actually running pub. |
| * |
| * @param container the working directory in which pub should be run (not <code>null</code>) |
| * @param monitor the progress monitor (not <code>null</code>) |
| */ |
| protected void runPub(IContainer container, final IProgressMonitor monitor) { |
| |
| // If pub is already running for this container, then wait until it finishes before returning |
| synchronized (currentContainers) { |
| if (!currentContainers.add(container)) { |
| while (currentContainers.contains(container)) { |
| try { |
| currentContainers.wait(1000); |
| } catch (InterruptedException e) { |
| //$FALL-THROUGH$ |
| } |
| } |
| return; |
| } |
| } |
| |
| try { |
| // Only run pub automatically if it is not already up to date |
| File dir = container.getLocation().toFile(); |
| File pubFile = new File(dir, DartCore.PUBSPEC_FILE_NAME); |
| File lockFile = new File(dir, DartCore.PUBSPEC_LOCK_FILE_NAME); |
| File packagesDir = new File(dir, DartCore.PACKAGES_DIRECTORY_NAME); |
| if ((DartCoreDebug.NO_PUB_PACKAGES || packagesDir.exists()) && lockFile.exists() |
| && lockFile.lastModified() >= pubFile.lastModified()) { |
| return; |
| } |
| |
| // Run pub or notify the user that it needs to be run |
| if (DartCore.getPlugin().isAutoRunPubEnabled()) { |
| new RunPubJob(container, RunPubJob.INSTALL_COMMAND, true).run(monitor); |
| } else { |
| MessageConsole console = DartCore.getConsole(); |
| console.printSeparator(""); |
| console.println("Run Tools > Pub Get to install packages"); |
| } |
| |
| } finally { |
| // Ensure that currentContainers is updated |
| synchronized (currentContainers) { |
| currentContainers.remove(container); |
| currentContainers.notifyAll(); |
| } |
| } |
| } |
| } |