All Downloads are FREE. Search and download functionalities are using the official Maven repository.

hudson.maven.MavenBuild Maven / Gradle / Ivy

Go to download

This plug-in provides deep integration of Hudson and Maven. This functionality used to be part of the Hudson core. Now it is a plug-in that is installed by default, but can be disabled.

There is a newer version: 2.2.0
Show newest version
/*
 * The MIT License
 * 
 * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
package hudson.maven;

import hudson.FilePath;
import hudson.EnvVars;
import hudson.maven.reporters.SurefireArchiver;
import hudson.slaves.WorkspaceList;
import hudson.slaves.WorkspaceList.Lease;
import hudson.maven.agent.AbortException;
import hudson.model.BuildListener;
import hudson.model.Computer;
import hudson.model.Hudson;
import hudson.model.Result;
import hudson.model.Run;
import hudson.model.Environment;
import hudson.model.TaskListener;
import hudson.model.Node;
import hudson.model.Executor;
import hudson.remoting.Channel;
import hudson.scm.ChangeLogSet;
import hudson.scm.ChangeLogSet.Entry;
import hudson.tasks.BuildWrapper;
import hudson.tasks.Maven.MavenInstallation;
import hudson.util.ArgumentListBuilder;
import org.apache.maven.BuildFailureException;
import org.apache.maven.artifact.versioning.ComparableVersion;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.execution.ReactorManager;
import org.apache.maven.lifecycle.LifecycleExecutionException;
import org.apache.maven.monitor.event.EventDispatcher;
import org.apache.maven.project.MavenProject;
import org.kohsuke.stapler.Ancestor;
import org.kohsuke.stapler.Stapler;
import org.kohsuke.stapler.StaplerRequest;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * {@link Run} for {@link MavenModule}.
 * 
 * @author Kohsuke Kawaguchi
 */
public class MavenBuild extends AbstractMavenBuild {
    /**
     * {@link MavenReporter}s that will contribute project actions.
     * Can be null if there's none.
     */
    /*package*/ List projectActionReporters;

    /**
     * {@link ExecutedMojo}s that record what was run.
     * Null until some time before the build completes,
     * or if this build is performed in earlier versions of Hudson.
     * @since 1.98.
     */
    private List executedMojos;

    /**
     * Name of the slave this project was built on.
     * Null or "" if built by the master. (null happens when we read old record that didn't have this information.)
     * @since 1.394
     */
    private String builtOn;    
    
    public MavenBuild(MavenModule job) throws IOException {
        super(job);
    }

    public MavenBuild(MavenModule job, Calendar timestamp) {
        super(job, timestamp);
    }

    public MavenBuild(MavenModule project, File buildDir) throws IOException {
        super(project, buildDir);
        SurefireArchiver.fixUp(projectActionReporters);
    }

