/******************************************************************************* * * Copyright (c) 2004-2009 Oracle Corporation. * * 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: * * Kohsuke Kawaguchi, Martin Eigenbrodt * * *******************************************************************************/ package hudson.model; import hudson.tasks.BuildStep; import hudson.tasks.BuildWrapper; import hudson.tasks.Builder; import hudson.tasks.Recorder; import hudson.tasks.Notifier; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Calendar; import java.util.Collection; import java.util.List; import java.util.logging.Logger; import static hudson.model.Result.FAILURE; import static hudson.model.Result.ABORTED; /** * A build of a {@link Project}. * * <h2>Steps of a build</h2> <p> Roughly speaking, a {@link Build} goes through * the following stages: * * <dl> <dt>SCM checkout <dd>Hudson decides which directory to use for a build, * then the source code is checked out * * <dt>Pre-build steps <dd>Everyone gets their * {@link BuildStep#prebuild(AbstractBuild, BuildListener)} invoked to indicate * that the build is starting * * <dt>Build wrapper set up * <dd>{@link BuildWrapper#setUp(AbstractBuild, Launcher, BuildListener)} is * invoked. This is normally to prepare an environment for the build. * * <dt>Builder runs * <dd>{@link Builder#perform(AbstractBuild, Launcher, BuildListener)} is * invoked. This is where things that are useful to users happen, like calling * Ant, Make, etc. * * <dt>Recorder runs * <dd>{@link Recorder#perform(AbstractBuild, Launcher, BuildListener)} is * invoked. This is normally to record the output from the build, such as test * results. * * <dt>Notifier runs * <dd>{@link Notifier#perform(AbstractBuild, Launcher, BuildListener)} is * invoked. This is normally to send out notifications, based on the results * determined so far. </dl> * * <p> And beyond that, the build is considered complete, and from then on * {@link Build} object is there to keep the record of what happened in this * build. * * @author Kohsuke Kawaguchi */ public abstract class Build<P extends BaseBuildableProject<P, B>, B extends Build<P, B>> extends AbstractBuild<P, B> { /** * Creates a new build. */ protected Build(P project) throws IOException { super(project); } protected Build(P job, Calendar timestamp) { super(job, timestamp); } /** * Loads a build from a log file. */ protected Build(P project, File buildDir) throws IOException { super(project, buildDir); } // // // actions // // @Override public void run() { run(createRunner()); } protected Runner createRunner() { return new RunnerImpl(); } protected class RunnerImpl extends AbstractRunner { protected Result doRun(BuildListener listener) throws Exception { if (!preBuild(listener, project.getBuilders())) { return FAILURE; } if (!preBuild(listener, project.getPublishers())) { return FAILURE; } Result r = null; try { List<BuildWrapper> wrappers = new ArrayList<BuildWrapper>(project.getBuildWrappers().values()); ParametersAction parameters = getAction(ParametersAction.class); if (parameters != null) { parameters.createBuildWrappers(Build.this, wrappers); } for (BuildWrapper w : wrappers) { Environment e = w.setUp((AbstractBuild<?, ?>) Build.this, launcher, listener); if (e == null) { return (r = FAILURE); } buildEnvironments.add(e); } if (!build(listener, project.getBuilders())) { r = FAILURE; } } catch (InterruptedException e) { r = ABORTED; throw e; } finally { if (r != null) { setResult(r); } // tear down in reverse order boolean failed = false; for (int i = buildEnvironments.size() - 1; i >= 0; i--) { if (!buildEnvironments.get(i).tearDown(Build.this, listener)) { failed = true; } } // WARNING The return in the finally clause will trump any return before if (failed) { return FAILURE; } } return r; } public void post2(BuildListener listener) throws IOException, InterruptedException { if (!performAllBuildSteps(listener, project.getPublishers(), true)) { setResult(FAILURE); } if (!performAllBuildSteps(listener, project.getProperties(), true)) { setResult(FAILURE); } } @Override public void cleanUp(BuildListener listener) throws Exception { // at this point it's too late to mark the build as a failure, so ignore return value. performAllBuildSteps(listener, project.getPublishers(), false); performAllBuildSteps(listener, project.getProperties(), false); super.cleanUp(listener); } private boolean build(BuildListener listener, Collection<Builder> steps) throws IOException, InterruptedException { for (BuildStep bs : steps) { if (!perform(bs, listener)) { return false; } } return true; } } private static final Logger LOGGER = Logger.getLogger(Build.class.getName()); }