| /******************************************************************************* |
| * 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; |
| } |
| } |