| /******************************************************************************* |
| * Copyright (c) 2000, 2016 QNX Software Systems 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: |
| * QNX Software Systems - Initial API and implementation |
| * Baltasar Belyavsky (Texas Instruments) - bug 340219: Project metadata files are saved unnecessarily |
| *******************************************************************************/ |
| package org.eclipse.cdt.make.internal.core; |
| |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Map.Entry; |
| import java.util.StringTokenizer; |
| |
| import org.eclipse.cdt.core.ErrorParserManager; |
| import org.eclipse.cdt.core.model.CoreModel; |
| import org.eclipse.cdt.core.settings.model.ICConfigurationDescription; |
| import org.eclipse.cdt.core.settings.model.ICProjectDescription; |
| import org.eclipse.cdt.core.settings.model.extension.CBuildData; |
| import org.eclipse.cdt.core.settings.model.extension.CConfigurationData; |
| import org.eclipse.cdt.make.core.IMakeBuilderInfo; |
| import org.eclipse.cdt.make.core.IMakeCommonBuildInfo; |
| import org.eclipse.cdt.make.core.MakeCorePlugin; |
| import org.eclipse.cdt.make.core.MakeProjectNature; |
| import org.eclipse.core.resources.ICommand; |
| import org.eclipse.core.resources.IProject; |
| import org.eclipse.core.resources.IProjectDescription; |
| import org.eclipse.core.resources.IncrementalProjectBuilder; |
| import org.eclipse.core.resources.ResourcesPlugin; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IConfigurationElement; |
| import org.eclipse.core.runtime.IExtension; |
| import org.eclipse.core.runtime.IPath; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.Path; |
| import org.eclipse.core.runtime.Platform; |
| import org.eclipse.core.runtime.Preferences; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.core.variables.VariablesPlugin; |
| |
| public class BuildInfoFactory { |
| |
| private static final String PREFIX = MakeCorePlugin.getUniqueIdentifier(); |
| |
| static final String BUILD_COMMAND = PREFIX + ".buildCommand"; //$NON-NLS-1$ |
| static final String BUILD_LOCATION = PREFIX + ".buildLocation"; //$NON-NLS-1$ |
| static final String STOP_ON_ERROR = PREFIX + ".stopOnError"; //$NON-NLS-1$ |
| static final String USE_DEFAULT_BUILD_CMD = PREFIX + ".useDefaultBuildCmd"; //$NON-NLS-1$ |
| static final String BUILD_TARGET_AUTO = PREFIX + ".autoBuildTarget"; //$NON-NLS-1$ |
| static final String BUILD_TARGET_INCREMENTAL = PREFIX + ".incrementalBuildTarget"; //$NON-NLS-1$ |
| static final String BUILD_TARGET_FULL = PREFIX + ".fullBuildTarget"; //$NON-NLS-1$ |
| static final String BUILD_TARGET_CLEAN = PREFIX + ".cleanBuildTarget"; //$NON-NLS-1$ |
| static final String BUILD_FULL_ENABLED = PREFIX + ".enableFullBuild"; //$NON-NLS-1$ |
| static final String BUILD_CLEAN_ENABLED = PREFIX + ".enableCleanBuild"; //$NON-NLS-1$ |
| static final String BUILD_INCREMENTAL_ENABLED = PREFIX + ".enabledIncrementalBuild"; //$NON-NLS-1$ |
| static final String BUILD_AUTO_ENABLED = PREFIX + ".enableAutoBuild"; //$NON-NLS-1$ |
| static final String BUILD_ARGUMENTS = PREFIX + ".buildArguments"; //$NON-NLS-1$ |
| static final String ENVIRONMENT = PREFIX + ".environment"; //$NON-NLS-1$ |
| static final String BUILD_APPEND_ENVIRONMENT = PREFIX + ".append_environment"; //$NON-NLS-1$ |
| |
| private abstract static class AbstractBuildInfo implements IMakeBuilderInfo { |
| |
| @Override |
| public void setUseDefaultBuildCmd(boolean on) throws CoreException { |
| putString(USE_DEFAULT_BUILD_CMD, String.valueOf(on)); |
| } |
| |
| @Override |
| public boolean isDefaultBuildCmd() { |
| if (getString(USE_DEFAULT_BUILD_CMD) == null) { // if no property |
| // then default to |
| // true |
| return true; |
| } |
| return getBoolean(USE_DEFAULT_BUILD_CMD); |
| } |
| |
| @Override |
| public String getBuildAttribute(String name, String defaultValue) { |
| String value = getString(name); |
| if (value == null) { |
| if (IMakeCommonBuildInfo.BUILD_COMMAND.equals(name)) { |
| value = getString(BuildInfoFactory.BUILD_COMMAND); |
| } else if (IMakeCommonBuildInfo.BUILD_ARGUMENTS.equals(name)) { |
| value = getString(BuildInfoFactory.BUILD_ARGUMENTS); |
| } else if (IMakeCommonBuildInfo.BUILD_LOCATION.equals(name)) { |
| value = getString(BuildInfoFactory.BUILD_LOCATION); |
| } else if (IMakeBuilderInfo.BUILD_TARGET_AUTO.equals(name)) { |
| value = getString(BuildInfoFactory.BUILD_TARGET_AUTO); |
| } else if (IMakeBuilderInfo.BUILD_TARGET_CLEAN.equals(name)) { |
| value = getString(BuildInfoFactory.BUILD_TARGET_CLEAN); |
| } else if (IMakeBuilderInfo.BUILD_TARGET_INCREMENTAL.equals(name)) { |
| value = getString(BuildInfoFactory.BUILD_TARGET_INCREMENTAL); |
| } |
| } |
| return value != null ? value : defaultValue != null ? defaultValue : ""; //$NON-NLS-1$ |
| } |
| |
| @Override |
| public void setBuildAttribute(String name, String value) throws CoreException { |
| putString(name, value); |
| } |
| |
| @Override |
| public Map<String, String> getExpandedEnvironment() { |
| Map<String, String> env = getEnvironment(); |
| HashMap<String, String> envMap = new HashMap<>(env.entrySet().size()); |
| for (Map.Entry<String, String> entry : env.entrySet()) { |
| String key = entry.getKey(); |
| String value = entry.getValue(); |
| // translate any string substitution variables |
| String translated = value; |
| try { |
| translated = VariablesPlugin.getDefault().getStringVariableManager() |
| .performStringSubstitution(value, false); |
| } catch (CoreException e) { |
| } |
| envMap.put(key, translated); |
| } |
| return envMap; |
| } |
| |
| @Override |
| public void setBuildCommand(IPath location) throws CoreException { |
| putString(IMakeCommonBuildInfo.BUILD_COMMAND, null); |
| putString(BuildInfoFactory.BUILD_COMMAND, location.toString()); |
| } |
| |
| @Override |
| public IPath getBuildCommand() { |
| if (isDefaultBuildCmd()) { |
| String command = getBuildParameter("defaultCommand"); //$NON-NLS-1$ |
| if (command == null) { |
| return new Path("make"); //$NON-NLS-1$ |
| } |
| return new Path(command); |
| } |
| String result = getBuildAttribute(IMakeCommonBuildInfo.BUILD_COMMAND, |
| getString(BuildInfoFactory.BUILD_COMMAND)); |
| try { |
| result = VariablesPlugin.getDefault().getStringVariableManager().performStringSubstitution(result, |
| false); |
| } catch (CoreException e) { |
| } |
| return new Path(result); |
| } |
| |
| protected String getBuildParameter(String name) { |
| IExtension extension = Platform.getExtensionRegistry().getExtension(ResourcesPlugin.PI_RESOURCES, |
| ResourcesPlugin.PT_BUILDERS, getBuilderID()); |
| if (extension == null) |
| return null; |
| IConfigurationElement[] configs = extension.getConfigurationElements(); |
| if (configs.length == 0) |
| return null; |
| // The nature exists, or this builder doesn't specify a nature |
| IConfigurationElement[] runElement = configs[0].getChildren("run"); //$NON-NLS-1$ |
| IConfigurationElement[] paramElement = runElement[0].getChildren("parameter"); //$NON-NLS-1$ |
| for (int i = 0; i < paramElement.length; i++) { |
| if (paramElement[i].getAttribute("name").equals(name)) { //$NON-NLS-1$ |
| return paramElement[i].getAttribute("value"); //$NON-NLS-1$ |
| } |
| } |
| return null; |
| } |
| |
| protected abstract String getBuilderID(); |
| |
| @Override |
| public void setBuildLocation(IPath location) throws CoreException { |
| putString(IMakeCommonBuildInfo.BUILD_LOCATION, null); |
| putString(BuildInfoFactory.BUILD_LOCATION, location.toString()); |
| } |
| |
| @Override |
| public IPath getBuildLocation() { |
| String result = getBuildAttribute(IMakeCommonBuildInfo.BUILD_LOCATION, |
| getString(BuildInfoFactory.BUILD_LOCATION)); |
| try { |
| result = VariablesPlugin.getDefault().getStringVariableManager().performStringSubstitution(result, |
| false); |
| } catch (CoreException e) { |
| } |
| return new Path(result); |
| } |
| |
| @Override |
| public String getBuildArguments() { |
| String result = getBuildAttribute(IMakeCommonBuildInfo.BUILD_ARGUMENTS, |
| getString(BuildInfoFactory.BUILD_ARGUMENTS)); |
| if (result == null) { |
| return ""; //$NON-NLS-1$ |
| } |
| try { |
| result = VariablesPlugin.getDefault().getStringVariableManager().performStringSubstitution(result, |
| false); |
| } catch (CoreException e) { |
| } |
| return result; |
| } |
| |
| @Override |
| public void setBuildArguments(String args) throws CoreException { |
| putString(IMakeCommonBuildInfo.BUILD_ARGUMENTS, null); |
| putString(BuildInfoFactory.BUILD_ARGUMENTS, args); |
| } |
| |
| @Override |
| public void setStopOnError(boolean enabled) throws CoreException { |
| putString(STOP_ON_ERROR, String.valueOf(enabled)); |
| } |
| |
| @Override |
| public boolean isStopOnError() { |
| return getBoolean(STOP_ON_ERROR); |
| } |
| |
| @Override |
| public void setAutoBuildTarget(String target) throws CoreException { |
| putString(IMakeBuilderInfo.BUILD_TARGET_AUTO, null); |
| putString(BuildInfoFactory.BUILD_TARGET_AUTO, target); |
| } |
| |
| @Override |
| public String getAutoBuildTarget() { |
| String result = getBuildAttribute(IMakeBuilderInfo.BUILD_TARGET_AUTO, |
| getString(BuildInfoFactory.BUILD_TARGET_AUTO)); |
| try { |
| result = VariablesPlugin.getDefault().getStringVariableManager().performStringSubstitution(result, |
| false); |
| } catch (CoreException e) { |
| } |
| return result; |
| } |
| |
| @Override |
| public void setIncrementalBuildTarget(String target) throws CoreException { |
| putString(IMakeBuilderInfo.BUILD_TARGET_INCREMENTAL, null); |
| putString(BuildInfoFactory.BUILD_TARGET_INCREMENTAL, target); |
| } |
| |
| @Override |
| public String getIncrementalBuildTarget() { |
| String result = getBuildAttribute(IMakeBuilderInfo.BUILD_TARGET_INCREMENTAL, |
| getString(BuildInfoFactory.BUILD_TARGET_INCREMENTAL)); |
| try { |
| result = VariablesPlugin.getDefault().getStringVariableManager().performStringSubstitution(result, |
| false); |
| } catch (CoreException e) { |
| } |
| return result; |
| } |
| |
| @Override |
| public void setFullBuildTarget(String target) throws CoreException { |
| |
| } |
| |
| @Override |
| public String getFullBuildTarget() { |
| String result = getBuildAttribute(IMakeBuilderInfo.BUILD_TARGET_INCREMENTAL, |
| getString(BuildInfoFactory.BUILD_TARGET_INCREMENTAL)); |
| try { |
| result = VariablesPlugin.getDefault().getStringVariableManager().performStringSubstitution(result, |
| false); |
| } catch (CoreException e) { |
| } |
| return result; |
| } |
| |
| @Override |
| public void setCleanBuildTarget(String target) throws CoreException { |
| putString(IMakeBuilderInfo.BUILD_TARGET_CLEAN, null); |
| putString(BuildInfoFactory.BUILD_TARGET_CLEAN, target); |
| } |
| |
| @Override |
| public String getCleanBuildTarget() { |
| String result = getBuildAttribute(IMakeBuilderInfo.BUILD_TARGET_CLEAN, |
| getString(BuildInfoFactory.BUILD_TARGET_CLEAN)); |
| try { |
| result = VariablesPlugin.getDefault().getStringVariableManager().performStringSubstitution(result, |
| false); |
| } catch (CoreException e) { |
| } |
| return result; |
| } |
| |
| @Override |
| public void setAutoBuildEnable(boolean enabled) throws CoreException { |
| putString(BUILD_AUTO_ENABLED, String.valueOf(enabled)); |
| } |
| |
| @Override |
| public boolean isAutoBuildEnable() { |
| return getBoolean(BUILD_AUTO_ENABLED); |
| } |
| |
| @Override |
| public void setIncrementalBuildEnable(boolean enabled) throws CoreException { |
| putString(BUILD_INCREMENTAL_ENABLED, String.valueOf(enabled)); |
| } |
| |
| @Override |
| public boolean isIncrementalBuildEnabled() { |
| return getBoolean(BUILD_INCREMENTAL_ENABLED); |
| } |
| |
| @Override |
| public void setFullBuildEnable(boolean enabled) throws CoreException { |
| putString(BUILD_FULL_ENABLED, String.valueOf(enabled)); |
| } |
| |
| @Override |
| public boolean isFullBuildEnabled() { |
| return getBoolean(BUILD_FULL_ENABLED); |
| } |
| |
| @Override |
| public void setCleanBuildEnable(boolean enabled) throws CoreException { |
| putString(BUILD_CLEAN_ENABLED, String.valueOf(enabled)); |
| } |
| |
| @Override |
| public boolean isCleanBuildEnabled() { |
| return getBoolean(BUILD_CLEAN_ENABLED); |
| } |
| |
| @Override |
| public String[] getErrorParsers() { |
| String parsers = getString(ErrorParserManager.PREF_ERROR_PARSER); |
| if (parsers != null && !parsers.isEmpty()) { |
| StringTokenizer tok = new StringTokenizer(parsers, ";"); //$NON-NLS-1$ |
| List<String> list = new ArrayList<>(tok.countTokens()); |
| while (tok.hasMoreElements()) { |
| list.add(tok.nextToken()); |
| } |
| return list.toArray(new String[list.size()]); |
| } |
| return new String[0]; |
| } |
| |
| @Override |
| public void setErrorParsers(String[] parsers) throws CoreException { |
| StringBuilder buf = new StringBuilder(); |
| for (int i = 0; i < parsers.length; i++) { |
| buf.append(parsers[i]).append(';'); |
| } |
| putString(ErrorParserManager.PREF_ERROR_PARSER, buf.toString()); |
| } |
| |
| @Override |
| public Map<String, String> getEnvironment() { |
| return decodeMap(getString(ENVIRONMENT)); |
| } |
| |
| @Override |
| public void setEnvironment(Map<String, String> env) throws CoreException { |
| putString(ENVIRONMENT, encodeMap(env)); |
| } |
| |
| @Override |
| public boolean appendEnvironment() { |
| if (getString(BUILD_APPEND_ENVIRONMENT) != null) { |
| return getBoolean(BUILD_APPEND_ENVIRONMENT); |
| } |
| return true; |
| } |
| |
| @Override |
| public void setAppendEnvironment(boolean append) throws CoreException { |
| putString(BUILD_APPEND_ENVIRONMENT, String.valueOf(append)); |
| } |
| |
| public boolean getBoolean(String property) { |
| return Boolean.parseBoolean(getString(property)); |
| } |
| |
| protected Map<String, String> decodeMap(String value) { |
| Map<String, String> map = new HashMap<>(); |
| if (value != null) { |
| StringBuilder envStr = new StringBuilder(value); |
| String escapeChars = "|\\"; //$NON-NLS-1$ |
| char escapeChar = '\\'; |
| try { |
| while (envStr.length() > 0) { |
| int ndx = 0; |
| while (ndx < envStr.length()) { |
| if (escapeChars.indexOf(envStr.charAt(ndx)) != -1) { |
| if (envStr.charAt(ndx - 1) == escapeChar) { |
| // escaped '|' - remove '\' and continue on. |
| envStr.deleteCharAt(ndx - 1); |
| if (ndx == envStr.length()) { |
| break; |
| } |
| } |
| if (envStr.charAt(ndx) == '|') |
| break; |
| } |
| ndx++; |
| } |
| StringBuilder line = new StringBuilder(envStr.substring(0, ndx)); |
| int lndx = 0; |
| while (lndx < line.length()) { |
| if (line.charAt(lndx) == '=') { |
| if (line.charAt(lndx - 1) == escapeChar) { |
| // escaped '=' - remove '\' and continue on. |
| line.deleteCharAt(lndx - 1); |
| } else { |
| break; |
| } |
| } |
| lndx++; |
| } |
| map.put(line.substring(0, lndx), line.substring(lndx + 1)); |
| envStr.delete(0, ndx + 1); |
| } |
| } catch (StringIndexOutOfBoundsException e) { |
| } |
| } |
| return map; |
| } |
| |
| protected String encodeMap(Map<String, String> values) { |
| StringBuilder str = new StringBuilder(); |
| for (Entry<String, String> entry : values.entrySet()) { |
| str.append(escapeChars(entry.getKey(), "=|\\", '\\')); //$NON-NLS-1$ |
| str.append("="); //$NON-NLS-1$ |
| str.append(escapeChars(entry.getValue(), "|\\", '\\')); //$NON-NLS-1$ |
| str.append("|"); //$NON-NLS-1$ |
| } |
| return str.toString(); |
| } |
| |
| protected String escapeChars(String string, String escapeChars, char escapeChar) { |
| StringBuilder str = new StringBuilder(string); |
| for (int i = 0; i < str.length(); i++) { |
| if (escapeChars.indexOf(str.charAt(i)) != -1) { |
| str.insert(i, escapeChar); |
| i++; |
| } |
| } |
| return str.toString(); |
| } |
| |
| protected abstract void putString(String name, String value) throws CoreException; |
| |
| protected abstract String getString(String property); |
| } |
| |
| private static class BuildInfoPreference extends AbstractBuildInfo { |
| |
| private Preferences prefs; |
| private String builderID; |
| private boolean useDefaults; |
| |
| BuildInfoPreference(Preferences prefs, String builderID, boolean useDefaults) { |
| this.prefs = prefs; |
| this.builderID = builderID; |
| this.useDefaults = useDefaults; |
| } |
| |
| @Override |
| protected void putString(String name, String value) { |
| if (useDefaults) { |
| if (value != null) { |
| prefs.setDefault(name, value); |
| } |
| } else { |
| if (value == null) { |
| prefs.setValue(name, prefs.getDefaultString(name)); |
| return; |
| } |
| prefs.setValue(name, value); |
| } |
| } |
| |
| @Override |
| protected String getString(String property) { |
| if (!prefs.contains(property)) { |
| return null; |
| } |
| if (useDefaults) { |
| return prefs.getDefaultString(property); |
| } |
| return prefs.getString(property); |
| } |
| |
| @Override |
| protected String getBuilderID() { |
| return builderID; |
| } |
| } |
| |
| private static class BuildInfoProject extends AbstractBuildInfo { |
| |
| private IProject project; |
| private String builderID; |
| private Map<String, String> args; |
| |
| BuildInfoProject(IProject project, String builderID) throws CoreException { |
| this.project = project; |
| this.builderID = builderID; |
| |
| ICommand builder = null; |
| |
| // first, give the build-system a chance to return the build-command overlayed with data managed by it |
| ICProjectDescription cProjectDescription = CoreModel.getDefault().getProjectDescription(project, false); |
| if (cProjectDescription != null) { |
| ICConfigurationDescription cConfigDescription = cProjectDescription.getActiveConfiguration(); |
| CConfigurationData configurationData = cConfigDescription.getConfigurationData(); |
| if (configurationData != null) { |
| CBuildData buildData = configurationData.getBuildData(); |
| if (buildData != null) { |
| builder = buildData.getBuildSpecCommand(); |
| } |
| } |
| } |
| |
| if (builder == null) { |
| builder = MakeProjectNature.getBuildSpec(project.getDescription(), builderID); |
| if (builder == null) { |
| throw new CoreException(new Status(IStatus.ERROR, MakeCorePlugin.getUniqueIdentifier(), -1, |
| MakeMessages.getString("BuildInfoFactory.Missing_Builder") + builderID, null)); //$NON-NLS-1$ |
| } |
| } |
| |
| Map<String, String> builderArgs = builder.getArguments(); |
| args = builderArgs; |
| } |
| |
| @Override |
| protected void putString(String name, String value) throws CoreException { |
| String curValue = args.get(name); |
| if (curValue != null && curValue.equals(value)) { |
| return; |
| } |
| if (value == null) { |
| args.remove(name); |
| } else { |
| args.put(name, value); |
| } |
| IProjectDescription description = project.getDescription(); |
| ICommand builder = MakeProjectNature.getBuildSpec(description, builderID); |
| builder.setArguments(args); |
| builder.setBuilding(IncrementalProjectBuilder.AUTO_BUILD, isAutoBuildEnable()); |
| builder.setBuilding(IncrementalProjectBuilder.FULL_BUILD, isFullBuildEnabled()); |
| builder.setBuilding(IncrementalProjectBuilder.INCREMENTAL_BUILD, isIncrementalBuildEnabled()); |
| builder.setBuilding(IncrementalProjectBuilder.CLEAN_BUILD, isCleanBuildEnabled()); |
| MakeProjectNature.setBuildSpec(description, builder); |
| project.setDescription(description, null); |
| } |
| |
| @Override |
| protected String getString(String name) { |
| return args.get(name); |
| } |
| |
| @Override |
| protected String getBuilderID() { |
| return builderID; |
| } |
| } |
| |
| private static class BuildInfoMap extends AbstractBuildInfo { |
| |
| private Map<String, String> args; |
| private String builderID; |
| |
| BuildInfoMap(Map<String, String> args, String builderID) { |
| this.args = args; |
| this.builderID = builderID; |
| } |
| |
| @Override |
| protected void putString(String name, String value) { |
| if (value == null) { |
| args.remove(name); |
| } else { |
| args.put(name, value); |
| } |
| } |
| |
| @Override |
| protected String getString(String name) { |
| return args.get(name); |
| } |
| |
| @Override |
| protected String getBuilderID() { |
| return builderID; |
| } |
| } |
| |
| public static IMakeBuilderInfo create(Preferences prefs, String builderID, boolean useDefaults) { |
| return new BuildInfoFactory.BuildInfoPreference(prefs, builderID, useDefaults); |
| } |
| |
| public static IMakeBuilderInfo create(IProject project, String builderID) throws CoreException { |
| return new BuildInfoFactory.BuildInfoProject(project, builderID); |
| } |
| |
| public static IMakeBuilderInfo create(Map<String, String> args, String builderID) { |
| return new BuildInfoFactory.BuildInfoMap(args, builderID); |
| } |
| } |