Skip to content
Permalink
Browse files

add initial pipeline steps

  • Loading branch information...
15knots committed Mar 11, 2018
1 parent c631576 commit d7166a478d070adefea058ce08d18834ec80dfed
@@ -0,0 +1,131 @@
/*
* The MIT License
*
* Copyright 2018 Martin Weber
*/
package hudson.plugins.cmake;

import java.io.Serializable;
import java.util.Set;

import org.jenkinsci.plugins.workflow.steps.Step;
import org.jenkinsci.plugins.workflow.steps.StepContext;
import org.jenkinsci.plugins.workflow.steps.StepDescriptor;
import org.jenkinsci.plugins.workflow.steps.StepExecution;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.DataBoundSetter;

import com.google.common.collect.ImmutableSet;

import hudson.EnvVars;
import hudson.FilePath;
import hudson.Launcher;
import hudson.Util;
import hudson.model.Node;
import hudson.model.TaskListener;
import hudson.util.ListBoxModel;

/**
* A Step that holds information about a cmake installation, the working
* directory for invocation and the arguments to pass to {@code cmake},<br>
* NOTE: Actually, this class is NOT abstract, but we want to re-use the
* {@code @DataBoundSetter} methods defined here.
*
* @author Martin weber
*/
public class AbstractStep extends Step implements Serializable {
private static final long serialVersionUID = 1L;

/** the name of the cmake tool installation to use for this build step */
private String installation;
private String workingDir;
private String toolArgs;

/**
* Minimal constructor.
*
* @param installation
* the name of the cmake tool installation from the global config
* page.
*/
@DataBoundConstructor
public AbstractStep(String installation) {
this.installation = Util.fixEmptyAndTrim(installation);
}

/** Gets the name of the cmake installation to use for this build step */
public String getInstallation() {
return this.installation;
}

@DataBoundSetter
public void setWorkingDir(String workingDir) {
this.workingDir = Util.fixEmptyAndTrim(workingDir);
}

public String getWorkingDir() {
return this.workingDir;
}

@DataBoundSetter
public void setArguments(String toolArgs) {
this.toolArgs = Util.fixEmptyAndTrim(toolArgs);
}

public String getArguments() {
return this.toolArgs;
}

/**
* This should be overwritten. It is only implemented because of compile
* error "@DataBoundConstructor may not be used on an abstract class"
*/
@Override
public StepExecution start(StepContext context) throws Exception {
throw new UnsupportedOperationException(
"Implemnetors MUST overwrite this in their sub-class");
}

/**
* Gets the basename of the command to run (cmake, cpack or ctest).
*
* @return {@code "cmake"}
*/
protected String getCommandBasename() {
return "cmake";
}

/**
* Finds the cmake tool installation to use for this build among all
* installations configured in the Jenkins administration
*
* @return selected CMake installation or {@code null} if none could be
* found
*/
protected CmakeTool getSelectedInstallation() {
return InstallationUtils.getInstallationByName(installation);
}

// //////////////////////////////////////////////////////////////////
// inner classes
// //////////////////////////////////////////////////////////////////
/**
* Descriptor for {@link AbstractStep}. Used as a singleton. The class
* is marked as public so that it can be accessed from views.
*/
public static abstract class DescriptorImpl extends StepDescriptor {

@Override
public Set<? extends Class<?>> getRequiredContext() {
return ImmutableSet.of(TaskListener.class, Launcher.class, EnvVars.class,
Node.class, FilePath.class);
}

/**
* Determines the values of the Cmake installation drop-down list box.
*/
public ListBoxModel doFillInstallationItems() {
return InstallationUtils.doFillInstallationNameItems();
}
}
}
@@ -0,0 +1,199 @@
/*
* The MIT License
*
* Copyright 2018 Martin Weber
*/
package hudson.plugins.cmake;

import java.io.IOException;
import java.util.Iterator;

import javax.servlet.ServletException;

import org.jenkinsci.plugins.workflow.steps.StepContext;
import org.jenkinsci.plugins.workflow.steps.StepExecution;
import org.jenkinsci.plugins.workflow.steps.SynchronousNonBlockingStepExecution;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.DataBoundSetter;
import org.kohsuke.stapler.QueryParameter;

import hudson.AbortException;
import hudson.EnvVars;
import hudson.FilePath;
import hudson.Launcher;
import hudson.Util;
import hudson.model.Node;
import hudson.model.TaskListener;
import hudson.util.ArgumentListBuilder;
import hudson.util.FormValidation;

