/******************************************************************************* * Copyright (c) 2004, 2006 * Thomas Hallgren, Kenneth Olwing, Mitch Sonies * Pontus Rydin, Nils Unden, Peer Torngren * The code, documentation and other materials contained herein have been * licensed under the Eclipse Public License - v 1.0 by the individual * copyright holders listed above, as Initial Contributors under such license. * The text of such license is available at www.eclipse.org. *******************************************************************************/ package org.eclipse.buckminster.core.commands; import java.io.File; import java.io.PrintStream; import java.util.Arrays; import java.util.Comparator; import java.util.List; import org.eclipse.buckminster.cmdline.Option; import org.eclipse.buckminster.cmdline.OptionDescriptor; import org.eclipse.buckminster.cmdline.OptionValueType; import org.eclipse.buckminster.cmdline.UsageException; import org.eclipse.buckminster.core.Messages; import org.eclipse.buckminster.core.TargetPlatform; import org.eclipse.buckminster.core.metadata.WorkspaceInfo; import org.eclipse.buckminster.runtime.MonitorUtils; import org.eclipse.core.resources.IMarker; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IWorkspace; import org.eclipse.core.resources.IWorkspaceRoot; import org.eclipse.core.resources.IncrementalProjectBuilder; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; public class Build extends WorkspaceCommand { static private final OptionDescriptor cleanDescriptor = new OptionDescriptor('c', "clean", OptionValueType.NONE); //$NON-NLS-1$ static private final OptionDescriptor thoroughDescriptor = new OptionDescriptor('t', "thorough", OptionValueType.NONE); //$NON-NLS-1$ static private final OptionDescriptor logfileDescriptor = new OptionDescriptor('l', "logfile", OptionValueType.REQUIRED); //$NON-NLS-1$ static private final OptionDescriptor continueOnErrorDescriptor = new OptionDescriptor('C', "continueonerror", OptionValueType.NONE); //$NON-NLS-1$ private static final int MAX_INCREMENTAL_RETRY_COUNT = 3; private static final int SUCCEEDED = 0; private static final int FAILED = 1; public static IMarker[] build(IProgressMonitor monitor, boolean clean) throws Exception { return build(monitor, clean, false); } public static IMarker[] build(IProgressMonitor monitor, boolean clean, boolean thorough) throws Exception { IWorkspace ws = ResourcesPlugin.getWorkspace(); IWorkspaceRoot wsRoot = ws.getRoot(); IProject[] projs = wsRoot.getProjects(); try { monitor.beginTask(null, projs.length * (clean ? 9 : 7)); // Ensure that the workspace is in sync // wsRoot.refreshLocal(IResource.DEPTH_INFINITE, MonitorUtils.subMonitor(monitor, projs.length)); if (clean || thorough) // // Clean first if requested // ws.build(IncrementalProjectBuilder.CLEAN_BUILD, MonitorUtils.subMonitor(monitor, projs.length * 2)); if (thorough) TargetPlatform.getInstance().refresh(); ws.build(IncrementalProjectBuilder.FULL_BUILD, MonitorUtils.subMonitor(monitor, projs.length * 5)); // Some errors are caused by circular dependencies in the build // hierarchy. They might be // fixed by additional incremental builds so we make // MAX_INCREMENTAL_RETRY_COUNT attempts // before we report errors. // IMarker[] markers; int top; for (int retries = 0;; ++retries) { // Get all problem markers. Sort them by timestamp // markers = wsRoot.findMarkers(IMarker.PROBLEM, true, IResource.DEPTH_INFINITE); top = markers.length; if (top == 0 || retries >= MAX_INCREMENTAL_RETRY_COUNT) break; boolean retryNeeded = false; for (int idx = 0; idx < top; ++idx) { if (markers[idx].getAttribute(IMarker.SEVERITY, IMarker.SEVERITY_INFO) != IMarker.SEVERITY_ERROR) continue; retryNeeded = true; break; } if (!retryNeeded) // // No errors spotted, we're happy. // break; // Build incrementally and then obtain a new set of markers // int buildType = (thorough && retries == 0) ? IncrementalProjectBuilder.FULL_BUILD : IncrementalProjectBuilder.INCREMENTAL_BUILD; ws.build(buildType, MonitorUtils.subMonitor(monitor, projs.length)); } Arrays.sort(markers, new Comparator<IMarker>() { @Override public int compare(IMarker a, IMarker b) { try { long diff = a.getCreationTime() - b.getCreationTime(); return diff > 0 ? 1 : (diff < 0 ? -1 : 0); } catch (CoreException e) { return 0; } } }); try { // Refresh workspace info since the build might have affected // cspec generation. When executing with a script, the next // perform will run with the same generated meta-data. WorkspaceInfo.forceRefreshOnAll(MonitorUtils.subMonitor(monitor, projs.length)); } catch (Exception e) { // ignore } return markers; } finally { monitor.done(); } } public static String formatMarkerMessage(String type, IMarker problem) { StringBuilder bld = new StringBuilder(); bld.append(type); bld.append(": file "); //$NON-NLS-1$ bld.append(problem.getResource().getLocation().toOSString()); int line = problem.getAttribute(IMarker.LINE_NUMBER, -1); if (line > 0) { bld.append(", line "); //$NON-NLS-1$ bld.append(line); } bld.append(": "); //$NON-NLS-1$ bld.append(problem.getAttribute(IMarker.MESSAGE, "")); //$NON-NLS-1$ return bld.toString(); } // set if a clean build is requested // private boolean clean = false; private boolean thorough = false; private File logFile = null; private boolean continueOnError = false; @Override protected void getOptionDescriptors(List<OptionDescriptor> appendHere) throws Exception { appendHere.add(cleanDescriptor); appendHere.add(thoroughDescriptor); appendHere.add(continueOnErrorDescriptor); appendHere.add(logfileDescriptor); super.getOptionDescriptors(appendHere); } @Override protected void handleOption(Option option) throws Exception { if (option.is(cleanDescriptor)) clean = true; else if (option.is(thoroughDescriptor)) thorough = true; else if (option.is(continueOnErrorDescriptor)) continueOnError = true; else if (option.is(logfileDescriptor)) logFile = new File(option.getValue()); else super.handleOption(option); } @Override protected void handleUnparsed(String[] unparsed) throws Exception { if (unparsed.length > 0) throw new UsageException(Messages.Too_many_arguments); } @Override protected int internalRun(IProgressMonitor monitor) throws Exception { long start = System.currentTimeMillis(); IMarker[] problems = build(monitor, clean, thorough); long seconds = (System.currentTimeMillis() - start) / 1000; PrintStream log = null; if (logFile != null) log = new PrintStream(logFile); try { int errors = 0; int warnings = 0; int infos = 0; for (IMarker problem : problems) { PrintStream console = null; String message = null; switch (problem.getAttribute(IMarker.SEVERITY, IMarker.SEVERITY_INFO)) { case IMarker.SEVERITY_ERROR: ++errors; console = System.err; message = formatMarkerMessage("Error", problem); //$NON-NLS-1$ break; case IMarker.SEVERITY_WARNING: ++warnings; console = System.err; message = formatMarkerMessage("Warning", problem); //$NON-NLS-1$ break; case IMarker.SEVERITY_INFO: ++infos; console = System.out; message = formatMarkerMessage("Info", problem); //$NON-NLS-1$ } if (message != null) { if (console != null) console.println(message); if (log != null) log.println(message); } } System.out.println("Errors: " + errors); //$NON-NLS-1$ System.out.println("Warnings: " + warnings); //$NON-NLS-1$ System.out.println("Infos: " + infos); //$NON-NLS-1$ int exitValue = errors == 0 ? SUCCEEDED : FAILED; System.out.println("Build " + (exitValue == FAILED ? "failed" : "succeeded") + " after " + seconds + " seconds."); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ if (exitValue == FAILED && continueOnError) System.out.println("Build continues anyway..."); //$NON-NLS-1$ return continueOnError ? SUCCEEDED : exitValue; } finally { if (log != null) log.close(); } } }