| /******************************************************************************* |
| * Copyright (c) 2000, 2018 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.core.externaltools.internal.model; |
| |
| import java.util.HashMap; |
| import java.util.Map; |
| import java.util.StringTokenizer; |
| |
| import org.eclipse.core.externaltools.internal.IExternalToolConstants; |
| import org.eclipse.core.externaltools.internal.registry.ExternalToolMigration; |
| import org.eclipse.core.resources.ICommand; |
| import org.eclipse.core.resources.IFile; |
| import org.eclipse.core.resources.IFolder; |
| import org.eclipse.core.resources.IProject; |
| import org.eclipse.core.resources.IResource; |
| import org.eclipse.core.resources.IncrementalProjectBuilder; |
| 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.NullProgressMonitor; |
| import org.eclipse.core.runtime.Path; |
| import org.eclipse.debug.core.DebugPlugin; |
| import org.eclipse.debug.core.ILaunchConfiguration; |
| import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; |
| import org.eclipse.debug.core.ILaunchManager; |
| |
| /** |
| * Utility methods for working with external tool project builders. |
| */ |
| public class BuilderCoreUtils { |
| |
| public static final String LAUNCH_CONFIG_HANDLE = "LaunchConfigHandle"; //$NON-NLS-1$ |
| /** |
| * Constant added to the build command to determine if we are doing an incremental build after a clean |
| * |
| * @since 3.7 |
| */ |
| public static final String INC_CLEAN = "incclean"; //$NON-NLS-1$ |
| |
| /** |
| * Constant used to find a builder using the 3.0-interim format |
| */ |
| public static final String BUILDER_FOLDER_NAME = ".externalToolBuilders"; //$NON-NLS-1$ |
| /** |
| * Constant used to represent the current project in the 3.0-final format. |
| */ |
| public static final String PROJECT_TAG = "<project>"; //$NON-NLS-1$ |
| |
| public static final String VERSION_1_0 = "1.0"; //$NON-NLS-1$ |
| public static final String VERSION_2_1 = "2.1"; //$NON-NLS-1$ |
| // The format shipped up to and including Eclipse 3.0 RC1 |
| public static final String VERSION_3_0_interim = "3.0.interim"; //$NON-NLS-1$ |
| // The format shipped in Eclipse 3.0 final |
| public static final String VERSION_3_0_final = "3.0"; //$NON-NLS-1$ |
| |
| private static final String BUILD_TYPE_SEPARATOR = ","; //$NON-NLS-1$ |
| private static final int[] DEFAULT_BUILD_TYPES = new int[] { |
| IncrementalProjectBuilder.INCREMENTAL_BUILD, |
| IncrementalProjectBuilder.FULL_BUILD }; |
| |
| /** |
| * Returns a launch configuration from the given ICommand arguments. If the |
| * given arguments are from an old-style external tool, an unsaved working |
| * copy will be created from the arguments and returned. |
| * |
| * @param commandArgs |
| * the builder ICommand arguments |
| * @return a launch configuration, a launch configuration working copy, or |
| * <code>null</code> if not possible. |
| */ |
| public static ILaunchConfiguration configFromBuildCommandArgs(IProject project, Map<String, String> commandArgs, String[] version) { |
| String configHandle = commandArgs.get(LAUNCH_CONFIG_HANDLE); |
| if (configHandle == null) { |
| // Probably an old-style (Eclipse 1.0 or 2.0) external tool. Try to |
| // migrate. |
| version[0] = VERSION_1_0; |
| return ExternalToolMigration.configFromArgumentMap(commandArgs); |
| } |
| ILaunchManager manager = DebugPlugin.getDefault().getLaunchManager(); |
| ILaunchConfiguration configuration = null; |
| if (configHandle.startsWith(PROJECT_TAG)) { |
| version[0] = VERSION_3_0_final; |
| IPath path = new Path(configHandle); |
| IFile file = project.getFile(path.removeFirstSegments(1)); |
| if (file.exists()) { |
| configuration = manager.getLaunchConfiguration(file); |
| } |
| } else { |
| // Try treating the handle as a file name. |
| // This is the format used in 3.0 RC1. |
| IPath path = new Path(BUILDER_FOLDER_NAME).append(configHandle); |
| IFile file = project.getFile(path); |
| if (file.exists()) { |
| version[0] = VERSION_3_0_interim; |
| configuration = manager.getLaunchConfiguration(file); |
| } else { |
| try { |
| // Treat the configHandle as a memento. This is the format |
| // used in Eclipse 2.1. |
| configuration = manager |
| .getLaunchConfiguration(configHandle); |
| } catch (CoreException e) { |
| } |
| if (configuration != null) { |
| version[0] = VERSION_2_1; |
| } |
| } |
| } |
| return configuration; |
| } |
| |
| public static void configureTriggers(ILaunchConfiguration config, ICommand newCommand) throws CoreException { |
| newCommand.setBuilding(IncrementalProjectBuilder.FULL_BUILD, false); |
| newCommand.setBuilding(IncrementalProjectBuilder.INCREMENTAL_BUILD, false); |
| newCommand.setBuilding(IncrementalProjectBuilder.AUTO_BUILD, false); |
| newCommand.setBuilding(IncrementalProjectBuilder.CLEAN_BUILD, false); |
| String buildKinds = config.getAttribute(IExternalToolConstants.ATTR_RUN_BUILD_KINDS, (String) null); |
| int[] triggers = buildTypesToArray(buildKinds); |
| boolean isfull = false, isinc = false; |
| for (int i = 0; i < triggers.length; i++) { |
| switch (triggers[i]) { |
| case IncrementalProjectBuilder.FULL_BUILD: |
| newCommand.setBuilding(IncrementalProjectBuilder.FULL_BUILD, true); |
| isfull = true; |
| break; |
| case IncrementalProjectBuilder.INCREMENTAL_BUILD: |
| newCommand.setBuilding(IncrementalProjectBuilder.INCREMENTAL_BUILD, true); |
| isinc = true; |
| break; |
| case IncrementalProjectBuilder.AUTO_BUILD: |
| newCommand.setBuilding(IncrementalProjectBuilder.AUTO_BUILD, true); |
| break; |
| case IncrementalProjectBuilder.CLEAN_BUILD: |
| newCommand.setBuilding(IncrementalProjectBuilder.CLEAN_BUILD, true); |
| break; |
| default: |
| break; |
| } |
| } |
| if(!isfull && isinc) { |
| Map<String, String> args = newCommand.getArguments(); |
| if(args == null) { |
| args = new HashMap<>(); |
| } |
| newCommand.setBuilding(IncrementalProjectBuilder.FULL_BUILD, true); |
| args.put(INC_CLEAN, Boolean.TRUE.toString()); |
| newCommand.setArguments(args); |
| } |
| if (!config.getAttribute(IExternalToolConstants.ATTR_TRIGGERS_CONFIGURED, false)) { |
| ILaunchConfigurationWorkingCopy copy = config.getWorkingCopy(); |
| copy.setAttribute(IExternalToolConstants.ATTR_TRIGGERS_CONFIGURED, true); |
| copy.doSave(); |
| } |
| } |
| |
| /** |
| * Returns whether the given configuration is an "unmigrated" builder. |
| * Unmigrated builders are external tools that are stored in an old format |
| * but have not been migrated by the user. Old format builders are always |
| * translated into launch config working copies in memory, but they're not |
| * considered "migrated" until the config has been saved and the project |
| * spec updated. |
| * |
| * @param config |
| * the config to examine |
| * @return whether the given config represents an unmigrated builder |
| */ |
| public static boolean isUnmigratedConfig(ILaunchConfiguration config) { |
| return config.isWorkingCopy() |
| && ((ILaunchConfigurationWorkingCopy) config).getOriginal() == null; |
| } |
| |
| /** |
| * Converts the given config to a build command which is stored in the given |
| * command. |
| * |
| * @return the configured build command |
| */ |
| public static ICommand toBuildCommand(IProject project, ILaunchConfiguration config, ICommand command) throws CoreException { |
| Map<String, String> args = null; |
| if (isUnmigratedConfig(config)) { |
| // This config represents an old external tool builder that hasn't |
| // been edited. Try to find the old ICommand and reuse the |
| // arguments. |
| // The goal here is to not change the storage format of old, |
| // unedited builders. |
| ICommand[] commands = project.getDescription().getBuildSpec(); |
| for (int i = 0; i < commands.length; i++) { |
| ICommand projectCommand = commands[i]; |
| String name = ExternalToolMigration |
| .getNameFromCommandArgs(projectCommand.getArguments()); |
| if (name != null && name.equals(config.getName())) { |
| args = projectCommand.getArguments(); |
| break; |
| } |
| } |
| } else { |
| ILaunchConfiguration temp = config; |
| if (config instanceof ILaunchConfigurationWorkingCopy) { |
| ILaunchConfigurationWorkingCopy workingCopy = (ILaunchConfigurationWorkingCopy) config; |
| if (workingCopy.getOriginal() != null) { |
| temp = workingCopy.getOriginal(); |
| } |
| } |
| args = new HashMap<>(); |
| // Launch configuration builders are stored with a project-relative |
| // path |
| StringBuilder buffer = new StringBuilder(PROJECT_TAG); |
| // Append the project-relative path (workspace path minus first |
| // segment) |
| buffer.append('/').append(temp.getFile().getFullPath().removeFirstSegments(1)); |
| args.put(LAUNCH_CONFIG_HANDLE, buffer.toString()); |
| } |
| command.setBuilderName(ExternalToolBuilder.ID); |
| command.setArguments(args); |
| return command; |
| } |
| |
| /** |
| * Returns the folder where project builders should be stored or |
| * <code>null</code> if the folder could not be created |
| */ |
| public static IFolder getBuilderFolder(IProject project, boolean create) { |
| if (project == null) { // Bug #428479 |
| return null; |
| } |
| |
| IFolder folder = project.getFolder(BUILDER_FOLDER_NAME); |
| if (!folder.exists() && create) { |
| try { |
| folder.create(true, true, new NullProgressMonitor()); |
| } catch (CoreException e) { |
| return null; |
| } |
| } |
| return folder; |
| } |
| |
| /** |
| * Migrates the launch configuration working copy, which is based on an old- |
| * style external tool builder, to a new, saved launch configuration. The |
| * returned launch configuration will contain the same attributes as the |
| * given working copy with the exception of the configuration name, which |
| * may be changed during the migration. The name of the configuration will |
| * only be changed if the current name is not a valid name for a saved |
| * config. |
| * |
| * @param workingCopy |
| * the launch configuration containing attributes from an |
| * old-style project builder. |
| * @return ILaunchConfiguration a new, saved launch configuration whose |
| * attributes match those of the given working copy as well as |
| * possible |
| * @throws CoreException |
| * if an exception occurs while attempting to save the new |
| * launch configuration |
| */ |
| public static ILaunchConfiguration migrateBuilderConfiguration( |
| IProject project, ILaunchConfigurationWorkingCopy workingCopy) |
| throws CoreException { |
| workingCopy.setContainer(getBuilderFolder(project, true)); |
| // Before saving, make sure the name is valid |
| String name = workingCopy.getName(); |
| name = name.replace('/', '.'); |
| if (name.charAt(0) == ('.')) { |
| name = name.substring(1); |
| } |
| IStatus status = ResourcesPlugin.getWorkspace().validateName(name, |
| IResource.FILE); |
| if (!status.isOK()) { |
| name = "ExternalTool"; //$NON-NLS-1$ |
| } |
| name = DebugPlugin.getDefault().getLaunchManager().generateLaunchConfigurationName(name); |
| workingCopy.rename(name); |
| return workingCopy.doSave(); |
| } |
| |
| /** |
| * Converts the build types string into an array of build kinds. |
| * |
| * @param buildTypes |
| * the string of built types to convert |
| * @return the array of build kinds. |
| */ |
| public static int[] buildTypesToArray(String buildTypes) { |
| if (buildTypes == null || buildTypes.length() == 0) { |
| return DEFAULT_BUILD_TYPES; |
| } |
| |
| int count = 0; |
| boolean incremental = false; |
| boolean full = false; |
| boolean auto = false; |
| boolean clean = false; |
| |
| StringTokenizer tokenizer = new StringTokenizer(buildTypes, |
| BUILD_TYPE_SEPARATOR); |
| while (tokenizer.hasMoreTokens()) { |
| String token = tokenizer.nextToken(); |
| if (IExternalToolConstants.BUILD_TYPE_INCREMENTAL.equals(token)) { |
| if (!incremental) { |
| incremental = true; |
| count++; |
| } |
| } else if (IExternalToolConstants.BUILD_TYPE_FULL.equals(token)) { |
| if (!full) { |
| full = true; |
| count++; |
| } |
| } else if (IExternalToolConstants.BUILD_TYPE_AUTO.equals(token)) { |
| if (!auto) { |
| auto = true; |
| count++; |
| } |
| } else if (IExternalToolConstants.BUILD_TYPE_CLEAN.equals(token)) { |
| if (!clean) { |
| clean = true; |
| count++; |
| } |
| } |
| } |
| |
| int[] results = new int[count]; |
| count = 0; |
| if (incremental) { |
| results[count] = IncrementalProjectBuilder.INCREMENTAL_BUILD; |
| count++; |
| } |
| if (full) { |
| results[count] = IncrementalProjectBuilder.FULL_BUILD; |
| count++; |
| } |
| if (auto) { |
| results[count] = IncrementalProjectBuilder.AUTO_BUILD; |
| count++; |
| } |
| if (clean) { |
| results[count] = IncrementalProjectBuilder.CLEAN_BUILD; |
| count++; |
| } |
| |
| return results; |
| } |
| } |