blob: 0732b87626093a58a1975bb6ab6e23f698498770 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2016 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.ant.internal.launching;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.util.FileUtils;
import org.eclipse.ant.internal.core.IAntCoreConstants;
import org.eclipse.ant.internal.launching.launchConfigurations.AntHomeClasspathEntry;
import org.eclipse.ant.internal.launching.launchConfigurations.AntProcess;
import org.eclipse.ant.internal.launching.launchConfigurations.RemoteAntRuntimeProcess;
import org.eclipse.ant.launching.IAntLaunchConstants;
import org.eclipse.core.externaltools.internal.IExternalToolConstants;
import org.eclipse.core.externaltools.internal.model.ExternalToolBuilder;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.variables.VariablesPlugin;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
import org.eclipse.debug.core.model.IProcess;
import org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants;
import org.eclipse.jdt.launching.IRuntimeClasspathEntry;
import org.eclipse.jdt.launching.IRuntimeClasspathEntry2;
import org.eclipse.jdt.launching.JavaRuntime;
import com.ibm.icu.text.MessageFormat;
/**
* General utility class dealing with Ant build files
*/
public final class AntLaunchingUtil {
public static final String ATTRIBUTE_SEPARATOR = ","; //$NON-NLS-1$;
public static final char ANT_CLASSPATH_DELIMITER = '*';
public static final String ANT_HOME_CLASSPATH_PLACEHOLDER = "G"; //$NON-NLS-1$
public static final String ANT_GLOBAL_USER_CLASSPATH_PLACEHOLDER = "UG"; //$NON-NLS-1$
/**
* No instances allowed
*/
private AntLaunchingUtil() {
super();
}
/**
* Returns a single-string of the strings for storage.
*
* @param strings
* the array of strings
* @return a single-string representation of the strings or <code>null</code> if the array is empty.
*/
public static String combineStrings(String[] strings) {
if (strings.length == 0)
return null;
if (strings.length == 1)
return strings[0];
StringBuffer buf = new StringBuffer();
for (int i = 0; i < strings.length - 1; i++) {
buf.append(strings[i]);
buf.append(ATTRIBUTE_SEPARATOR);
}
buf.append(strings[strings.length - 1]);
return buf.toString();
}
/**
* Returns an array of targets to be run, or <code>null</code> if none are specified (indicating the default target or implicit target should be
* run).
*
* @param configuration
* launch configuration
* @return array of target names, or <code>null</code>
* @throws CoreException
* if unable to access the associated attribute
*/
public static String[] getTargetNames(ILaunchConfiguration configuration) throws CoreException {
String attribute = null;
if (IAntLaunchConstants.ID_ANT_BUILDER_LAUNCH_CONFIGURATION_TYPE.equals(configuration.getType().getIdentifier())) {
attribute = getTargetNamesForAntBuilder(configuration);
}
if (attribute == null) {
attribute = configuration.getAttribute(IAntLaunchConstants.ATTR_ANT_TARGETS, (String) null);
if (attribute == null) {
return null;
}
}
return AntLaunchingUtil.parseRunTargets(attribute);
}
private static String getTargetNamesForAntBuilder(ILaunchConfiguration configuration) throws CoreException {
String buildType = ExternalToolBuilder.getBuildType();
String targets = null;
if (IExternalToolConstants.BUILD_TYPE_AUTO.equals(buildType)) {
targets = configuration.getAttribute(IAntLaunchConstants.ATTR_ANT_AUTO_TARGETS, (String) null);
} else if (IExternalToolConstants.BUILD_TYPE_CLEAN.equals(buildType)) {
targets = configuration.getAttribute(IAntLaunchConstants.ATTR_ANT_CLEAN_TARGETS, (String) null);
} else if (IExternalToolConstants.BUILD_TYPE_FULL.equals(buildType)) {
targets = configuration.getAttribute(IAntLaunchConstants.ATTR_ANT_AFTER_CLEAN_TARGETS, (String) null);
} else if (IExternalToolConstants.BUILD_TYPE_INCREMENTAL.equals(buildType)) {
targets = configuration.getAttribute(IAntLaunchConstants.ATTR_ANT_MANUAL_TARGETS, (String) null);
}
return targets;
}
/**
* Returns a map of properties to be defined for the build, or <code>null</code> if none are specified (indicating no additional properties
* specified for the build).
*
* @param configuration
* launch configuration
* @return map of properties (name --> value), or <code>null</code>
* @throws CoreException
* if unable to access the associated attribute
*/
public static Map<String, String> getProperties(ILaunchConfiguration configuration) throws CoreException {
// TODO PLATFORM DEBUG 1.5 API
Map<String, String> map = configuration.getAttribute(IAntLaunchConstants.ATTR_ANT_PROPERTIES, (Map<String, String>) null);
return map;
}
/**
* Returns a String specifying the Ant home to use for the build.
*
* @param configuration
* launch configuration
* @return String specifying Ant home to use or <code>null</code>
* @throws CoreException
* if unable to access the associated attribute
*/
public static String getAntHome(ILaunchConfiguration configuration) throws CoreException {
IRuntimeClasspathEntry[] entries = JavaRuntime.computeUnresolvedRuntimeClasspath(configuration);
for (int i = 0; i < entries.length; i++) {
IRuntimeClasspathEntry entry = entries[i];
if (entry.getType() == IRuntimeClasspathEntry.OTHER) {
IRuntimeClasspathEntry2 entry2 = (IRuntimeClasspathEntry2) entry;
if (entry2.getTypeId().equals(AntHomeClasspathEntry.TYPE_ID)) {
return ((AntHomeClasspathEntry) entry2).getAntHome();
}
}
}
return null;
}
/**
* Returns an array of property files to be used for the build, or <code>null</code> if none are specified (indicating no additional property
* files specified for the build).
*
* @param configuration
* launch configuration
* @return array of property file names, or <code>null</code>
* @throws CoreException
* if unable to access the associated attribute
*/
public static String[] getPropertyFiles(ILaunchConfiguration configuration) throws CoreException {
String attribute = configuration.getAttribute(IAntLaunchConstants.ATTR_ANT_PROPERTY_FILES, (String) null);
if (attribute == null) {
return null;
}
String[] propertyFiles = AntLaunchingUtil.parseString(attribute, ","); //$NON-NLS-1$
for (int i = 0; i < propertyFiles.length; i++) {
String propertyFile = propertyFiles[i];
propertyFile = expandVariableString(propertyFile, AntCoreModelMessages.AntUtil_6);
propertyFiles[i] = propertyFile;
}
return propertyFiles;
}
/**
* Returns the list of URLs that define the custom classpath for the Ant build, or <code>null</code> if the global classpath is to be used.
*
* @param config
* launch configuration
* @return a list of <code>URL</code>
*
* @throws CoreException
* if file does not exist, IO problems, or invalid format.
*/
public static URL[] getCustomClasspath(ILaunchConfiguration config) throws CoreException {
boolean useDefault = config.getAttribute(IJavaLaunchConfigurationConstants.ATTR_DEFAULT_CLASSPATH, true);
if (useDefault) {
return null;
}
IRuntimeClasspathEntry[] unresolved = JavaRuntime.computeUnresolvedRuntimeClasspath(config);
// don't consider bootpath entries
List<IRuntimeClasspathEntry> userEntries = new ArrayList<>(unresolved.length);
for (int i = 0; i < unresolved.length; i++) {
IRuntimeClasspathEntry entry = unresolved[i];
if (entry.getClasspathProperty() == IRuntimeClasspathEntry.USER_CLASSES) {
userEntries.add(entry);
}
}
IRuntimeClasspathEntry[] entries = JavaRuntime.resolveRuntimeClasspath(userEntries.toArray(new IRuntimeClasspathEntry[userEntries.size()]), config);
URL[] urls = new URL[entries.length];
for (int i = 0; i < entries.length; i++) {
IRuntimeClasspathEntry entry = entries[i];
try {
urls[i] = new URL(IAntCoreConstants.FILE_PROTOCOL + entry.getLocation());
}
catch (MalformedURLException e) {
throw new CoreException(new Status(IStatus.ERROR, AntLaunching.getUniqueIdentifier(), AntLaunching.INTERNAL_ERROR, AntCoreModelMessages.AntUtil_7, e));
}
}
return urls;
}
private static String expandVariableString(String variableString, String invalidMessage) throws CoreException {
String expandedString = VariablesPlugin.getDefault().getStringVariableManager().performStringSubstitution(variableString);
if (expandedString == null || expandedString.length() == 0) {
String msg = MessageFormat.format(invalidMessage, new Object[] { variableString });
throw new CoreException(new Status(IStatus.ERROR, AntLaunching.PLUGIN_ID, 0, msg, null));
}
return expandedString;
}
/**
* Returns the list of target names to run
*
* @param extraAttibuteValue
* the external tool's extra attribute value for the run targets key.
* @return a list of target names
*/
public static String[] parseRunTargets(String extraAttibuteValue) {
return parseString(extraAttibuteValue, ATTRIBUTE_SEPARATOR);
}
/**
* Returns the list of Strings that were delimiter separated.
*
* @param delimString
* the String to be tokenized based on the delimiter
* @return a list of Strings
*/
public static String[] parseString(String delimString, String delim) {
if (delimString == null) {
return new String[0];
}
// Need to handle case where separator character is
// actually part of the target name!
StringTokenizer tokenizer = new StringTokenizer(delimString, delim);
String[] results = new String[tokenizer.countTokens()];
for (int i = 0; i < results.length; i++) {
results[i] = tokenizer.nextToken().trim();
}
return results;
}
/**
* Returns an IFile with the given fully qualified path (relative to the workspace root). The returned IFile may or may not exist.
*/
public static IFile getFile(String fullPath) {
IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
return root.getFile(new Path(fullPath));
}
/**
* Returns the workspace file associated with the given path in the local file system, or <code>null</code> if none. If the path happens to be a
* relative path, then the path is interpreted as relative to the specified parent file.
*
* Attempts to handle linked files; the first found linked file with the correct path is returned.
*
* @param path
* @param buildFileParent
* @return file or <code>null</code>
* @see org.eclipse.core.resources.IWorkspaceRoot#findFilesForLocation(IPath)
*/
public static IFile getFileForLocation(String path, File buildFileParent) {
if (path == null) {
return null;
}
IPath filePath = new Path(path);
IFile file = null;
URI location = filePath.makeAbsolute().toFile().toURI();
IFile[] files = ResourcesPlugin.getWorkspace().getRoot().findFilesForLocationURI(location);
if (files.length > 0) {
file = files[0];
}
if (file == null) {
// relative path
File relativeFile = null;
try {
// this call is ok if buildFileParent is null
relativeFile = FileUtils.getFileUtils().resolveFile(buildFileParent, path);
filePath = new Path(relativeFile.getAbsolutePath());
location = filePath.makeAbsolute().toFile().toURI();
files = ResourcesPlugin.getWorkspace().getRoot().findFilesForLocationURI(location);
if (files.length > 0) {
file = files[0];
} else {
return null;
}
}
catch (BuildException be) {
return null;
}
}
if (file.exists()) {
return file;
}
File ioFile = file.getLocation().toFile();
if (ioFile.exists()) {// needs to handle case insensitivity on WINOS
files = ResourcesPlugin.getWorkspace().getRoot().findFilesForLocationURI(ioFile.toURI());
if (files.length > 0) {
return files[0];
}
}
return null;
}
/**
* Migrates the classpath in the given configuration from the old format to the new format. The old format is not preserved. Instead, the default
* classpath will be used. However, ANT_HOME settings are preserved.
*
* @param configuration
* a configuration to migrate
* @throws CoreException
* if unable to migrate
* @since 3.0
*/
@SuppressWarnings({ "restriction", "deprecation" })
public static void migrateToNewClasspathFormat(ILaunchConfiguration configuration) throws CoreException {
String oldClasspath = configuration.getAttribute(AntLaunching.ATTR_ANT_CUSTOM_CLASSPATH, (String) null);
String oldAntHome = configuration.getAttribute(AntLaunching.ATTR_ANT_HOME, (String) null);
String provider = configuration.getAttribute(IJavaLaunchConfigurationConstants.ATTR_CLASSPATH_PROVIDER, (String) null);
if (oldClasspath != null || oldAntHome != null || provider == null) {
ILaunchConfigurationWorkingCopy workingCopy = null;
if (configuration.isWorkingCopy()) {
workingCopy = (ILaunchConfigurationWorkingCopy) configuration;
} else {
workingCopy = configuration.getWorkingCopy();
}
workingCopy.setAttribute(AntLaunching.ATTR_ANT_CUSTOM_CLASSPATH, (String) null);
workingCopy.setAttribute(AntLaunching.ATTR_ANT_HOME, (String) null);
workingCopy.setAttribute(IJavaLaunchConfigurationConstants.ATTR_CLASSPATH_PROVIDER, "org.eclipse.ant.ui.AntClasspathProvider"); //$NON-NLS-1$
workingCopy.setAttribute(IJavaLaunchConfigurationConstants.ATTR_DEFAULT_CLASSPATH, true);
if (oldAntHome != null) {
IRuntimeClasspathEntry[] entries = JavaRuntime.computeUnresolvedRuntimeClasspath(workingCopy);
List<String> mementos = new ArrayList<>(entries.length);
for (int i = 0; i < entries.length; i++) {
IRuntimeClasspathEntry entry = entries[i];
if (entry.getType() == IRuntimeClasspathEntry.OTHER) {
IRuntimeClasspathEntry2 entry2 = (IRuntimeClasspathEntry2) entry;
if (entry2.getTypeId().equals(AntHomeClasspathEntry.TYPE_ID)) {
AntHomeClasspathEntry homeEntry = new AntHomeClasspathEntry(oldAntHome);
mementos.add(homeEntry.getMemento());
continue;
}
}
mementos.add(entry.getMemento());
}
workingCopy.setAttribute(IJavaLaunchConfigurationConstants.ATTR_DEFAULT_CLASSPATH, false);
workingCopy.setAttribute(IJavaLaunchConfigurationConstants.ATTR_CLASSPATH, mementos);
}
workingCopy.doSave();
}
}
public static boolean isSeparateJREAntBuild(ILaunchConfiguration configuration) {
boolean separateJRE = true;
try {
// always null for same JRE
separateJRE = configuration.getAttribute(IJavaLaunchConfigurationConstants.ATTR_MAIN_TYPE_NAME, (String) null) != null;
}
catch (CoreException e) {
AntLaunching.log(AntCoreModelMessages.AntUtil_2, e);
}
return separateJRE;
}
public static void linkBuildFailedMessage(String message, IProcess process) {
String fileName = null;
String lineNumber = ""; //$NON-NLS-1$
int fileStart = 0;
int index = message.indexOf("xml"); //$NON-NLS-1$
if (index > 0) {
int numberStart = index + 4;
int numberEnd = message.indexOf(':', numberStart);
int fileEnd = index + 3;
if (numberStart > 0 && fileEnd > 0) {
fileName = message.substring(fileStart, fileEnd).trim();
if (numberEnd > 0) {
lineNumber = message.substring(numberStart, numberEnd).trim();
}
}
}
if (fileName != null) {
int num = -1;
try {
num = Integer.parseInt(lineNumber);
}
catch (NumberFormatException e) {
// do nothing
}
URI location = new Path(fileName).makeAbsolute().toFile().toURI();
IFile[] files = ResourcesPlugin.getWorkspace().getRoot().findFilesForLocationURI(location);
IFile file = null;
if (files.length > 0) {
file = files[0];
}
if (file != null && file.exists()) {
if (process != null) {
ILaunch launch = null;
if (process instanceof RemoteAntRuntimeProcess) {
launch = ((RemoteAntRuntimeProcess) process).getLaunch();
} else if (process instanceof AntProcess) {
launch = ((AntProcess) process).getLaunch();
}
if (launch != null) {
((AntLaunch) launch).addLinkDescriptor(message, fileName, num, 0, message.length());
}
}
}
}
}
/**
* Returns whether the given configuration should be launched in the background. When unspecified, the default value for an Ant launch
* configuration is <code>true</code>.
*
* @param configuration
* the configuration
* @return whether the configuration is configured to launch in the background
*/
public static boolean isLaunchInBackground(ILaunchConfiguration configuration) {
boolean launchInBackground = true;
try {
launchInBackground = configuration.getAttribute(IExternalToolConstants.ATTR_LAUNCH_IN_BACKGROUND, true);
}
catch (CoreException ce) {
AntLaunching.log(ce);
}
return launchInBackground;
}
}