    @Override
    public String getUpUrl() {
        StaplerRequest req = Stapler.getCurrentRequest();
        if(req!=null) {
            List ancs = req.getAncestors();
            for( int i=1; i ancs = req.getAncestors();
            for( int i=1; i getChangeSet() {
        return new FilteredChangeLogSet(this);
    }

    /**
     * We always get the changeset from {@link MavenModuleSetBuild}.
     */
    @Override
    public boolean hasChangeSetComputed() {
        return true;
    }

    /**
     * Exposes {@code MAVEN_OPTS} to forked processes.
     *
     * 

* See {@link MavenModuleSetBuild#getEnvironment(TaskListener)} for discussion. */ @Override public EnvVars getEnvironment(TaskListener log) throws IOException, InterruptedException { EnvVars envs = super.getEnvironment(log); String opts = project.getParent().getMavenOpts(); if(opts!=null) envs.put("MAVEN_OPTS", opts); return envs; } public void registerAsProjectAction(MavenReporter reporter) { if(projectActionReporters==null) projectActionReporters = new ArrayList(); projectActionReporters.add(reporter); } public void registerAsProjectAction(MavenProjectActionBuilder builder) { if(projectActionReporters==null) projectActionReporters = new ArrayList(); projectActionReporters.add(builder); } public List getProjectActionBuilders() { if(projectActionReporters==null) return Collections.emptyList(); return Collections.unmodifiableList(projectActionReporters); } public List getExecutedMojos() { if(executedMojos==null) return Collections.emptyList(); else return Collections.unmodifiableList(executedMojos); } @Override public void run() { run(new RunnerImpl()); getProject().updateTransientActions(); MavenModuleSetBuild parentBuild = getModuleSetBuild(); if(parentBuild!=null) parentBuild.notifyModuleBuild(this); } /** * If the parent {@link MavenModuleSetBuild} is kept, keep this record, too. */ @Override public String getWhyKeepLog() { MavenModuleSetBuild pb = getParentBuild(); if(pb!=null && pb.getWhyKeepLog()!=null) return Messages.MavenBuild_KeptBecauseOfParent(pb); return super.getWhyKeepLog(); } // used by executedMojos.jelly public static ExecutedMojo.Cache createExecutedMojoCache() { return new ExecutedMojo.Cache(); } /** * Backdoor for {@link MavenModuleSetBuild} to assign workspaces for modules. */ @Override protected void setWorkspace(FilePath path) { super.setWorkspace(path); } /** * @see hudson.model.AbstractBuild#getBuiltOn() * @since 1.394 */ public Node getBuiltOn() { if(builtOn==null || builtOn.equals("")) return Hudson.getInstance(); else return Hudson.getInstance().getNode(builtOn); } /** * @param builtOn * @since 1.394 */ public void setBuiltOnStr( String builtOn ) { this.builtOn = builtOn; } /** * Runs Maven and builds the project. */ private static final class Builder extends MavenBuilder { private final MavenBuildProxy buildProxy; private final MavenReporter[] reporters; /** * Records of what was executed. */ private final List executedMojos = new ArrayList(); private long startTime; public Builder(BuildListener listener,MavenBuildProxy buildProxy,MavenReporter[] reporters, List goals, Map systemProps) { super(listener,goals,systemProps); this.buildProxy = new FilterImpl(buildProxy); this.reporters = reporters; } private class FilterImpl extends MavenBuildProxy.Filter implements Serializable { public FilterImpl(MavenBuildProxy buildProxy) { super(buildProxy); } @Override public void executeAsync(final BuildCallable program) throws IOException { futures.add(Channel.current().callAsync(new AsyncInvoker(core,program))); } public MavenBuildInformation getMavenBuildInformation() { return super.core.getMavenBuildInformation(); } private static final long serialVersionUID = 1L; } @Override void preBuild(MavenSession session, ReactorManager rm, EventDispatcher dispatcher) throws BuildFailureException, LifecycleExecutionException, IOException, InterruptedException { for (MavenReporter r : reporters) r.preBuild(buildProxy,rm.getTopLevelProject(),listener); } @Override void postBuild(MavenSession session, ReactorManager rm, EventDispatcher dispatcher) throws BuildFailureException, LifecycleExecutionException, IOException, InterruptedException { buildProxy.setExecutedMojos(executedMojos); for (MavenReporter r : reporters) r.postBuild(buildProxy,rm.getTopLevelProject(),listener); } @Override void preExecute(MavenProject project, MojoInfo info) throws IOException, InterruptedException, AbortException { for (MavenReporter r : reporters) if(!r.preExecute(buildProxy,project,info,listener)) throw new AbortException(r+" failed"); startTime = System.currentTimeMillis(); } @Override void postExecute(MavenProject project, MojoInfo info, Exception exception) throws IOException, InterruptedException, AbortException { executedMojos.add(new ExecutedMojo(info,System.currentTimeMillis()-startTime)); for (MavenReporter r : reporters) if(!r.postExecute(buildProxy,project,info,listener,exception)) throw new AbortException(r+" failed"); } @Override void onReportGenerated(MavenProject project, MavenReportInfo report) throws IOException, InterruptedException, AbortException { for (MavenReporter r : reporters) if(!r.reportGenerated(buildProxy,project,report,listener)) throw new AbortException(r+" failed"); } @Override void preModule(MavenProject project) throws InterruptedException, IOException, AbortException { for (MavenReporter r : reporters) if(!r.enterModule(buildProxy,project,listener)) throw new AbortException(r+" failed"); } @Override void postModule(MavenProject project) throws InterruptedException, IOException, AbortException { for (MavenReporter r : reporters) if(!r.leaveModule(buildProxy,project,listener)) throw new AbortException(r+" failed"); } private static final long serialVersionUID = 1L; } /** * {@link MavenBuildProxy} implementation. */ class ProxyImpl implements MavenBuildProxy, Serializable { public V execute(BuildCallable program) throws T, IOException, InterruptedException { return program.call(MavenBuild.this); } /** * This method is implemented by the remote proxy before the invocation * gets to this. So correct code shouldn't be invoking this method on the master ever. * * @deprecated * This helps IDE find coding mistakes when someone tries to call this method. */ public final void executeAsync(BuildCallable program) throws IOException { throw new AssertionError(); } public FilePath getRootDir() { return new FilePath(MavenBuild.this.getRootDir()); } public FilePath getProjectRootDir() { return new FilePath(MavenBuild.this.getParent().getRootDir()); } public FilePath getModuleSetRootDir() { return new FilePath(MavenBuild.this.getParent().getParent().getRootDir()); } public FilePath getArtifactsDir() { return new FilePath(MavenBuild.this.getArtifactsDir()); } public void setResult(Result result) { MavenBuild.this.setResult(result); } public Calendar getTimestamp() { return MavenBuild.this.getTimestamp(); } public long getMilliSecsSinceBuildStart() { return System.currentTimeMillis()-getTimestamp().getTimeInMillis(); } public boolean isArchivingDisabled() { return MavenBuild.this.getParent().getParent().isArchivingDisabled(); } public void registerAsProjectAction(MavenReporter reporter) { MavenBuild.this.registerAsProjectAction(reporter); } public void registerAsProjectAction(MavenProjectActionBuilder builder) { MavenBuild.this.registerAsProjectAction(builder); } public void registerAsAggregatedProjectAction(MavenReporter reporter) { MavenModuleSetBuild pb = getParentBuild(); if(pb!=null) pb.registerAsProjectAction(reporter); } public void setExecutedMojos(List executedMojos) { MavenBuild.this.executedMojos = executedMojos; } private Object writeReplace() { return Channel.current().export(MavenBuildProxy.class,this); } public MavenBuildInformation getMavenBuildInformation() { return new MavenBuildInformation( MavenBuild.this.getModuleSetBuild().getMavenVersionUsed()); } } public class ProxyImpl2 extends ProxyImpl implements MavenBuildProxy2 { private final SplittableBuildListener listener; long startTime; private final OutputStream log; private final MavenModuleSetBuild parentBuild; ProxyImpl2(MavenModuleSetBuild parentBuild,SplittableBuildListener listener) throws FileNotFoundException { this.parentBuild = parentBuild; this.listener = listener; log = new FileOutputStream(getLogFile()); // no buffering so that AJAX clients can see the log live } public void start() { onStartBuilding(); startTime = System.currentTimeMillis(); try { listener.setSideOutputStream(log); } catch (IOException e) { e.printStackTrace(); } } public void end() { if(result==null) setResult(Result.SUCCESS); onEndBuilding(); duration += System.currentTimeMillis()- startTime; parentBuild.notifyModuleBuild(MavenBuild.this); try { listener.setSideOutputStream(null); save(); } catch (IOException e) { e.printStackTrace(); } } /** * Sends the accumuldated log in {@link SplittableBuildListener} to the log of this build. */ public void appendLastLog() { try { listener.setSideOutputStream(log); listener.setSideOutputStream(null); } catch (IOException e) { e.printStackTrace(); } } /** * Performs final clean up. Invoked after the entire aggregator build is completed. */ protected void close() { try { log.close(); } catch (IOException e) { e.printStackTrace(); } if(hasntStartedYet()) { // Mark the build as aborted. This method is used when the aggregated build // failed before it didn't even get to this module. run(new Runner() { public Result run(BuildListener listener) { listener.getLogger().println(Messages.MavenBuild_FailedEarlier()); return Result.NOT_BUILT; } public void post(BuildListener listener) { } public void cleanUp(BuildListener listener) { } }); } } /** * Gets the build for which this proxy is created. */ public MavenBuild owner() { return MavenBuild.this; } private Object writeReplace() { // when called from remote, methods need to be executed in the proper Executor's context. return Channel.current().export(MavenBuildProxy2.class, Executor.currentExecutor().newImpersonatingProxy(MavenBuildProxy2.class,this)); } } private class RunnerImpl extends AbstractRunner { private List reporters; @Override protected Lease decideWorkspace(Node n, WorkspaceList wsl) throws InterruptedException, IOException { return wsl.allocate(getModuleSetBuild().getModuleRoot().child(getProject().getRelativePath())); } protected Result doRun(BuildListener listener) throws Exception { // pick up a list of reporters to run reporters = getProject().createReporters(); MavenModuleSet mms = getProject().getParent(); if(debug) listener.getLogger().println("Reporters="+reporters); for (BuildWrapper w : mms.getBuildWrappersList()) { Environment e = w.setUp(MavenBuild.this, launcher, listener); if (e == null) { return Result.FAILURE; } buildEnvironments.add(e); } EnvVars envVars = getEnvironment(listener); // buildEnvironments should be set up first MavenInstallation mvn = getProject().getParent().getMaven(); mvn = mvn.forEnvironment(envVars).forNode(Computer.currentComputer().getNode(), listener); MavenInformation mavenInformation = getModuleRoot().act( new MavenVersionCallable( mvn.getHome() )); String mavenVersion = mavenInformation.getVersion(); listener.getLogger().println("Found mavenVersion " + mavenVersion + " from file " + mavenInformation.getVersionResourcePath()); ProcessCache.MavenProcess process = null; boolean maven3orLater = new ComparableVersion (mavenVersion).compareTo( new ComparableVersion ("3.0") ) >= 0; if ( maven3orLater ) { process = MavenBuild.mavenProcessCache.get( launcher.getChannel(), listener, new Maven3ProcessFactory( getParent().getParent(), launcher, envVars, null ) ); } else { process = MavenBuild.mavenProcessCache.get( launcher.getChannel(), listener, new MavenProcessFactory( getParent().getParent(), launcher, envVars, null ) ); } ArgumentListBuilder margs = new ArgumentListBuilder("-N","-B"); if(mms.usesPrivateRepository()) // use the per-project repository. should it be per-module? But that would cost too much in terms of disk // the workspace must be on this node, so getRemote() is safe. margs.add("-Dmaven.repo.local="+getWorkspace().child(".repository").getRemote()); margs.add("-f",getModuleRoot().child("pom.xml").getRemote()); margs.addTokenized(getProject().getGoals()); Map systemProps = new HashMap(envVars); // backward compatibility systemProps.put("hudson.build.number",String.valueOf(getNumber())); boolean normalExit = false; if (maven3orLater) { // FIXME here for maven 3 builds return Result.ABORTED; } else { try { Result r = process.call(new Builder( listener,new ProxyImpl(), reporters.toArray(new MavenReporter[reporters.size()]), margs.toList(), systemProps)); normalExit = true; return r; } finally { if(normalExit) process.recycle(); else process.discard(); // tear down in reverse order boolean failed=false; for( int i=buildEnvironments.size()-1; i>=0; i-- ) { if (!buildEnvironments.get(i).tearDown(MavenBuild.this,listener)) { failed=true; } } // WARNING The return in the finally clause will trump any return before if (failed) return Result.FAILURE; } } } public void post2(BuildListener listener) throws Exception { for (MavenReporter reporter : reporters) reporter.end(MavenBuild.this,launcher,listener); } } private static final int MAX_PROCESS_CACHE = 5; protected static final ProcessCache mavenProcessCache = new ProcessCache(MAX_PROCESS_CACHE); /** * Set true to produce debug output. */ public static boolean debug = false; @Override public MavenModule getParent() {// don't know why, but javac wants this return super.getParent(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy