blob: d7dd1889d1cc9ef0d97e86d2035f8cf201917ae0 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2017 SSI Schaefer IT Solutions GmbH and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* SSI Schaefer IT Solutions GmbH
*******************************************************************************/
package org.eclipse.tea.library.build.util;
import java.util.Collection;
import org.eclipse.core.resources.IBuildConfiguration;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IncrementalProjectBuilder;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.MultiStatus;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Status;
import org.eclipse.tea.core.services.TaskProgressTracker;
import org.eclipse.tea.core.services.TaskingLog;
import org.eclipse.tea.library.build.chain.TeaBuildProjectElement;
import org.eclipse.tea.library.build.config.TeaBuildConfig;
import org.eclipse.tea.library.build.internal.Activator;
import org.eclipse.tea.library.build.model.WorkspaceData;
/**
* Utility method collection for the build library.
*/
public class TeaBuildUtil {
private static final MultiStatus DEF_STATUS = new MultiStatus(Activator.PLUGIN_ID, IStatus.ERROR, "", null);
public static void tryCompile(Collection<IProject> projects) {
// Step 1: find all active configurations for all projects
IBuildConfiguration[] toBuild = projects.stream().map(TeaBuildUtil::getActiveConfigSafe)
.toArray(IBuildConfiguration[]::new);
// Step 2: tell the workspace to build
try {
ResourcesPlugin.getWorkspace().build(toBuild, IncrementalProjectBuilder.FULL_BUILD, false, null);
} catch (CoreException e) {
// internal error
throw new RuntimeException(e);
}
}
public static IBuildConfiguration getActiveConfigSafe(IProject project) {
try {
return project.getActiveBuildConfig();
} catch (CoreException e) {
throw new RuntimeException(e);
}
}
/**
* Compile a single {@link TeaBuildProjectElement}'s {@link IProject}.
* Retries according to {@link TeaBuildConfig}.
*
* @param log
* log output destination
* @param tracker
* {@link TaskProgressTracker} to provide cancellation support
* @param element
* {@link TeaBuildProjectElement} to compile
* @param config
* {@link TeaBuildConfig} determining retry behavior
* @return the resulting {@link IStatus} of the operation.
*/
public static IStatus tryCompile(TaskingLog log, TaskProgressTracker tracker, TeaBuildProjectElement element,
TeaBuildConfig config) {
IProject project = element.getProject();
boolean isBinary = WorkspaceData.isBinaryProject(project);
String logTxt = (isBinary ? " binProject" : "compileProject") + ": " + element.getName();
Exception compilerException = null;
MultiStatus status = DEF_STATUS;
/*
* Compiling a project may need several attempts since the Eclipse
* builder is a little stupid.
*/
for (int pass = 0; pass < config.compileRetries; ++pass) {
if (tracker.isCanceled()) {
throw new OperationCanceledException();
}
try {
if (pass == 0) {
log.info(logTxt);
// first try: the conventional way
project.build(IncrementalProjectBuilder.FULL_BUILD, null);
} else {
if (isBinary) {
break; // never recompile
}
log.info(logTxt + ": recompile " + Integer.toString(pass));
// subsequent attempts: cleanup a little first
project.close(null);
project.open(null);
project.refreshLocal(IResource.DEPTH_INFINITE, null);
project.build(IncrementalProjectBuilder.CLEAN_BUILD, null);
project.build(IncrementalProjectBuilder.FULL_BUILD, null);
}
compilerException = null;
status = getStatus(element);
if (status.getSeverity() <= IStatus.WARNING) {
break;
}
if (pass == 0 && status.getSeverity() > IStatus.WARNING) {
// In case of an error in pass 0, there is a probability for
// the errors to
// disappear by just triggering another full build (without
// clean!). Causes
// can be compiler confusion and dependencies between
// generated artifacts.
log.info(logTxt + ": fast recompile");
project.build(IncrementalProjectBuilder.FULL_BUILD, null);
status = getStatus(element);
if (status.getSeverity() > IStatus.WARNING) {
for (IStatus s : status.getChildren()) {
if (s.getSeverity() > IStatus.WARNING) {
log.debug(s.getMessage());
}
}
}
}
} catch (Exception e) {
compilerException = e;
status = new MultiStatus(Activator.PLUGIN_ID, IStatus.ERROR,
"Exception during build of " + element.getName(), e);
log.info(logTxt + ": got exception: " + e.toString());
}
}
if (compilerException != null) {
return new Status(IStatus.ERROR, Activator.PLUGIN_ID, logTxt + ": project can't be compiled",
compilerException);
}
return status;
}
public static MultiStatus getStatus(TeaBuildProjectElement element) {
MultiStatus status = new MultiStatus(Activator.PLUGIN_ID, IStatus.OK, element.getName(), null);
element.getStatus().forEach(status::add);
return status;
}
}