package com.google.dart.tools.core.pub; import com.google.dart.engine.sdk.DirectoryBasedDartSdk; 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.dart2js.ProcessRunner; import com.google.dart.tools.core.model.DartSdkManager; import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IFolder; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IncrementalProjectBuilder; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.core.runtime.preferences.IEclipsePreferences; import org.eclipse.osgi.util.NLS; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; /** * Runs the pub operation as an external process. The operation can be scheduled via the {@link Job} * infrastructure or executed directly by calling {@link #run(IProgressMonitor)} or * {@link #runSilent(IProgressMonitor)} if this operation should not display output in the console. * * @coverage dart.tools.core.pub */ public class RunPubJob extends Job { public static final String UPDATE_COMMAND = "upgrade"; //$NON-NLS-1$ public static final String INSTALL_COMMAND = "get"; //$NON-NLS-1$ public static final String INSTALL_OFFLINE_COMMAND = "get --offline"; //$NON-NLS-1$ public static final String PUBLISH_COMMAND = "publish"; //$NON-NLS-1$ public static final String BUILD_DEBUG_COMMAND = "build --mode debug"; //$NON-NLS-1$ public static final String BUILD_COMMAND = "build"; //$NON-NLS-1$ /** * The pub command to be run (e.g. "install", "update") */ private final String command; /** * The directory in which the pub command will be run */ private final IContainer container; /** * Indicates whether this pub job was triggered by the builder, or packages view etc. instead of * by a menu option. */ private final boolean autorun; /** * The directory which contains the sources to build, used only for pub build command */ private final IContainer sourceFolder; /** * Construct a new job for running a pub command * * @param container the directory in which the cmd will be run (not {@code null}) * @param command the command to be run... either {@link #INSTALL_COMMAND} or * {@link #UPDATE_COMMAND} * @param autorun indicate whether pub was run by editor without user interaction */ public RunPubJob(IContainer container, String command, boolean autorun) { this(container, command, autorun, null); } /** * Construct a new job for running a pub command * * @param container the directory in which the cmd will be run (not {@code null}) * @param command the command to be run... either {@link #INSTALL_COMMAND} or * {@link #UPDATE_COMMAND} * @param autorun indicate whether pub was run by editor without user interaction * @param the folder containing the sources to build, used only for pub build commands */ public RunPubJob(IContainer container, String command, boolean autorun, IContainer sourceFolder) { super(NLS.bind(PubMessages.RunPubJob_name, command)); this.command = command; this.container = container; this.autorun = autorun; this.sourceFolder = sourceFolder; // TODO(keertip): comment out for now, on windows pub install takes long time and blocks builder // setRule(container); } /** * Runs the pub command and displays the output in the console. * * @return the result of running the pub command */ @Override public IStatus run(IProgressMonitor monitor) { MessageConsole console = DartCore.getConsole(); String path = container.getLocation().toOSString(); if (autorun) { console.printSeparator(NLS.bind(PubMessages.RunPubJob_auto_running, command, path)); } else { console.printSeparator(NLS.bind(PubMessages.RunPubJob_running, command, path)); } IStatus status = runSilent(monitor); console.println(status.getMessage()); return status; } /** * Runs the pub command. * * @return the result of running the pub command */ public IStatus runSilent(IProgressMonitor monitor) { ProcessRunner runner = null; try { // Build the process description to run pub DirectoryBasedDartSdk sdk = DartSdkManager.getManager().getSdk(); File pubFile = sdk.getPubExecutable(); ProcessBuilder builder = new ProcessBuilder(); builder.directory(DartCore.getApplicationDirectory(container).getLocation().toFile()); builder.redirectErrorStream(true); List<String> args = new ArrayList<String>(); args.add(pubFile.getAbsolutePath()); if (DartCoreDebug.NO_PUB_PACKAGES) { args.add("--no-package-symlinks"); } // Add command. addArgs(command, args); // Add global args. IEclipsePreferences preferences = DartCore.getPlugin().getPrefs(); if (preferences != null) { String pubArgs = preferences.get(DartCore.PUB_GLOBAL_ARGS_PREFERENCE, null); addArgs(pubArgs, args); } // add folder for pub build if (command.equals(BUILD_COMMAND) || command.equals(BUILD_DEBUG_COMMAND)) { if (sourceFolder != null) { String name = sourceFolder.getFullPath().removeFirstSegments( container.getFullPath().segmentCount()).toString(); args.add(name); } else { args.add("--all"); } } builder.command(args); // Run the pub command as an external process. runner = newProcessRunner(builder); try { runner.runSync(monitor); } catch (IOException e) { String message = NLS.bind(PubMessages.RunPubJob_failed, command, e.toString()); return new Status(IStatus.CANCEL, DartCore.PLUGIN_ID, message, e); } StringBuilder stringBuilder = new StringBuilder(); if (!runner.getStdOut().isEmpty()) { stringBuilder.append(runner.getStdOut().trim() + "\n"); //$NON-NLS-1$ } int exitCode = runner.getExitCode(); if (exitCode != 0) { String output = "[" + exitCode + "] " + stringBuilder.toString(); String message = NLS.bind(PubMessages.RunPubJob_failed, command, output); if (command.equals(INSTALL_COMMAND)) { message += "\n** Warning: Application may fail to run since packages did not get installed." + "Try running pub get again. **"; } return new Status(IStatus.ERROR, DartCore.PLUGIN_ID, message); } try { // Refresh the Eclipse resources container.refreshLocal(IResource.DEPTH_INFINITE, monitor); // run build.dart after pub updates - do a full build if (command.equals(UPDATE_COMMAND)) { container.getProject().build(IncrementalProjectBuilder.FULL_BUILD, monitor); } // Disable analysis of the build directory generated by pub build if (RunPubJob.BUILD_COMMAND.equals(command)) { setDerived(monitor); } } catch (CoreException e) { // Log the exception and move on DartCore.logError("Exception refreshing " + container, e); } catch (IOException e) { DartCore.logError("Exception adding to ignores " + container, e); } return new Status(IStatus.OK, DartCore.PLUGIN_ID, stringBuilder.toString()); } catch (OperationCanceledException exception) { String message = NLS.bind(PubMessages.RunPubJob_canceled, command); return new Status(IStatus.CANCEL, DartCore.PLUGIN_ID, message, exception); } finally { if (runner != null) { runner.dispose(); } monitor.done(); } } /** * Answer the {@link ProcessRunner} used to execute the pub operation. This is overridden when * testing this class to prevent from actually running pub. * * @param builder the process description (not {@code null}) * @return the process runner (not {@code null}) */ protected ProcessRunner newProcessRunner(ProcessBuilder builder) { return new ProcessRunner(builder); } private void addArgs(String cmd, List<String> args) { if (cmd != null) { if (cmd.contains(" ")) { String[] strings = cmd.split(" "); args.addAll(Arrays.asList(strings)); } else { args.add(cmd); } } } private void setDerived(IProgressMonitor monitor) throws IOException { IFolder buildDir = container.getFolder(new Path("build")); try { ((IResource) buildDir).setDerived(true, monitor); DartCore.addToIgnores(buildDir); } catch (CoreException e) { DartCore.logError("Failed to set derived flag: " + buildDir, e); } } }