package hudson.plugins.buckminster.command; import hudson.FilePath; import hudson.Functions; import hudson.Launcher; import hudson.model.AbstractBuild; import hudson.model.BuildListener; import hudson.model.Computer; import hudson.model.Hudson; import hudson.model.JDK; import hudson.model.Node; import hudson.model.TaskListener; import hudson.plugins.buckminster.BuckminsterInstallation; import hudson.plugins.buckminster.EclipseBuckminsterBuilder; import hudson.plugins.buckminster.install.BuckminsterInstallable; import hudson.remoting.Callable; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.net.MalformedURLException; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Creates the commandline to invoke to process and writes a textfile containing the * buckminster commands to a file commands.txt in the workspace * @author Johannes Utzig * */ public class CommandLineBuilder { private BuckminsterInstallation installation; private String commands; private String logLevel; private String additionalParams; private FilePath hudsonWorkspaceRoot; private String userWorkspace; private String userTemp; private String userOutput; private String userCommandFile; public CommandLineBuilder(BuckminsterInstallation installation, String commands, String logLevel, String additionalParams, String userWorkspace, String userTemp, String userOutput, String userCommandFile) { super(); this.installation = installation; this.commands = commands; this.logLevel = logLevel; this.additionalParams = additionalParams; this.userWorkspace = userWorkspace; this.userTemp = userTemp; this.userOutput = userOutput; this.userCommandFile = userCommandFile; } /** * fills an arraylist with all program arguments for buckminster * * @param build * @param launcher * @return the list containing the actual invocation and all parameters * @throws MalformedURLException * @throws IOException * @throws InterruptedException */ public List<String> buildCommands(AbstractBuild<?,?> build, BuildListener listener, Launcher launcher) throws MalformedURLException, IOException, InterruptedException{ List<String> commandList = new ArrayList<String>(); hudsonWorkspaceRoot = build.getWorkspace(); commandList.add(getJavaExecutable(listener, launcher, build)); Map<String, String> properties = new HashMap<String, String>(build.getEnvironment(listener)); properties.putAll(build.getBuildVariables()); addJVMProperties(commandList, properties); FilePath commandsPath = getCommandFilePath(build, properties); addStarterParameters(build, commandList, properties); commandList.add("--loglevel"); commandList.add(getLogLevel()); // Tell Buckminster about the command file commandList.add("-S"); commandList.add(commandsPath.getRemote()); //only write out commands if the user did not specify a custom command file if(userCommandFile==null || userCommandFile.length()==0) { writeCommandFile(commandsPath, properties); } return commandList; } private String getJavaExecutable(BuildListener listener, Launcher launcher, AbstractBuild<?,?> build) throws IOException, InterruptedException { JDK jdk = build.getProject().getJDK(); if(jdk!=null) { jdk= jdk.forNode(Computer.currentComputer().getNode(), listener); jdk = jdk.forEnvironment(build.getEnvironment(listener)); } //if none is configured, hope it is in the PATH //otherwise use the configured one if(jdk!=null) { Boolean isWindows = !launcher.isUnix(); FilePath javaExecutable; if(isWindows) { javaExecutable = Computer.currentComputer().getNode().createPath(jdk.getHome()).child("bin").child("java.exe"); } else { javaExecutable = Computer.currentComputer().getNode().createPath(jdk.getHome()).child("bin").child("java"); } if(javaExecutable.exists()) { return javaExecutable.getRemote(); } else { String message = "The configured JDK \"{0}\" points to \"{1}\" but no executable exists. Defaulting to \"java\""; message = MessageFormat.format(message, jdk.getName(),jdk.getHome()); listener.error(message); } } return "java"; } private FilePath getCommandFilePath(AbstractBuild<?, ?> build, Map<String, String> properties) { // the file listing all the commands since buckminster doesn't accept // several commands as programm arguments if(userCommandFile==null || userCommandFile.length()==0) { return build.getWorkspace().child("commands.txt"); } return build.getWorkspace().child(expandProperties(userCommandFile, properties)); } public BuckminsterInstallation getInstallation() { return installation; } public String getCommands() { return commands; } public String getLogLevel() { return logLevel; } public String getAdditionalParams() { return additionalParams; } private void writeCommandFile(FilePath commandsPath, Map<String, String> properties) throws IOException, InterruptedException { commandsPath.write(expandProperties(getCommands(), properties), "UTF-8"); } private void addStarterParameters(AbstractBuild<?,?> build, List<String> commandList, Map<String, String> properties) throws IOException, InterruptedException { commandList.add("-jar"); commandList.add(findEquinoxLauncher()); // Specify Eclipse Product commandList.add("-application"); commandList.add("org.eclipse.buckminster.cmdline.headless"); // set the workspace to the hudson workspace commandList.add("-data"); String workspace = getDataPath(build, properties); commandList.add(workspace); } private String getDataPath(AbstractBuild<?, ?> build, Map<String, String> properties) throws IOException, InterruptedException { if(userWorkspace==null || userWorkspace.length()==0) { return hudsonWorkspaceRoot.getRemote(); } return expandProperties(userWorkspace, properties); } private void addJVMProperties(List<String> commandList, Map<String, String> properties) throws IOException, InterruptedException { //temp and output root commandList.add(MessageFormat.format("-Dbuckminster.output.root={0}",getOutputDir(properties))); commandList.add(MessageFormat.format("-Dbuckminster.temp.root={0}",getTempDir(properties))); String params = getInstallation().getParams(); String[] globalVMParams = params.split("[\n\r]+"); for (int i = 0; i < globalVMParams.length; i++) { if(globalVMParams[i].trim().length()>0) commandList.add(expandProperties(globalVMParams[i],properties)); } if(globalVMParams.length==0) { commandList.add("-Xmx512m"); commandList.add("-XX:PermSize=128m"); } //job vm setting (properties) String jobParams = getAdditionalParams(); if(jobParams!=null){ String[] additionalJobParams = jobParams.split("[\n\r]+"); for (int i = 0; i < additionalJobParams.length; i++) { if(additionalJobParams[i].trim().length()>0){ String parameter = expandProperties(additionalJobParams[i],properties); commandList.add(parameter); } } } } private Object getTempDir(Map<String, String> properties) { if(userTemp==null || userTemp.length()==0) { return hudsonWorkspaceRoot.child("buckminster.temp").getRemote(); } return hudsonWorkspaceRoot.child(expandProperties(userTemp, properties)).getRemote(); } private String getOutputDir(Map<String, String> properties) { if(userOutput==null || userOutput.length()==0) { return hudsonWorkspaceRoot.child("buckminster.output").getRemote(); } return hudsonWorkspaceRoot.child(expandProperties(userOutput, properties)).getRemote(); } private String expandProperties(String string, Map<String, String> properties) { Pattern pattern = Pattern.compile("\\$\\{(.*?)\\}"); Matcher matcher = pattern.matcher(string); while(matcher.find()){ if(matcher.group(1)!=null){ if(properties.containsKey(matcher.group(1))){ String replacement = null; if(matcher.group(1).equalsIgnoreCase("WORKSPACE")){ //special treatment for the workspace variable because the path has to be transformed into a unix path //see: https://hudson.dev.java.net/issues/show_bug.cgi?id=4947 replacement = new File(properties.get("WORKSPACE")).toURI().getPath(); } else{ replacement = properties.get(matcher.group(1)); } string = string.replace(matcher.group(0), replacement); } } } return string; } /** * searches for the eclipse starter jar * <p> * The content of the folder $ECLIPSE_HOME/plugins is listed and the first * file that starts with <code>org.eclipse.equinox.launcher_</code> is * returned. * * @return the guess for the startup jar, or <code>null</code> if none was * found * @throws IOException * @throws InterruptedException * @see EclipseBuckminsterBuilder#getEclipseHome() */ private String findEquinoxLauncher() throws IOException, InterruptedException { FilePath installationHome = Computer.currentComputer().getNode().createPath(getInstallation().getHome()); FilePath pluginDir = installationHome.child("plugins"); if(!pluginDir.exists()) throw new FileNotFoundException("No 'plugins' directory has been found in "+installation.getHome()); List<FilePath> plugins = pluginDir.list(); for (FilePath filePath : plugins) { if (filePath.getName() .startsWith("org.eclipse.equinox.launcher_")) { return filePath.getRemote(); } } throw new FileNotFoundException("No equinox launcher jar has been found in "+pluginDir.getRemote()); } public static List<String> createDirectorScript(BuckminsterInstallable installable, FilePath toolDir, Node node, TaskListener log, Set<String> repositories, Set<String> featuresToInstall) throws IOException, InterruptedException { return createDirectorScript(installable, toolDir, node, log, repositories, featuresToInstall, new HashSet<String>()); } public static List<String> createDirectorScript(BuckminsterInstallable installable, FilePath toolDir, Node node, TaskListener log, Set<String> repositories, Set<String> featuresToInstall, Set<String> featuresToUninstall) throws IOException, InterruptedException { List<String> commands = new ArrayList<String>(); String executableName = toolDir.getChannel().call(new Callable<String, IOException>() { private static final long serialVersionUID = 2062576798236698029L; public String call() throws IOException { if(Functions.isWindows()) return "director.bat"; return "director"; } }); String directorInvocation = toolDir.child("director").child(executableName).getRemote(); commands.add(directorInvocation); FilePath buckyDir = toolDir.child("buckminster"); String buckyDirPath = buckyDir.getRemote(); List<JDK> jdks = Hudson.getInstance().getJDKs(); if(jdks!=null && jdks.size()>0) { JDK jdk = Hudson.getInstance().getJDKs().get(0); jdk = jdk.forNode(node, log); jdk = jdk.forEnvironment(node.toComputer().getEnvironment()); commands.add("-vm"); commands.add(node.createPath(jdk.getBinDir().getPath()).child("java").getRemote()); } commands.add("-d"); commands.add(buckyDirPath); commands.add("-p"); commands.add("Buckminster"); if(repositories.size()>0) { commands.add("-r"); commands.add(toCSV(repositories)); } if(featuresToUninstall.size()>0) { commands.add("-uninstallIU"); commands.add(toCSV(featuresToUninstall)); } if(featuresToInstall.size()>0) { commands.add("-installIU"); commands.add(toCSV(featuresToInstall)); } return commands; } private static String toCSV(Collection<String> values) { return toCSV(values,", "); } private static String toCSV(Collection<String> values, String separator) { StringBuilder builder = new StringBuilder(); for (String value : values) { builder.append(value); builder.append(separator); } builder.setLength(builder.length()-separator.length()); return builder.toString(); } }