package hudson.plugins.buckminster;
import hudson.CopyOnWrite;
import hudson.Extension;
import hudson.FilePath;
import hudson.Launcher;
import hudson.Proc;
import hudson.matrix.MatrixProject;
import hudson.model.AbstractBuild;
import hudson.model.AbstractProject;
import hudson.model.BuildListener;
import hudson.model.Computer;
import hudson.model.Describable;
import hudson.model.Descriptor;
import hudson.model.FreeStyleProject;
import hudson.model.Hudson;
import hudson.plugins.buckminster.command.CommandLineBuilder;
import hudson.plugins.buckminster.targetPlatform.NoTargetPlatformReference;
import hudson.plugins.buckminster.targetPlatform.TargetPlatformPublisher;
import hudson.plugins.buckminster.targetPlatform.TargetPlatformReference;
import hudson.tasks.BuildStepDescriptor;
import hudson.tasks.Builder;
import hudson.tasks.Publisher;
import hudson.tools.ToolProperty;
import hudson.util.DescribableList;
import hudson.util.FormValidation;
import java.io.File;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.servlet.ServletException;
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 org.kohsuke.stapler.StaplerResponse;
/**
* Build Step that invokes Eclipse Buckminster.
*
* <p>
* When the user configures the project and enables this builder,
* {@link DescriptorImpl#newInstance(StaplerRequest)} is invoked and a new
* {@link EclipseBuckminsterBuilder} is created. The created instance is
* persisted to the project configuration XML by using XStream, so this allows
* you to use instance fields (like {@link #name}) to remember the
* configuration.
*
* <p>
* When a build is performed, the
* {@link #perform(AbstractBuild, Launcher, BuildListener)} method will be invoked.
*
* @author Johannes Utzig
*/
public class EclipseBuckminsterBuilder extends Builder {
private final String installationName, commands, logLevel, params, targetPlatformName, userTemp, userOutput, userCommand, userWorkspace;
@DataBoundConstructor
public EclipseBuckminsterBuilder(String installationName, String commands, String logLevel, String params, String targetPlatformName, String userTemp, String userOutput, String userCommand, String userWorkspace) {
this.installationName = installationName;
this.commands = commands;
this.logLevel = logLevel;
this.params = params;
this.targetPlatformName = targetPlatformName;
this.userTemp = userTemp;
this.userOutput = userOutput;
this.userCommand = userCommand;
this.userWorkspace = userWorkspace;
}
/**
* We'll use this from the <tt>config.jelly</tt>.
*/
public String getEclipseHome() {
return getInstallation().getHome();
}
public String getCommands() {
return commands;
}
public String getParams() {
return params;
}
public String getUserWorkspace() {
return userWorkspace;
}
public String getLogLevel(){
if(logLevel!=null && logLevel.length()!=0)
return logLevel;
return "info";
}
public boolean isSelected(String item){
return getLogLevel().equals(item);
}
public BuckminsterInstallation getInstallation() {
for (BuckminsterInstallation si : DESCRIPTOR.getBuckminsterInstallations()) {
if (installationName != null
&& si.getName().equals(installationName)) {
return si;
}
}
return null;
}
public String getUserTemp() {
return userTemp;
}
public String getUserOutput() {
return userOutput;
}
public String getUserCommand() {
return userCommand;
}
public TargetPlatformReference getTargetPlatform() {
for (TargetPlatformReference reference : DESCRIPTOR.getTargetPlatforms()) {
if (targetPlatformName != null
&& reference.getName().equals(targetPlatformName)) {
return reference;
}
}
return null;
}
@Override
public boolean perform(AbstractBuild<?,?> build, Launcher launcher,
BuildListener listener) {
try {
BuckminsterInstallation installation = getInstallation();
if(installation==null)
{
installation = pickDefault(listener);
if(installation==null)
{
listener.error("No Buckminster Tool Installation has been configured.");
return false;
}
}
installation = installation.forNode(Computer.currentComputer().getNode(), listener);
installation = installation.forEnvironment(build.getEnvironment(listener));
String modifiedCommands = getCommands();
TargetPlatformReference targetPlatform = getTargetPlatform();
if(targetPlatform!=null && targetPlatform.getPath()!=null){
modifiedCommands = "setpref targetPlatformPath=\""+targetPlatform.getPath()+"\"" +"\n" + modifiedCommands;
}
CommandLineBuilder cmdBuilder = new CommandLineBuilder(installation,modifiedCommands,getLogLevel(),getParams(),getUserWorkspace(),getUserTemp(),getUserOutput(), getUserCommand());
List<String> buildCommands = cmdBuilder.buildCommands(build,listener, launcher);
Proc proc = launcher.launch().envs(build.getEnvironment(listener)).pwd(build.getWorkspace()).cmds(buildCommands).stdout(listener).start();
return proc.join()==0;
}
catch (Exception e) {
listener.error(e.getLocalizedMessage());
return false;
}
}
private BuckminsterInstallation pickDefault(BuildListener listener) {
BuckminsterInstallation[] available = DESCRIPTOR.getBuckminsterInstallations();
if(available==null || available.length==0)
return null;
String message = "The selected Buckminster Tool Installation \"{0}\" has not been found, using \"{1}\" instead!";
message = MessageFormat.format(message, installationName,available[0].getName());
listener.error(message);
return available[0];
}
/**
* Descriptor should be singleton.
*/
@Extension
public static final DescriptorImpl DESCRIPTOR = new DescriptorImpl();
/**
* Descriptor for {@link EclipseBuckminsterBuilder}. Used as a singleton. The class
* is marked as public so that it can be accessed from views.
*/
@SuppressWarnings("deprecation")
public static final class DescriptorImpl extends BuildStepDescriptor<Builder> {
@CopyOnWrite
private volatile BuckminsterInstallation[] buckminsterInstallations = new BuckminsterInstallation[0];
@CopyOnWrite
private volatile EclipseInstallation[] installations = new EclipseInstallation[0];
DescriptorImpl() {
super(EclipseBuckminsterBuilder.class);
load();
}
/**
* Asks hudson for all available {@link AbstractProject}s and iterates over their {@link PublisherList}.
* <p>
* If a {@link TargetPlatformPublisher} is found and it offers a {@link TargetPlatformReference}, that reference is added to the result list.
*
* @return a list of all {@link TargetPlatformReference}s published by a {@link TargetPlatformPublisher}
*/
@SuppressWarnings("unchecked")
public List<TargetPlatformReference> getTargetPlatforms() {
List<AbstractProject> projects = Hudson.getInstance().getAllItems(AbstractProject.class);
List<TargetPlatformReference> references = new ArrayList<TargetPlatformReference>();
references.add(new NoTargetPlatformReference());
for (AbstractProject<?,?> project : projects) {
DescribableList<Publisher,Descriptor<Publisher>> publishersList = project.getPublishersList();
for (Describable<?> describable : publishersList) {
if (describable instanceof TargetPlatformPublisher) {
TargetPlatformPublisher publisher = (TargetPlatformPublisher) describable;
TargetPlatformReference reference = publisher.getTargetPlatformReference(project);
if(reference!=null)
{
references.add(reference);
}
}
}
}
return references;
}
public EclipseInstallation[] getInstallations() {
return installations;
}
public BuckminsterInstallation[] getBuckminsterInstallations() {
if(installations!=null && installations.length>0)
{
//convert the old installations and reset the installations field
List<BuckminsterInstallation> converted = convertLegacyInstallations(installations);
if(buckminsterInstallations!=null && buckminsterInstallations.length>0)
{
Collections.addAll(converted, buckminsterInstallations);
}
buckminsterInstallations = converted.toArray(new BuckminsterInstallation[converted.size()]);
installations = new EclipseInstallation[0];
}
return buckminsterInstallations;
}
/**
* performs on-thy-fly validation of the eclipse home path field.
* @param req the request
* @param rsp the response
* @return one of {@link FormValidation#error(String)} or {@link FormValidation#ok()}
* @throws IOException
* @throws ServletException
*/
public FormValidation doCheckEclipseHome(StaplerRequest req, StaplerResponse rsp)
throws IOException, ServletException {
final String value = req.getParameter("value");
File eclipseHome = new File(value);
if (!eclipseHome.exists()) {
String error = "The path {0} does not exist";
error = MessageFormat.format(error, value);
return FormValidation.error(error);
} else if (!eclipseHome.isDirectory()) {
String error = "{0} is not a directory";
error = MessageFormat.format(error, value);
return FormValidation.error(error);
} else if (!new File(eclipseHome.getAbsolutePath()
+ "/plugins").exists()) {
String error = "{0} does not contain a plugins directory";
error = MessageFormat.format(error, value);
return FormValidation.error(error);
}
return FormValidation.ok();
}
public FormValidation doCheckEclipseVersion(StaplerRequest req,
StaplerResponse rsp) throws IOException, ServletException {
final String version = req.getParameter("value");
if (!(version.startsWith("3.4") || version.startsWith("3.5"))) {
return FormValidation.error("Eclipse version is not valid. Currently only 3.4.x and 3.5 are supported.");
}
return FormValidation.ok();
}
/**
* Performs on-the-fly validation on the file mask wildcard.
*/
public FormValidation doCheckUserCommand(@AncestorInPath AbstractProject<?,?> project, @QueryParameter String value) throws IOException {
FilePath path = project.getSomeWorkspace();
if(path==null)
//we tried, but we couldn't get a workspace
return FormValidation.ok();
return path.validateRelativePath(value, true, true);
}
/**
* This human readable name is used in the configuration screen.
*/
public String getDisplayName() {
return "Run Buckminster";
}
@Override
public boolean configure(StaplerRequest req, JSONObject o)
throws FormException {
save();
return super.configure(req, o);
}
private List<BuckminsterInstallation> convertLegacyInstallations(
EclipseInstallation[] installations) {
List<BuckminsterInstallation> convertedInstallations = new ArrayList<BuckminsterInstallation>(installations.length);
for (EclipseInstallation eclipseInstallation : installations) {
BuckminsterInstallation inst = new BuckminsterInstallation(eclipseInstallation.getName(), eclipseInstallation.getHome(), eclipseInstallation.getVersion(), eclipseInstallation.getParams(), Collections.<ToolProperty<?>>emptyList());
convertedInstallations.add(inst);
}
return convertedInstallations;
}
@Override
public Builder newInstance(StaplerRequest req, JSONObject formData)
throws hudson.model.Descriptor.FormException {
return req.bindJSON(EclipseBuckminsterBuilder.class,
formData);
}
@SuppressWarnings("unchecked")
@Override
public boolean isApplicable(Class<? extends AbstractProject> jobType) {
return FreeStyleProject.class.isAssignableFrom(jobType) || MatrixProject.class.isAssignableFrom(jobType);
}
public void setBuckminsterInstallations(
BuckminsterInstallation... installations) {
this.buckminsterInstallations = installations;
save();
}
}
}