diff --git a/proposal/myrmidon/src/java/org/apache/antlib/nativelib/Exec.java b/proposal/myrmidon/src/java/org/apache/antlib/nativelib/Exec.java
new file mode 100644
index 000000000..30a144eb5
--- /dev/null
+++ b/proposal/myrmidon/src/java/org/apache/antlib/nativelib/Exec.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) The Apache Software Foundation. All rights reserved.
+ *
+ * This software is published under the terms of the Apache Software License
+ * version 1.1, a copy of which has been included with this distribution in
+ * the LICENSE.txt file.
+ */
+package org.apache.antlib.nativelib;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Properties;
+import org.apache.avalon.excalibur.i18n.ResourceManager;
+import org.apache.avalon.excalibur.i18n.Resources;
+import org.apache.myrmidon.api.AbstractTask;
+import org.apache.myrmidon.api.TaskException;
+import org.apache.aut.nativelib.Os;
+import org.apache.tools.ant.taskdefs.exec.Execute2;
+import org.apache.tools.ant.types.Argument;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.types.EnvironmentData;
+import org.apache.tools.ant.types.EnvironmentVariable;
+
+/**
+ * Executes a native command.
+ *
+ * @author JDD
+ * @author Sam Ruby
+ * @author Peter Donald
+ * @author Stefan Bodewig
+ * @author Mariusz Nowostawski
+ * @author Thomas Haas
+ */
+public class Exec
+ extends AbstractTask
+{
+ private static final Resources REZ =
+ ResourceManager.getPackageResources( Exec.class );
+
+ private long m_timeout;
+ private EnvironmentData m_env = new EnvironmentData();
+ private Commandline m_command = new Commandline();
+ private boolean m_newEnvironment;
+ private File m_dir;
+ private String m_os;
+
+ /**
+ * The working directory of the process
+ */
+ public void setDir( final File dir )
+ throws TaskException
+ {
+ m_dir = dir;
+ }
+
+ /**
+ * The command to execute.
+ */
+ public void setExecutable( final String value )
+ throws TaskException
+ {
+ m_command.setExecutable( value );
+ }
+
+ /**
+ * Use a completely new environment
+ */
+ public void setNewenvironment( final boolean newEnvironment )
+ {
+ m_newEnvironment = newEnvironment;
+ }
+
+ /**
+ * Only execute the process if os.name
is included in this
+ * string.
+ */
+ public void setOs( final String os )
+ {
+ m_os = os;
+ }
+
+ /**
+ * Timeout in milliseconds after which the process will be killed.
+ */
+ public void setTimeout( final long timeout )
+ {
+ m_timeout = timeout;
+ }
+
+ /**
+ * Add a nested env element - an environment variable.
+ */
+ public void addEnv( final EnvironmentVariable var )
+ {
+ m_env.addVariable( var );
+ }
+
+ /**
+ * Add a nested arg element - a command line argument.
+ */
+ public Argument createArg()
+ {
+ return m_command.createArgument();
+ }
+
+ public void execute()
+ throws TaskException
+ {
+ validate();
+ if( Os.isFamily( m_os ) )
+ {
+ final Execute2 exe = createExecute();
+ doExecute( exe );
+ }
+ }
+
+ private void doExecute( final Execute2 exe )
+ throws TaskException
+ {
+ try
+ {
+ final int err = exe.execute();
+ if( 0 != err )
+ {
+ final String message =
+ REZ.getString( "exec.bad-resultcode.error", new Integer( err ) );
+ throw new TaskException( message );
+ }
+ }
+ catch( final IOException ioe )
+ {
+ final String message = REZ.getString( "exec.failed.error", ioe );
+ throw new TaskException( message, ioe );
+ }
+ }
+
+ private void validate()
+ throws TaskException
+ {
+ if( null == m_command.getExecutable() )
+ {
+ final String message = REZ.getString( "exec.no-executable.error" );
+ throw new TaskException( message );
+ }
+
+ // default directory to the project's base directory
+ if( m_dir == null )
+ {
+ m_dir = getBaseDirectory();
+ }
+ else
+ {
+ if( !m_dir.exists() )
+ {
+ final String message = REZ.getString( "exec.dir-noexist.error", m_dir );
+ throw new TaskException( message );
+ }
+ else if( !m_dir.isDirectory() )
+ {
+ final String message = REZ.getString( "exec.dir-notdir.error", m_dir );
+ throw new TaskException( message );
+ }
+ }
+ }
+
+ private Execute2 createExecute()
+ throws TaskException
+ {
+ final Properties environment = m_env.getVariables();
+
+ logExecDetails( environment );
+
+ final Execute2 exe = new Execute2();
+ setupLogger( exe );
+ exe.setTimeout( m_timeout );
+ exe.setWorkingDirectory( m_dir );
+ exe.setNewenvironment( m_newEnvironment );
+ exe.setEnvironment( environment );
+ exe.setCommandline( m_command.getCommandline() );
+ return exe;
+ }
+
+ private void logExecDetails( final Properties environment )
+ {
+ // show the command
+ getLogger().debug( m_command.toString() );
+ final String message = REZ.getString( "exec.env-vars.notice", environment );
+ getLogger().debug( message );
+ }
+}
diff --git a/proposal/myrmidon/src/java/org/apache/antlib/nativelib/Resources.properties b/proposal/myrmidon/src/java/org/apache/antlib/nativelib/Resources.properties
index 86749caaa..b713f3f2e 100644
--- a/proposal/myrmidon/src/java/org/apache/antlib/nativelib/Resources.properties
+++ b/proposal/myrmidon/src/java/org/apache/antlib/nativelib/Resources.properties
@@ -1,3 +1,11 @@
loadenv.no-prefix.error=No prefix specified for environment data.
loadenv.prefix.notice=Loading Environment with prefix {0}.
loadenv.ignoring-empty.warn=Key {0} in native OS environment is empty - ignoring key.
+
+exec.failed.error=Execute failed. (Reason: {0}).
+exec.bad-resultcode.error=Bad result code {0}.
+exec.no-executable.error=No executable specified.
+exec.dir-noexist.error=The directory you specified ({0}) does not exist.
+exec.dir-notdir.error=The directory you specified is not a directory.
+exec.env-vars.notice=Setting environment variables: {0}.
+exec.invalid-os.notice=This OS, {0} was not found in the specified list of valid OSes: {1}.