简体   繁体   中英

How to run ant from an Eclipse plugin, send output to an Eclipse console, and capture the build result (success/failure)?

From within an Eclipse plugin, I'd like to run an Ant build script. I also want to display the Ant output to the user, by displaying it in an Eclipse console. Finally, I also want to wait for the Ant build to be finished, and capture the result: did the build succeed or fail?

I found three ways to run an Ant script from eclipse:

  • Instantiate an org.eclipse.ant.core.AntRunner , call some setters and call run() or run(IProgressMonitor) . The result is either normal termination (indicating success), or a CoreException with an IStatus containing a BuildException (indicating failure), or else something else went wrong. However, I don't see the Ant output anywhere.
  • Instantiate an org.eclipse.ant.core.AntRunner and call run(Object) , passing a String[] containing the command line arguments. The result is either normal termination (indication success), or an InvocationTargetException (indicating failure), or else something else went wrong. The Ant output is sent to Eclipse's stdout, it seems; it is not visible in Eclipse itself.
  • Call DebugPlugin.getDefault().getLaunchManager() , then on that call getLaunchConfigurationType(IAntLaunchConfigurationConstants.ID_ANT_BUILDER_LAUNCH_CONFIGURATION_TYPE) , then on that set attribute "org.eclipse.ui.externaltools.ATTR_LOCATION" to the build file name (and attribute DebugPlugin.ATTR_CAPTURE_OUTPUT to true) and finally call launch() . The Ant output is shown in an Eclipse console, but I have no idea how to capture the build result (success/failure) in my code. Or how to wait for termination of the launch, even.

Is there any way to have both console output and capture the result?

Edit 05/16/2016 @Lii alerted me to the fact that any output between the ILaunchConfigurationWorkingCopy#launch call and when the IStreamListener is appended will be lost. He made a contribution to this answer here .

Original Answer I realize this is an old post, but I was able to do exactly what you want in one of my plugins. If it doesn't help you at this point, maybe it will help someone else. I originally did this in 3.2, but it has been updated for 3.6 API changes...

// show the console
final IWorkbenchPage activePage = PlatformUI.getWorkbench()
        .getActiveWorkbenchWindow()
        .getActivePage();
activePage.showView(IConsoleConstants.ID_CONSOLE_VIEW);

// let launch manager handle ant script so output is directed to Console view
final ILaunchManager manager = DebugPlugin.getDefault().getLaunchManager();
ILaunchConfigurationType type = manager.getLaunchConfigurationType(IAntLaunchConstants.ID_ANT_LAUNCH_CONFIGURATION_TYPE);
final ILaunchConfigurationWorkingCopy workingCopy = type.newInstance(null, [*** GIVE YOUR LAUNCHER A NAME ***]);
workingCopy.setAttribute(ILaunchManager.ATTR_PRIVATE, true);
workingCopy.setAttribute(IExternalToolConstants.ATTR_LOCATION, [*** PATH TO ANT SCRIPT HERE ***]);
final ILaunch launch = workingCopy.launch(ILaunchManager.RUN_MODE, null);
// make sure the build doesnt fail
final boolean[] buildSucceeded = new boolean[] { true };
((AntProcess) launch.getProcesses()[0]).getStreamsProxy()
        .getErrorStreamMonitor()
        .addListener(new IStreamListener() {
            @Override
            public void streamAppended(String text, IStreamMonitor monitor) {
                if (text.indexOf("BUILD FAILED") > -1) {
                    buildSucceeded[0] = false;
                }
            }
        });
// wait for the launch (ant build) to complete
manager.addLaunchListener(new ILaunchesListener2() {
    public void launchesTerminated(ILaunch[] launches) {
        boolean patchSuccess = false;
        try {
            if (!buildSucceeded[0]) {
                throw new Exception("Build FAILED!");
            }
            for (int i = 0; i < launches.length; i++) {
                if (launches[i].equals(launch)
                        && buildSucceeded[0]
                        && !((IProgressMonitor) launches[i].getProcesses()[0]).isCanceled()) {
                    [*** DO YOUR THING... ***]
                    break;
                }
            }
        } catch (Exception e) {
            [*** DO YOUR THING... ***]
        } finally {
            // get rid of this listener
            manager.removeLaunchListener(this);
            [*** DO YOUR THING... ***]
        }
    }

    public void launchesAdded(ILaunch[] launches) {
    }

    public void launchesChanged(ILaunch[] launches) {
    }

    public void launchesRemoved(ILaunch[] launches) {
    }
});

I'd like to add one thing to happytime harry's answer .

Sometimes the first writes to the stream happens before the stream listener is added. Then streamAppended on the listener is never called for those writes so output is lost.

See for example this bug . I think happytime harry's solution might have this problem. I myself registered my stream listener in ILaunchListener.launchChanged and this happened 4/5 times.

If one wants to be sure to get all the output from a stream then the IStreamMonitor.getContents method can be used to fetch the output that happened before the listener got added.

The following is an attempt on a utility method that handles this. It is based on the code in ProcessConsole .

/**
 * Adds listener to monitor, and calls listener with any content monitor already has.
 * NOTE: This methods synchronises on monitor while listener is called. Listener may
 * not wait on any thread that waits for monitors monitor, what would result in dead-lock.
 */
public static void addAndNotifyStreamListener(IStreamMonitor monitor, IStreamListener listener) {
    // Synchronise on monitor to prevent writes to stream while we are adding listener.
    // It's weird to synchronise on monitor because that's a shared object, but that's 
    // what ProcessConsole does.  
    synchronized (monitor) {
        String contents = monitor.getContents();
        if (!contents.isEmpty()) {
            // Call to unknown code while synchronising on monitor. This is dead-lock prone!
            // Listener must not wait for other threads that are waiting in line to 
            // synchronise on monitor.
            listener.streamAppended(contents, monitor);
        }
        monitor.addListener(listener);
    }
}

PS: There is some weird stuff going on in ProcessConsole.java . Why is the content buffering switched of from the ProcessConsole.StreamListener constructor?! If the ProcessConsole.StreamListener runs before this one maybe this solution doesn't work.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM