package org.jenkinsci.plugins.unity3d; import hudson.EnvVars; import hudson.Extension; import hudson.Functions; import hudson.Launcher; import hudson.Util; import hudson.model.EnvironmentSpecific; import hudson.model.Hudson; import hudson.model.Node; import hudson.model.TaskListener; import hudson.remoting.Callable; import hudson.slaves.NodeSpecific; import hudson.tools.ToolDescriptor; import hudson.tools.ToolInstallation; import hudson.tools.ToolInstaller; import hudson.tools.ToolProperty; import hudson.util.FormValidation; import org.jenkinsci.plugins.unity3d.io.PipeFileAfterModificationAction; import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.QueryParameter; import java.io.File; import java.io.IOException; import java.io.OutputStream; import java.util.Collections; import java.util.List; import java.util.concurrent.Future; /** * Represents a Unity3d installation (name, home_dir, etc.) * * @author Jerome Lacoste */ public class Unity3dInstallation extends ToolInstallation implements EnvironmentSpecific<Unity3dInstallation>, NodeSpecific<Unity3dInstallation> { // private static final Logger log = LoggerFactory.getLogger(Unity3dInstallation.class); @DataBoundConstructor public Unity3dInstallation(final String name, final String home, final List<? extends ToolProperty<?>> properties) { super(name, home, properties); } public Unity3dInstallation(final Unity3dInstallation source, final String home, final List<? extends ToolProperty<?>> properties) { super(source.getName(), home, properties); } public Unity3dInstallation forEnvironment(EnvVars env) { return new Unity3dInstallation(this, env.expand(getHome()), getProperties().toList()); } public Unity3dInstallation forNode(Node node, TaskListener log) throws IOException, InterruptedException { return new Unity3dInstallation(this, translateFor(node, log), getProperties().toList()); } /** * Gets the executable path of this Unity3dBuilder on the given target system. */ public String getExecutable(Launcher launcher) throws IOException, InterruptedException { return launcher.getChannel().call(new Callable<String, IOException>() { public String call() throws IOException { File exe = getExeFile(); if (exe.exists()) return exe.getPath(); return null; } }); } private File getExeFile() { String unityHome = Util.replaceMacro(getHome(), EnvVars.masterEnvVars); return getExeFile(new File(unityHome)); } private static File getExeFile(File unityHome) { if (Functions.isWindows()) { return new File(unityHome, "Editor/Unity.exe"); } else { // mac assumed return new File(unityHome, "Contents/MacOS/Unity"); } } /** * Create a long running task that pipes the Unity3d editor.log into the specified pipe. * <p> * This future can be {@link Future#cancel(boolean) cancelled} in order for the pipe to be closed properly. * @param launcher * @param ros the output stream to write into * @return the number of bytes read * @throws IOException */ public Future<Long> pipeEditorLog(final Launcher launcher, final String customLogFile, final OutputStream ros) throws IOException { return launcher.getChannel().callAsync(new Callable<Long, IOException>() { public Long call() throws IOException { return new PipeFileAfterModificationAction(getEditorLogFile(customLogFile).getAbsolutePath(), ros, true).call(); } }); } /** * Returns the Editor.log path on the remote machine * @param launcher * @return * @throws IOException * @throws InterruptedException */ public String getEditorLogPath(final Launcher launcher, final String customLogFile) throws IOException, InterruptedException { return launcher.getChannel().call(new Callable<String, IOException>() { public String call() throws IOException { return getEditorLogFile(customLogFile).getAbsolutePath(); } }); } private File getEditorLogFile(String customLogFile) { if (customLogFile != null) return new File(customLogFile); if (Functions.isWindows()) { File applocaldata = new File(EnvVars.masterEnvVars.get("LOCALAPPDATA")); return new File(applocaldata, "Unity/Editor/Editor.log"); } else { // mac assumed File userhome = new File(EnvVars.masterEnvVars.get("HOME")); return new File(userhome, "Library/Logs/Unity/Editor.log"); } } @Extension public static class DescriptorImpl extends ToolDescriptor<Unity3dInstallation> { @Override public String getDisplayName() { return "Unity3d"; } // for compatibility reasons, the persistence is done by Unity3dBuilder.DescriptorImpl @Override public Unity3dInstallation[] getInstallations() { return Hudson.getInstance().getDescriptorByType(Unity3dBuilder.DescriptorImpl.class).getInstallations(); } @Override public void setInstallations(Unity3dInstallation... installations) { Hudson.getInstance().getDescriptorByType(Unity3dBuilder.DescriptorImpl.class).setInstallations(installations); } @Override public List<? extends ToolInstaller> getDefaultInstallers() { return Collections.emptyList(); } /** * Checks if the UNITY_HOME is valid. */ public FormValidation doCheckHome(@QueryParameter File value) { // this can be used to check the existence of a file on the server, so needs to be protected if (!Hudson.getInstance().hasPermission(Hudson.ADMINISTER)) return FormValidation.ok(); if (value.getPath().equals("")) return FormValidation.ok(); if (!value.isDirectory()) return FormValidation.error(Messages.Unity3d_NotADirectory(value)); File unityExe = getExeFile(value); if (!unityExe.exists()) return FormValidation.error(Messages.Unity3d_NotUnity3dHomeDirectory(value)); return FormValidation.ok(); } public FormValidation doCheckName(@QueryParameter String value) { return FormValidation.validateRequired(value); } } }