hudson.plugins.cmake.CmakeBuilder Maven / Gradle / Ivy
package hudson.plugins.cmake;
import hudson.EnvVars;
import hudson.Extension;
import hudson.FilePath;
import hudson.Launcher;
import hudson.Proc;
import hudson.Util;
import hudson.matrix.MatrixProject;
import hudson.model.AbstractBuild;
import hudson.model.AbstractProject;
import hudson.model.BuildListener;
import hudson.model.FreeStyleProject;
import hudson.model.Node;
import hudson.model.TaskListener;
import hudson.tasks.Builder;
import hudson.tasks.BuildStepDescriptor;
import hudson.util.FormValidation;
import net.sf.json.JSONObject;
import org.kohsuke.stapler.AncestorInPath;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.QueryParameter;
import org.kohsuke.stapler.StaplerRequest;
import javax.servlet.ServletException;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
/**
* Executes cmake as the build process.
*
*
* @author Volker Kaiser
*/
public class CmakeBuilder extends Builder {
private static final String CMAKE = "cmake";
private String sourceDir;
private String buildDir;
private String installDir;
private String buildType;
private String otherBuildType;
private String generator;
private String makeCommand;
private String installCommand;
private String preloadScript;
private String cmakeArgs;
private boolean cleanBuild;
private CmakeBuilderImpl builderImpl;
@DataBoundConstructor
public CmakeBuilder(String sourceDir,
String buildDir,
String installDir,
String buildType,
String otherBuildType,
boolean cleanBuild,
String generator,
String makeCommand,
String installCommand,
String preloadScript,
String cmakeArgs) {
this.sourceDir = sourceDir;
this.buildDir = buildDir;
this.installDir = installDir;
this.buildType = buildType;
this.otherBuildType = otherBuildType;
this.cleanBuild = cleanBuild;
this.generator = generator;
this.makeCommand = makeCommand;
this.installCommand = installCommand;
this.cmakeArgs = cmakeArgs;
this.preloadScript = preloadScript;
builderImpl = new CmakeBuilderImpl();
}
public String getSourceDir() {
return this.sourceDir;
}
public String getBuildDir() {
return this.buildDir;
}
public String getInstallDir() {
return this.installDir;
}
public String getBuildType() {
return this.buildType;
}
public String getOtherBuildType() {
return this.otherBuildType;
}
public boolean getCleanBuild() {
return this.cleanBuild;
}
public String getGenerator() {
return this.generator;
}
public String getMakeCommand() {
return this.makeCommand;
}
public String getInstallCommand() {
return this.installCommand;
}
public String getPreloadScript() {
return this.preloadScript;
}
public String getCmakeArgs() {
return this.cmakeArgs;
}
public boolean perform(AbstractBuild build, Launcher launcher, BuildListener listener) throws InterruptedException, IOException {
listener.getLogger().println("MODULE: " + build.getModuleRoot());
if (builderImpl == null) {
builderImpl = new CmakeBuilderImpl();
}
EnvVars envs = build.getEnvironment(listener);
final FilePath workSpace = build.getProject().getWorkspace();
String theSourceDir;
String theInstallDir;
String theBuildDir = this.buildDir;
try {
if (this.cleanBuild) {
listener.getLogger().println("Cleaning Build Dir... " + this.buildDir);
theBuildDir = builderImpl.preparePath(workSpace, envs, this.buildDir,
CmakeBuilderImpl.PreparePathOptions.CREATE_NEW_IF_EXISTS);
} else {
theBuildDir = builderImpl.preparePath(workSpace, envs, this.buildDir,
CmakeBuilderImpl.PreparePathOptions.CREATE_IF_NOT_EXISTING);
}
theSourceDir = builderImpl.preparePath(workSpace, envs, this.sourceDir,
CmakeBuilderImpl.PreparePathOptions.CHECK_PATH_EXISTS);
theInstallDir = builderImpl.preparePath(workSpace, envs, this.installDir,
CmakeBuilderImpl.PreparePathOptions.CREATE_NEW_IF_EXISTS);
} catch (IOException ioe) {
listener.getLogger().println(ioe.getMessage());
return false;
}
String theBuildType = this.buildType;
if (this.otherBuildType.length() > 0) {
theBuildType = this.otherBuildType;
}
listener.getLogger().println("Build dir : " + theBuildDir.toString());
listener.getLogger().println("Source dir : " + theSourceDir.toString());
listener.getLogger().println("Install dir : " + theInstallDir.toString());
String cmakeBin = checkCmake(build.getBuiltOn(), listener);
String cmakeCall = builderImpl.buildCMakeCall(cmakeBin, this.generator, this.preloadScript, theSourceDir, theInstallDir, theBuildType, cmakeArgs);
listener.getLogger().println("CMake call : " + cmakeCall);
try {
int result = launcher.launch(cmakeCall, envs,
listener.getLogger(), new FilePath(workSpace, theBuildDir)).join();
if (result != 0) {
return false;
}
if (!getMakeCommand().trim().isEmpty()) {
result = launcher.launch(getMakeCommand(), envs,
listener.getLogger(), new FilePath(workSpace, theBuildDir)).join();
if (result != 0) {
return false;
}
}
final boolean doInstall =
!theInstallDir.isEmpty() && !getInstallCommand().trim().isEmpty();
if (doInstall) {
result = launcher.launch(getInstallCommand(), envs,
listener.getLogger(), new FilePath(workSpace, theBuildDir)).join();
}
return (result == 0);
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException ie) {
ie.printStackTrace();
}
return false;
}
private String checkCmake(Node node, BuildListener listener) throws IOException,
InterruptedException {
String cmakeBin = CMAKE;
String cmakePath = getDescriptor().cmakePath();
if (cmakePath != null && cmakePath.length() > 0) {
cmakeBin = cmakePath;
}
Proc proc = node.createLauncher(listener).launch(cmakeBin + " -version",
new HashMap(), listener.getLogger(), node.getRootPath());
proc.join();
return cmakeBin;
}
@Override
public DescriptorImpl getDescriptor() {
return (DescriptorImpl) super.getDescriptor();
}
/**
* Descriptor for {@link CmakeBuilder}. Used as a singleton.
* The class is marked as public so that it can be accessed from views.
*
*
* See views/hudson/plugins/hello_world/CmakeBuilder/*.jelly
* for the actual HTML fragment for the configuration screen.
*/
@Extension
public static final class DescriptorImpl extends BuildStepDescriptor {
/**
* To persist global configuration information,
* simply store it in a field and call save().
*
*
* If you don't want fields to be persisted, use transient.
*/
private String cmakePath;
private transient List allowedBuildTypes;
private transient String errorMessage;
public DescriptorImpl() {
super(CmakeBuilder.class);
load();
this.allowedBuildTypes = new ArrayList();
this.allowedBuildTypes.add("Debug");
this.allowedBuildTypes.add("Release");
this.allowedBuildTypes.add("RelWithDebInfo");
this.allowedBuildTypes.add("MinSizeRel");
this.errorMessage = "Must be one of Debug, Release, RelWithDebInfo, MinSizeRel";
}
public FormValidation doCheckSourceDir(@AncestorInPath AbstractProject project, @QueryParameter final String value) throws IOException, ServletException {
FilePath ws = project.getSomeWorkspace();
if(ws==null) return FormValidation.ok();
return ws.validateRelativePath(value,true,false);
}
/**
* Performs on-the-fly validation of the form field 'name'.
*
* @param value
*/
public FormValidation doCheckBuildDir(@QueryParameter final String value) throws IOException, ServletException {
if(value.length()==0)
return FormValidation.error("Please set a build directory");
if(value.length() < 1)
return FormValidation.warning("Isn't the name too short?");
File file = new File(value);
if (file.isFile())
return FormValidation.error("build dir is a file");
//TODO add more checks
return FormValidation.ok();
}
/**
* Performs on-the-fly validation of the form field 'buildType'.
*
* @param value
*/
public FormValidation doCheckBuildType(@QueryParameter final String value) throws IOException, ServletException {
for (String allowed : DescriptorImpl.this.allowedBuildTypes)
if (value.equals(allowed))
return FormValidation.ok();
if (value.length() > 0)
return FormValidation.error(DescriptorImpl.this.errorMessage);
return FormValidation.ok();
}
/**
* Performs on-the-fly validation of the form field 'makeCommand'.
*
* @param value
*/
public FormValidation doCheckMakeCommand(@QueryParameter final String value) throws IOException, ServletException {
if (value.length() == 0) {
return FormValidation.error("Please set make command");
}
return FormValidation.validateExecutable(value);
}
/**
* This human readable name is used in the configuration screen.
*/
public String getDisplayName() {
return "CMake Build";
}
@Override
public boolean configure(StaplerRequest req, JSONObject o) throws FormException {
// to persist global configuration information,
// set that to properties and call save().
cmakePath = o.getString("cmakePath");
save();
return super.configure(req, o);
}
public String cmakePath() {
return cmakePath;
}
@Override
public Builder newInstance(StaplerRequest req, JSONObject formData) throws FormException {
return req.bindJSON(CmakeBuilder.class, formData);
}
@Override
public boolean isApplicable(Class extends AbstractProject> jobType) {
return FreeStyleProject.class.isAssignableFrom(jobType)
|| MatrixProject.class.isAssignableFrom(jobType);
}
}
}