/**
* Provides a pipeline build step that allows to invoke selected tools of the
* cmake-suite ({@code cmake}, {@code cpack} and {@code ctest}) with arbitrary
* arguments.<br>
* Similar to {@code CToolBuilder}, but pipeline compatible.<br>
* NOTE: Actually, this class is NOT abstract, but we want to re-use the
* {@code @DataBoundSetter} methods defined here.
*
* @author Martin Weber
*/
public class AbstractToolStep extends AbstractStep {
private static final long serialVersionUID = 1L;

/**
* Exit codes of the tool that indicate a failure but should be ignored,
* thus causing the build to proceed.<br>
*/
private String ignoredExitCodes;

/**
* Parsed and cached exit codes to ignore.
*/
private transient IntSet ignoredExitCodesParsed;

/**
* Minimal constructor.
*
* @param installation
* the name of the cmake tool installation from the global config
* page.
*/
@DataBoundConstructor
public AbstractToolStep(String installation) {
super(installation);
}

/**
* Gets the exit codes of the tool that indicate a failure but should be
* ignored, thus causing the build to proceed.
*
* @return the ignoredExitCodes property value or <code>null</code>
*/
public String getIgnoredExitCodes() {
return ignoredExitCodes;
}

@DataBoundSetter
public void setIgnoredExitCodes(String ignoredExitCodes) {
this.ignoredExitCodes = Util.fixEmptyAndTrim(ignoredExitCodes);
}

/*
* (non-Javadoc)
*
* @see
* org.jenkinsci.plugins.workflow.steps.Step#start(org.jenkinsci.plugins.
* workflow.steps.StepContext)
*/
@Override
public StepExecution start(StepContext context) throws Exception {
return new Execution(this, context);
}

// //////////////////////////////////////////////////////////////////
// inner classes
// //////////////////////////////////////////////////////////////////
private static class Execution
extends SynchronousNonBlockingStepExecution<Integer> {
private static final long serialVersionUID = 1L;

private final AbstractToolStep step;

Execution(AbstractToolStep step, StepContext context) {
super(context);
this.step = step;
}

/**
* @see org.jenkinsci.plugins.workflow.steps.SynchronousNonBlockingStepExecution#run()
*
* @return the exit code of the cmake/cpack/ctest process
*/
@Override
protected Integer run() throws Exception {
final StepContext context = getContext();
final TaskListener listener = context.get(TaskListener.class);
final Launcher launcher = context.get(Launcher.class);
final Node node = context.get(Node.class);

CmakeTool installToUse = step.getSelectedInstallation();
// Raise an error if the cmake installation isn't found
if (installToUse == null) {
throw new AbortException(
"There is no CMake installation selected."
+ " Please review the build step configuration"
+ " and make sure it is configured on the Global Tool Configuration page.");
}

// Get the CMake version for this node, installing it if necessary
installToUse = installToUse.forNode(node, listener)
.forEnvironment(context.get(EnvVars.class));
final String bindir = installToUse.getBindir();

/* Determine remote working directory path. Create it. */
final FilePath workSpace = context.get(FilePath.class);
final String workDir = step.getWorkingDir();
final FilePath theWorkDir = LaunchUtils.makeRemotePath(workSpace,
workDir);
if (workDir != null) {
theWorkDir.mkdirs();
}

/* Invoke tool in working dir */
ArgumentListBuilder cmakeCall = LaunchUtils.buildCommandline(
bindir + step.getCommandBasename(), step.getArguments());
final int exitCode;
if (0 == (exitCode = launcher.launch().pwd(theWorkDir)
.stdout(listener).cmds(cmakeCall).join())) {
return Integer.valueOf(exitCode);
}
// should this failure be ignored?
if (step.getIgnoredExitCodes() != null) {
if (step.ignoredExitCodesParsed == null) {
// parse and cache
final IntSet ints = new IntSet();
ints.setValues(step.ignoredExitCodes);
step.ignoredExitCodesParsed = ints;
}
for (Iterator<Integer> iter = step.ignoredExitCodesParsed
.iterator(); iter.hasNext();) {
if (exitCode == iter.next()) {
// ignore this failure exit code
listener.getLogger().printf(
"%1s exited with failure code %2$s, ignored.%n",
step.getCommandBasename(), exitCode);
return Integer.valueOf(exitCode); // no failure
}
}
// invocation failed, not ignored
}
// invocation failed
throw new AbortException(
String.format("%1s exited with failure code %2$s%n",
step.getCommandBasename(), exitCode));
}
} // Execution

protected abstract static class DescriptorImpl
extends AbstractStep.DescriptorImpl {

/**
* Performs on-the-fly validation of the form field 'ignoredExitCodes'.
*
* @param value
*/
@Restricted(NoExternalUse.class) // Only for UI calls
public FormValidation doCheckIgnoredExitCodes(
@QueryParameter final String value)
throws IOException, ServletException {
try {
new IntSet().setValues(Util.fixEmptyAndTrim(value));
} catch (IllegalArgumentException iae) {
return FormValidation.error(iae.getLocalizedMessage());
}
return FormValidation.ok();
}
}
}
@@ -0,0 +1,52 @@
/*
* The MIT License
*
* Copyright 2018 Martin Weber
*/
package hudson.plugins.cmake;

import org.kohsuke.stapler.DataBoundConstructor;

import hudson.Extension;

/**
* Provides a pipeline build step that allows to invoke {@code cmake} with
* arbitrary arguments.<br>
* Similar to {@code CToolBuilder}, but pipeline compatible.
*
* @author Martin Weber
*/
public class CMakeStep extends AbstractToolStep {
private static final long serialVersionUID = 1L;

/**
* Minimal constructor.
*
* @param installation
* the name of the cmake tool installation from the global config
* page.
*/
@DataBoundConstructor
public CMakeStep(String installation) {
super(installation);
}

@Extension(optional = true)
public static class DescriptorImpl
extends AbstractToolStep.DescriptorImpl {

@Override
public String getFunctionName() {
return "cmake";
}

/**
* This human readable name is used in the configuration screen.
*/
@Override
public String getDisplayName() {
return "Run cmake with arbitrary arguments";
}

}
}
Oops, something went wrong.

0 comments on commit d7166a4

Please sign in to comment.
You can’t perform that action at this time.