Bug 7016 - XP not checked for when getting the environment variables
Summary: XP not checked for when getting the environment variables
Status: RESOLVED DUPLICATE of bug 6456
Alias: None
Product: Ant
Classification: Unclassified
Component: Core tasks (show other bugs)
Version: 1.4.1
Hardware: PC All
: P3 normal (vote)
Target Milestone: ---
Assignee: Ant Notifications List
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2002-03-10 22:31 UTC by Charlie Calvert
Modified: 2008-02-22 12:18 UTC (History)
0 users



Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Charlie Calvert 2002-03-10 22:31:57 UTC
In Execute.java You are checking for 2000 and NT, but not for xp. Here is hte 
fixed code for GetProcEnvCommand:

            if ( osname.indexOf("nt") >= 0 || osname.indexOf("2000") >= 0 || 
osname.indexOf("xp") >= 0 ) 
			{
                // Windows 2000/NT/XP
				System.out.println("running under xp");
                String[] cmd = {"cmd", "/c", "set" };
                return cmd;
            }
            else {
                // Windows 98/95 - need to use an auxiliary script
				System.out.println("running under Windows 
95/98");
                String[] cmd = {"command.com", "/c", "set" };
                return cmd;
            }

Notice that I check for xp.

Here is the whole class. Notice that I have left in some debug statements:

/*
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 2000 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution, if
 *    any, must include the following acknowlegement:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowlegement may appear in the software itself,
 *    if and wherever such third-party acknowlegements normally appear.
 *
 * 4. The names "The Jakarta Project", "Ant", and "Apache Software
 *    Foundation" must not be used to endorse or promote products derived
 *    from this software without prior written permission. For written
 *    permission, please contact apache@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache"
 *    nor may "Apache" appear in their names without prior written
 *    permission of the Apache Group.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 */

package org.apache.tools.ant.taskdefs;

import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.types.Commandline;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.BufferedReader;
import java.io.StringReader;
import java.io.ByteArrayOutputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Vector;

/**
 * Runs an external program.
 *
 * @author thomas.haas@softwired-inc.com
 */
public class Execute {

    /** Invalid exit code. **/
    public final static int INVALID = Integer.MAX_VALUE;

    private String[] cmdl = null;
    private String[] env = null;
    private int exitValue = INVALID;
    private ExecuteStreamHandler streamHandler;
    private ExecuteWatchdog watchdog;
    private File workingDirectory = null;
    private Project project = null;
    private boolean newEnvironment = false;

    /** Controls whether the VM is used to launch commands, where possible */
    private boolean useVMLauncher = true;    
    
    private static String antWorkingDirectory = System.getProperty("user.dir");
    private static CommandLauncher vmLauncher = null;
    private static CommandLauncher shellLauncher = null;
    private static Vector procEnvironment = null;

    /** 
     * Builds a command launcher for the OS and JVM we are running under
     */
    static {
        // Try using a JDK 1.3 launcher
        try {
            vmLauncher = new Java13CommandLauncher();
        }
        catch ( NoSuchMethodException exc ) {
            // Ignore and keep try
        }

        String osname = System.getProperty("os.name").toLowerCase();
        if ( osname.indexOf("mac os") >= 0 ) {
            // Mac
            shellLauncher = new MacCommandLauncher(new CommandLauncher());
        }
        else if ( osname.indexOf("os/2") >= 0 ) {
            // OS/2 - use same mechanism as Windows 2000
            shellLauncher = new WinNTCommandLauncher(new CommandLauncher());
        }
        else if ( osname.indexOf("windows") >= 0 ) {
            // Windows.  Need to determine which JDK we're running in
            CommandLauncher baseLauncher;
            if ( System.getProperty("java.version").startsWith("1.1") ) {
                // JDK 1.1
                baseLauncher = new Java11CommandLauncher();
            }
            else {
                // JDK 1.2
                baseLauncher = new CommandLauncher();
            }

            // Determine if we're running under 2000/NT or 98/95
            if ( osname.indexOf("nt") >= 0 || osname.indexOf("2000") >= 0 || 
osname.indexOf("xp") >= 0 ) 
			{
                // Windows 2000/NT
                System.out.println("running under xp");
                shellLauncher = new WinNTCommandLauncher(baseLauncher);
            }
            else {
                // Windows 98/95 - need to use an auxiliary script
                shellLauncher = new ScriptCommandLauncher("bin/antRun.bat", 
baseLauncher);
            }
        }
        else {
            // Generic
            shellLauncher = new ScriptCommandLauncher("bin/antRun", new 
CommandLauncher());
        }
    }

    /**
     * Find the list of environment variables for this process.
     */
    public static synchronized Vector getProcEnvironment() {
        if (procEnvironment != null) return procEnvironment;

        procEnvironment = new Vector();
        try {
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            Execute exe = new Execute(new PumpStreamHandler(out));
            exe.setCommandline(getProcEnvCommand());
            // Make sure we do not recurse forever
            exe.setNewenvironment(true);
            int retval = exe.execute();
            if ( retval != 0 ) {
                // Just try to use what we got
            }

            BufferedReader in = 
                new BufferedReader(new StringReader(out.toString()));
            String var = null;
            String line, lineSep = System.getProperty("line.separator");
            while ((line = in.readLine()) != null) {
                if (line.indexOf('=') == -1) {
                    // Chunk part of previous env var (UNIX env vars can
                    // contain embedded new lines).
                    if (var == null) {
                        var = lineSep + line;
                    }
                    else {
                        var += lineSep + line;
                    }
                }
                else {
                    // New env var...append the previous one if we have it.
                    if (var != null) {
                        procEnvironment.addElement(var);
                    }
                    var = line;
                }
            }
            // Since we "look ahead" before adding, there's one last env var.
            procEnvironment.addElement(var);
        } 
        catch (java.io.IOException exc) {
            exc.printStackTrace();
            // Just try to see how much we got
        }
        return procEnvironment;
    }

    private static String[] getProcEnvCommand() {
        String osname = System.getProperty("os.name").toLowerCase();
        if ( osname.indexOf("mac os") >= 0 ) {
            // Mac
            // Determine if we are running under OS X
            try {
                String version = System.getProperty("os.version");
                int majorVersion = 
                    Integer.parseInt(version.substring(0, version.indexOf
('.')));

                if (majorVersion >= 10) {
                    // OS X - just line UNIX
                    String[] cmd = {"/usr/bin/env"};
                    return cmd;
                }
            } catch (NumberFormatException e) {
                // fall through to OS 9
            }
            // OS 9 and previous
            // TODO: I have no idea how to get it, someone must fix it
            String[] cmd = null;
            return cmd;
        }
        else if ( osname.indexOf("os/2") >= 0 ) {
            // OS/2 - use same mechanism as Windows 2000
            // Not sure
            String[] cmd = {"cmd", "/c", "set" };
            return cmd;
        }
        else if ( osname.indexOf("indows") >= 0 ) {
            // Determine if we're running under 2000/NT or 98/95
			System.out.println("osname: " + osname);
            if ( osname.indexOf("nt") >= 0 || osname.indexOf("2000") >= 0 || 
osname.indexOf("xp") >= 0 ) 
			{
                // Windows 2000/NT/XP
				System.out.println("running under xp");
                String[] cmd = {"cmd", "/c", "set" };
                return cmd;
            }
            else {
                // Windows 98/95 - need to use an auxiliary script
				System.out.println("running under Windows 
95/98");
                String[] cmd = {"command.com", "/c", "set" };
                return cmd;
            }
        }
        else {
            // Generic UNIX
            // Alternatively one could use: /bin/sh -c env
            String[] cmd = {"/usr/bin/env"};
            return cmd;
        }
    }

    /**
     * Creates a new execute object using <code>PumpStreamHandler</code> for
     * stream handling.
     */
    public Execute() {
        this(new PumpStreamHandler(), null);
    }


    /**
     * Creates a new execute object.
     *
     * @param streamHandler the stream handler used to handle the input and
     *        output streams of the subprocess.
     */
    public Execute(ExecuteStreamHandler streamHandler) {
        this(streamHandler, null);
    }

    /**
     * Creates a new execute object.
     *
     * @param streamHandler the stream handler used to handle the input and
     *        output streams of the subprocess.
     * @param watchdog a watchdog for the subprocess or <code>null</code> to
     *        to disable a timeout for the subprocess.
     */
    public Execute(ExecuteStreamHandler streamHandler, ExecuteWatchdog 
watchdog) {
        this.streamHandler = streamHandler;
        this.watchdog = watchdog;
    }


    /**
     * Returns the commandline used to create a subprocess.
     *
     * @return the commandline used to create a subprocess
     */
    public String[] getCommandline() {
        return cmdl;
    }


    /**
     * Sets the commandline of the subprocess to launch.
     *
     * @param commandline the commandline of the subprocess to launch
     */
    public void setCommandline(String[] commandline) {
        cmdl = commandline;
    }

    /**
     * Set whether to propagate the default environment or not.
     *
     * @param newenv whether to propagate the process environment.
     */
    public void setNewenvironment(boolean newenv) {
        newEnvironment = newenv;
    }

    /**
     * Returns the environment used to create a subprocess.
     *
     * @return the environment used to create a subprocess
     */
    public String[] getEnvironment() {
        if (env == null || newEnvironment) return env;
        return patchEnvironment();
    }


    /**
     * Sets the environment variables for the subprocess to launch.
     *
     * @param commandline array of Strings, each element of which has
     * an environment variable settings in format <em>key=value</em> 
     */
    public void setEnvironment(String[] env) {
        this.env = env;
    }

    /**
     * Sets the working directory of the process to execute.
     *
     * <p>This is emulated using the antRun scripts unless the OS is
     * Windows NT in which case a cmd.exe is spawned,
     * or MRJ and setting user.dir works, or JDK 1.3 and there is
     * official support in java.lang.Runtime.
     *
     * @param wd the working directory of the process.
     */
    public void setWorkingDirectory(File wd) {
        if (wd == null || wd.getAbsolutePath().equals(antWorkingDirectory))
            workingDirectory = null;
        else
            workingDirectory = wd;
    }

    /**
     * Set the name of the antRun script using the project's value.
     *
     * @param project the current project.
     */
    public void setAntRun(Project project) throws BuildException {
        this.project = project;
    }

    /**
     * Launch this execution through the VM, where possible, rather than 
through
     * the OS's shell. In some cases and operating systems using the shell 
will 
     * allow the shell to perform additional processing such as associating an 
     * executable with a script, etc
     *
     * @param vmLauncher true if exec should launch through thge VM, 
     *                   false if the shell should be used to launch the 
command.
     */
    public void setVMLauncher(boolean useVMLauncher) {
        this.useVMLauncher = useVMLauncher;
    }
    
    /**
     * Runs a process defined by the command line and returns its exit status.
     *
     * @return the exit status of the subprocess or <code>INVALID</code>
     * @exception java.io.IOExcpetion The exception is thrown, if launching
     *            of the subprocess failed
     */
    public int execute() throws IOException {
        CommandLauncher launcher = vmLauncher != null ? vmLauncher : 
shellLauncher;
        if (!useVMLauncher) {
            launcher = shellLauncher;
        }
        
        final Process process = launcher.exec(project, getCommandline(), 
getEnvironment(), workingDirectory);
        try {
            streamHandler.setProcessInputStream(process.getOutputStream());
            streamHandler.setProcessOutputStream(process.getInputStream());
            streamHandler.setProcessErrorStream(process.getErrorStream());
        } catch (IOException e) {
            process.destroy();
            throw e;
        }
        streamHandler.start();
        if (watchdog != null) watchdog.start(process);
        waitFor(process);
        if (watchdog != null) watchdog.stop();
        streamHandler.stop();
        if (watchdog != null) watchdog.checkException();
        return getExitValue();
    }

    protected void waitFor(Process process) {
        try {
            process.waitFor();
            setExitValue(process.exitValue());
        } catch (InterruptedException e) {}
    }

    protected void setExitValue(int value) {
        exitValue = value;
    }

    public int getExitValue() {
        return exitValue;
    }

    /**
     * Patch the current environment with the new values from the user.
     * @return the patched environment
     */
    private String[] patchEnvironment() {
        Vector osEnv = (Vector) getProcEnvironment().clone();
        for (int i = 0; i < env.length; i++) {
            int pos = env[i].indexOf('=');
            // Get key including "="
            String key = env[i].substring(0, pos+1);
            int size = osEnv.size();
            for (int j = 0; j < size; j++) {
                if (((String)osEnv.elementAt(j)).startsWith(key)) {
                    osEnv.removeElementAt(j);
                    break;
                }
            }
            osEnv.addElement(env[i]);
        }
        String[] result = new String[osEnv.size()];
        osEnv.copyInto(result);
        return result;
    }

    /**
     * A utility method that runs an external command.  Writes the output and
     * error streams of the command to the project log.
     *
     * @param task      The task that the command is part of.  Used for logging
     * @param cmdline   The command to execute.
     *
     * @throws BuildException if the command does not return 0.
     */
    public static void runCommand(Task task, String[] cmdline) throws 
BuildException
    {
        try {
            task.log(Commandline.toString(cmdline), Project.MSG_VERBOSE);
            Execute exe = new Execute(new LogStreamHandler(task, 
                                                           Project.MSG_INFO,
                                                           Project.MSG_ERR));
            exe.setAntRun(task.getProject());
            exe.setCommandline(cmdline);
            int retval = exe.execute();
            if ( retval != 0 ) {
                throw new BuildException(cmdline[0] + " failed with return 
code " + retval, task.getLocation());
            }
        } 
        catch (java.io.IOException exc) {
            throw new BuildException("Could not launch " + cmdline[0] + ": " + 
exc, task.getLocation());
        }
    }

    /**
     * A command launcher for a particular JVM/OS platform.  This class is
     * a general purpose command launcher which can only launch commands in
     * the current working directory.
     */
    private static class CommandLauncher
    {
        /** 
         * Launches the given command in a new process.
         *
         * @param project       The project that the command is part of
         * @param cmd           The command to execute
         * @param env           The environment for the new process.  If null,
         *                      the environment of the current proccess is 
used.
         */
        public Process exec(Project project, String[] cmd, String[] env) 
throws IOException
        {
            if (project != null) {
                project.log("Execute:CommandLauncher: " +
                            Commandline.toString(cmd), Project.MSG_DEBUG);
            }                            
            return Runtime.getRuntime().exec(cmd, env);
        }

        /** 
         * Launches the given command in a new process, in the given working
         * directory.
         *
         * @param project       The project that the command is part of
         * @param cmd           The command to execute
         * @param env           The environment for the new process.  If null,
         *                      the environment of the current proccess is 
used.
         * @param workingDir    The directory to start the command in.  If 
null,
         *                      the current directory is used
         */
        public Process exec(Project project, String[] cmd, String[] env, File 
workingDir) throws IOException
        {
            if ( workingDir == null ) {
                return exec(project, cmd, env);
            }
            throw new IOException("Cannot execute a process in different 
directory under this JVM");
        }
    }

    /**
     * A command launcher for JDK/JRE 1.1 under Windows.  Fixes quoting 
problems
     * in Runtime.exec().  Can only launch commands in the current working
     * directory
     */
    private static class Java11CommandLauncher extends CommandLauncher
    {
        /**
         * Launches the given command in a new process.  Needs to quote
         * arguments
         */
        public Process exec(Project project, String[] cmd, String[] env) 
throws IOException 
        {
            // Need to quote arguments with spaces, and to escape quote 
characters
            String[] newcmd = new String[cmd.length];
            for ( int i = 0; i < cmd.length; i++ ) {
                newcmd[i] = Commandline.quoteArgument(cmd[i]);
            }
            if (project != null) {
                project.log("Execute:Java11CommandLauncher: " +
                            Commandline.toString(newcmd), Project.MSG_DEBUG);
            }                            
            return Runtime.getRuntime().exec(newcmd, env);
        }
    }

    /**
     * A command launcher for JDK/JRE 1.3 (and higher).  Uses the built-in
     * Runtime.exec() command
     */
    private static class Java13CommandLauncher extends CommandLauncher
    {
        public Java13CommandLauncher() throws NoSuchMethodException
        {
            // Locate method Runtime.exec(String[] cmdarray, String[] envp, 
File dir)
            _execWithCWD = Runtime.class.getMethod("exec", new Class[] {String
[].class, String[].class, File.class});
        }

        /** 
         * Launches the given command in a new process, in the given working
         * directory
         */
        public Process exec(Project project, String[] cmd, String[] env, File 
workingDir) 
            throws IOException
        {
            try {
                if (project != null) {
                    project.log("Execute:Java13CommandLauncher: " +
                                Commandline.toString(cmd), Project.MSG_DEBUG);
                }                                
                Object[] arguments = { cmd, env, workingDir };
                return (Process)_execWithCWD.invoke(Runtime.getRuntime(), 
arguments);
            } 
            catch (InvocationTargetException exc) {
                Throwable realexc = exc.getTargetException();
                if ( realexc instanceof ThreadDeath ) {
                    throw (ThreadDeath)realexc;
                } 
                else if ( realexc instanceof IOException ) {
                    throw (IOException)realexc;
                } 
                else {
                    throw new BuildException("Unable to execute command", 
realexc);
                }
            } 
            catch (Exception exc) {
                // IllegalAccess, IllegalArgument, ClassCast
                throw new BuildException("Unable to execute command", exc);
            }
        }
        
        private Method _execWithCWD;
    }
    
    /**
     * A command launcher that proxies another command launcher.  
     *
     * Sub-classes override exec(args, env, workdir)
     */
    private static class CommandLauncherProxy extends CommandLauncher
    {
        CommandLauncherProxy(CommandLauncher launcher)
        {
            _launcher = launcher;
        }

        /** 
         * Launches the given command in a new process.  Delegates this
         * method to the proxied launcher
         */
        public Process exec(Project project, String[] cmd, String[] env) 
throws IOException
        {
            return _launcher.exec(project, cmd, env);
        }

        private CommandLauncher _launcher;
    }

    /**
     * A command launcher for Windows 2000/NT/XP that uses 'cmd.exe' when
     * launching commands in directories other than the current working
     * directory.
     */
    private static class WinNTCommandLauncher extends CommandLauncherProxy
    {
        WinNTCommandLauncher(CommandLauncher launcher)
        {
            super(launcher);
        }

        /** 
         * Launches the given command in a new process, in the given working
         * directory.
         */
        public Process exec(Project project, String[] cmd, String[] env, File 
workingDir) throws IOException
        {
            File commandDir = workingDir;
            if ( workingDir == null ) {
                if ( project != null ) {
                    commandDir = project.getBaseDir();
                } else {
                    return exec(project, cmd, env);
                }
            }

            // Use cmd.exe to change to the specified directory before running
            // the command
            final int preCmdLength = 6;
            String[] newcmd = new String[cmd.length + preCmdLength];
            newcmd[0] = "cmd";
            newcmd[1] = "/c";
            newcmd[2] = "cd";
            newcmd[3] = "/d";
            newcmd[4] = commandDir.getAbsolutePath();
            newcmd[5] = "&&";
            System.arraycopy(cmd, 0, newcmd, preCmdLength, cmd.length);

            return exec(project, newcmd, env);
        }
    }

    /**
     * A command launcher for Mac that uses a dodgy mechanism to change
     * working directory before launching commands.
     */
    private static class MacCommandLauncher extends CommandLauncherProxy
    {
        MacCommandLauncher(CommandLauncher launcher)
        {
            super(launcher);
        }

        /** 
         * Launches the given command in a new process, in the given working
         * directory
         */
        public Process exec(Project project, String[] cmd, String[] env, File 
workingDir) throws IOException
        {
            if ( workingDir == null ) {
                return exec(project, cmd, env);
            }

            System.getProperties().put("user.dir", workingDir.getAbsolutePath
());
            try {
                return exec(project, cmd, env);
            } 
            finally {
                System.getProperties().put("user.dir", antWorkingDirectory);
            }
        }
    }

    /**
     * A command launcher that uses an auxiliary script to launch commands
     * in directories other than the current working directory.
     */
    private static class ScriptCommandLauncher extends CommandLauncherProxy
    {
        ScriptCommandLauncher(String script, CommandLauncher launcher)
        {
            super(launcher);
            _script = script;
        }

        /** 
         * Launches the given command in a new process, in the given working
         * directory
         */
        public Process exec(Project project, String[] cmd, String[] env, File 
workingDir) throws IOException
        {
            if ( project == null ) {
                if ( workingDir == null ) {
                    return exec(project, cmd, env);
                }
                throw new IOException("Cannot locate antRun script: No project 
provided");
            }
            
            // Locate the auxiliary script
            String antHome = project.getProperty("ant.home");
            if ( antHome == null ) {
                throw new IOException("Cannot locate antRun script: 
Property 'ant.home' not found");
            }
            String antRun = project.resolveFile(antHome + File.separator + 
_script).toString();

            // Build the command
            File commandDir = workingDir;
            if ( workingDir == null && project != null ) {
                commandDir = project.getBaseDir();
            }

            String[] newcmd = new String[cmd.length + 2];
            newcmd[0] = antRun;
            newcmd[1] = commandDir.getAbsolutePath();
            System.arraycopy(cmd, 0, newcmd, 2, cmd.length);
            
            return exec(project, newcmd, env);
        }

        private String _script;
    }
}
Comment 1 Steve Loughran 2002-03-11 05:43:04 UTC
Charlie.
thanks for the patch, but I am afraid to have to tell you that a patch for 
this has been in ant1.5alpha for some time, it is only since java1.4 came out 
that people using java1.4.1 have noticed, and we are starting to see these 
reports trickle in.

Which is why we have an item about the subject on our news page
http://jakarta.apache.org/ant/antnews.html

Anyway, we appreciate your contribution, even if it is a duplicate. There are 
many other outstanding bugs and feature requests for which we do need code...

*** This bug has been marked as a duplicate of 6456 ***