package hudson.plugins.msbuild; import hudson.CopyOnWrite; import hudson.Extension; import hudson.Launcher; import hudson.Util; import hudson.model.AbstractBuild; import hudson.model.BuildListener; import hudson.model.Descriptor; import hudson.tasks.Builder; import hudson.util.ArgumentListBuilder; import java.io.File; import java.io.IOException; import java.util.Map; import net.sf.json.JSONObject; import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.StaplerRequest; /** * Sample {@link Builder}. * * <p> * When the user configures the project and enables this builder, * {@link DescriptorImpl#newInstance(StaplerRequest)} is invoked * and a new {@link MsBuildBuilder} 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(Build, Launcher, BuildListener)} method * will be invoked. * * @author kyle.sweeney@valtech.com * 2009/03/01 -- Gregory Boissinot - Zenika - Add the possibility to manage multiple Msbuild version * 2009/05/20 - Fix #3610 */ public class MsBuildBuilder extends Builder { /** * Identifies {@link Visual Studio} to be used. */ private final String msBuildName; private final String msBuildFile; private final String cmdLineArgs; /** * When this builder is created in the project configuration step, * the builder object will be created from the strings below. * @param msName The Visual studio logical identifiant name * @param msBuildFile The name/location of the msbuild file * @param targets Whitespace separated list of command line arguments */ @DataBoundConstructor public MsBuildBuilder(String msBuildName, String msBuildFile,String cmdLineArgs) { this.msBuildName=msBuildName; this.msBuildFile=msBuildFile; this.cmdLineArgs=cmdLineArgs; } public String getCmdLineArgs(){ return cmdLineArgs; } public String getMsBuildFile(){ return msBuildFile; } public String getMsBuildName() { return msBuildName; } public MsBuildInstallation getMsBuild() { for( MsBuildInstallation i : DESCRIPTOR.getInstallations() ) { if(msBuildName!=null && i.getName().equals(msBuildName)) return i; } return null; } @Override public boolean perform(AbstractBuild<?,?> build, Launcher launcher, BuildListener listener) throws InterruptedException { ArgumentListBuilder args = new ArgumentListBuilder(); String execName= "msbuild.exe"; MsBuildInstallation ai = getMsBuild(); if(ai==null) { listener.getLogger().println("Path To MSBuild.exe: " +execName); args.add(execName); } else { File exec = ai.getExecutable(); if(!ai.getExists()) { listener.fatalError(exec+" doesn't exist"); return false; } listener.getLogger().println("Path To MSBuild.exe: " +exec.getPath()); args.add(exec.getPath()); } //Remove all tabs, carriage returns, and newlines and replace them with //whitespaces, so that we can add them as parameters to the executable String normalizedTarget = cmdLineArgs.replaceAll("[\t\r\n]+"," "); if(normalizedTarget.trim().length()>0) args.addTokenized(normalizedTarget); args.addKeyValuePairs("-P:",build.getBuildVariables()); //If a msbuild file is specified, then add it as an argument, otherwise //msbuild will search for any file that ends in .proj or .sln if(msBuildFile != null && msBuildFile.trim().length() > 0){ args.add(msBuildFile); } //According to the Ant builder source code, in order to launch a program //from the command line in windows, we must wrap it into cmd.exe. This //way the return code can be used to determine whether or not the build failed. if(!launcher.isUnix()) { args.prepend("cmd.exe","/C"); args.add("&&","exit","%%ERRORLEVEL%%"); } //Try to execute the command listener.getLogger().println("Executing command: "+args.toStringWithQuote()); try { Map<String,String> env = build.getEnvironment(listener); int r = launcher.launch().cmds(args).envs(env).stdout(listener).pwd(build.getModuleRoot()).join(); return r==0; } catch (IOException e) { Util.displayIOException(e,listener); e.printStackTrace( listener.fatalError("command execution failed") ); return false; } } @Override public Descriptor<Builder> getDescriptor() { // see Descriptor javadoc for more about what a descriptor is. return DESCRIPTOR; } /** * Descriptor should be singleton. */ @Extension public static final DescriptorImpl DESCRIPTOR = new DescriptorImpl(); /** * Descriptor for {@link MsBuildBuilder}. Used as a singleton. * The class is marked as public so that it can be accessed from views. */ public static final class DescriptorImpl extends Descriptor<Builder> { @CopyOnWrite private volatile MsBuildInstallation[] installations = new MsBuildInstallation[0]; DescriptorImpl() { super(MsBuildBuilder.class); load(); } /** * This human readable name is used in the configuration screen. */ public String getDisplayName() { return "Build a Visual Studio project or solution using MSBuild."; } @Override public boolean configure(StaplerRequest req, JSONObject formData) throws FormException{ installations = req.bindParametersToList(MsBuildInstallation.class,"msbuild.").toArray(new MsBuildInstallation[0]); save(); return true; } public MsBuildInstallation[] getInstallations() { return installations; } } }