From b09a3880fd91ece29369e4616371cadace1b628f Mon Sep 17 00:00:00 2001 From: Andrew Gvozdev Date: Thu, 31 Jan 2013 11:01:49 -0500 Subject: [PATCH] bug 403257: MinGW toolchain detection does not consider configuration-specific settings. --- .../MingwEnvironmentVariableSupplier.java | 202 ++++----------- .../gnu/mingw/MingwIsToolChainSupported.java | 17 +- .../org/eclipse/cdt/internal/core/MinGW.java | 244 ++++++++++++++++++ 3 files changed, 311 insertions(+), 152 deletions(-) create mode 100644 core/org.eclipse.cdt.core/utils/org/eclipse/cdt/internal/core/MinGW.java diff --git a/build/org.eclipse.cdt.managedbuilder.gnu.ui/src/org/eclipse/cdt/managedbuilder/gnu/mingw/MingwEnvironmentVariableSupplier.java b/build/org.eclipse.cdt.managedbuilder.gnu.ui/src/org/eclipse/cdt/managedbuilder/gnu/mingw/MingwEnvironmentVariableSupplier.java index c5dcb3c01a0..c2c0ec14a46 100644 --- a/build/org.eclipse.cdt.managedbuilder.gnu.ui/src/org/eclipse/cdt/managedbuilder/gnu/mingw/MingwEnvironmentVariableSupplier.java +++ b/build/org.eclipse.cdt.managedbuilder.gnu.ui/src/org/eclipse/cdt/managedbuilder/gnu/mingw/MingwEnvironmentVariableSupplier.java @@ -1,195 +1,104 @@ /******************************************************************************* - * Copyright (c) 2006, 2010 QNX Software Systems and others. + * Copyright (c) 2006, 2013 QNX Software Systems 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: - * QNX Software Systems - Initial API and implementation + * Doug Schaefer, QNX Software Systems - Initial API and implementation + * Andrew Gvozdev - Ability to use different MinGW versions in different cfg *******************************************************************************/ package org.eclipse.cdt.managedbuilder.gnu.mingw; import org.eclipse.cdt.core.CCorePlugin; import org.eclipse.cdt.core.envvar.IEnvironmentVariable; -import org.eclipse.cdt.core.settings.model.util.CDataUtil; +import org.eclipse.cdt.internal.core.MinGW; +import org.eclipse.cdt.internal.core.envvar.EnvironmentVariableManager; import org.eclipse.cdt.managedbuilder.core.IConfiguration; import org.eclipse.cdt.managedbuilder.envvar.IBuildEnvironmentVariable; import org.eclipse.cdt.managedbuilder.envvar.IConfigurationEnvironmentVariableSupplier; import org.eclipse.cdt.managedbuilder.envvar.IEnvironmentVariableProvider; -import org.eclipse.cdt.utils.PathUtil; +import org.eclipse.cdt.managedbuilder.internal.envvar.BuildEnvVar; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.Path; -import org.eclipse.core.runtime.Platform; /** - * @author Doug Schaefer - * * @noextend This class is not intended to be subclassed by clients. */ public class MingwEnvironmentVariableSupplier implements IConfigurationEnvironmentVariableSupplier { private static final String ENV_PATH = "PATH"; //$NON-NLS-1$ - - private static String envPathValueCached = null; - private static String envMingwHomeValueCached = null; - private static IPath binDir = null; - private static IPath msysBinDir = null; - - private static class MingwBuildEnvironmentVariable implements IBuildEnvironmentVariable { - private static final String PATH_SEPARATOR = ";"; //$NON-NLS-1$ - private final String name; - private final String value; - private final int operation; - - public MingwBuildEnvironmentVariable(String name, String value, int operation) { - this.name = name; - this.value = value; - this.operation = operation; - } - - @Override - public String getName() { - return name; - } - - @Override - public String getValue() { - return value; - } - - @Override - public int getOperation() { - return operation; - } - - @Override - public String getDelimiter() { - return PATH_SEPARATOR; - } - } + private static final String BACKSLASH = java.io.File.separator; + private static final String PATH_DELIMITER = EnvironmentVariableManager.getDefault().getDefaultDelimiter(); /** * @return location of $MINGW_HOME/bin folder on the file-system. + * @deprecated. Deprecated as of CDT 8.2. Note that MinGW root path in general may depend on configuration. * * If you use this do not cache results to ensure user preferences are accounted for. * Please rely on internal caching. */ + @Deprecated public static IPath getBinDir() { - locateMingw(); + IPath binDir = null; + String minGWHome = MinGW.getMinGWHome(); + if (minGWHome != null) { + binDir = new Path(minGWHome).append("bin"); //$NON-NLS-1$ + } return binDir; } /** * @return location of $MINGW_HOME/msys/bin folder on the file-system. + * @deprecated. Deprecated as of CDT 8.2. Note that MinGW root path in general may depend on configuration. * * If you use this do not cache results to ensure user preferences are accounted for. * Please rely on internal caching. */ + @Deprecated public static IPath getMsysBinDir() { - locateMingw(); + IPath msysBinDir = null; + String msysHome = MinGW.getMSysHome(); + if (msysHome != null) { + msysBinDir = new Path(msysHome).append("bin"); //$NON-NLS-1$ + } return msysBinDir; } - /** - * Locate MinGW directories. The results are judicially cached so it is reasonably cheap to call. - * The reason to call it each time is to check if user changed environment in preferences. - */ - private static void locateMingw() { - IEnvironmentVariable varPath = CCorePlugin.getDefault().getBuildEnvironmentManager().getVariable(ENV_PATH, null, true); - String envPathValue = varPath != null ? varPath.getValue() : null; - IEnvironmentVariable varMingwHome = CCorePlugin.getDefault().getBuildEnvironmentManager().getVariable("MINGW_HOME", null, true); //$NON-NLS-1$ - String envMingwHomeValue = varMingwHome != null ? varMingwHome.getValue() : null; - - if (CDataUtil.objectsEqual(envPathValue, envPathValueCached) && CDataUtil.objectsEqual(envMingwHomeValue, envMingwHomeValueCached)) { - return; - } - - envPathValueCached = envPathValue; - envMingwHomeValueCached = envMingwHomeValue; - - binDir = locateBinDir(); - msysBinDir = locateMsysBinDir(binDir); - } - - private static IPath locateBinDir() { - // Check $MINGW_HOME - IPath mingwBinDir = new Path(envMingwHomeValueCached + "\\bin"); //$NON-NLS-1$ - if (mingwBinDir.toFile().isDirectory()) { - return mingwBinDir; - } - - // Try the mingw directory in the platform install directory - // CDT distributions like Wascana may distribute MinGW like that - IPath installPath = new Path(Platform.getInstallLocation().getURL().getFile()); - mingwBinDir = installPath.append("mingw\\bin"); //$NON-NLS-1$ - if (mingwBinDir.toFile().isDirectory()) { - return mingwBinDir; - } - - // Check for MinGW-w64 on Windows 64 bit, see http://mingw-w64.sourceforge.net/ - if (Platform.ARCH_X86_64.equals(Platform.getOSArch())) { - IPath gcc64Loc = PathUtil.findProgramLocation("x86_64-w64-mingw32-gcc.exe", envPathValueCached); //$NON-NLS-1$ - if (gcc64Loc != null) { - return gcc64Loc.removeLastSegments(1); - } - } - - // Look in PATH values. Look for mingw32-gcc.exe - // TODO: Since this dir is already in the PATH, why are we adding it here? - // This is really only to support isToolchainAvail. Must be a better way. - // AG: Because otherwise the toolchain won't be shown in the list of "supported" toolchains in UI - // when MinGW installed in custom location even if it is in the PATH - IPath gccLoc = PathUtil.findProgramLocation("mingw32-gcc.exe", envPathValueCached); //$NON-NLS-1$ - if (gccLoc != null) { - return gccLoc.removeLastSegments(1); - } - - // Try the default MinGW install dir - mingwBinDir = new Path("C:\\MinGW\\bin"); //$NON-NLS-1$ - if (mingwBinDir.toFile().isDirectory()) { - return mingwBinDir; - } - - return null; - } - - private static IPath locateMsysBinDir(IPath binPath) { - if (binPath != null) { - // Just look in the install location parent dir - IPath installPath = new Path(Platform.getInstallLocation().getURL().getFile()); - IPath msysBinPath = installPath.append("msys\\bin"); //$NON-NLS-1$ - if (msysBinPath.toFile().isDirectory()) { - return msysBinPath; - } - - if (envMingwHomeValueCached != null) { - msysBinPath = new Path(envMingwHomeValueCached + "\\msys\\1.0\\bin"); //$NON-NLS-1$ - if (msysBinPath.toFile().isDirectory()) { - return msysBinPath; - } - } - - // Try the new MinGW msys bin dir - msysBinPath = new Path("C:\\MinGW\\msys\\1.0\\bin"); //$NON-NLS-1$ - if (msysBinPath.toFile().isDirectory()) { - return msysBinPath; - } - } - return null; - } - @Override public IBuildEnvironmentVariable getVariable(String variableName, IConfiguration configuration, IEnvironmentVariableProvider provider) { - if (variableName.equals(ENV_PATH)) { - locateMingw(); - if (binDir != null) { - String pathStr = binDir.toOSString(); - if (msysBinDir != null) { - pathStr += MingwBuildEnvironmentVariable.PATH_SEPARATOR + msysBinDir.toOSString(); + if (variableName.equals(MinGW.ENV_MINGW_HOME)) { + IEnvironmentVariable varMinGWHome = CCorePlugin.getDefault().getBuildEnvironmentManager().getVariable(MinGW.ENV_MINGW_HOME, null, false); + if (varMinGWHome == null) { + // Contribute if the variable does not already come from workspace environment + String minGWHome = MinGW.getMinGWHome(); + if (minGWHome == null) { + // If the variable is not defined still show it in the environment variables list as a hint to user + minGWHome = ""; //$NON-NLS-1$ } - return new MingwBuildEnvironmentVariable(ENV_PATH, pathStr, IBuildEnvironmentVariable.ENVVAR_PREPEND); + return new BuildEnvVar(MinGW.ENV_MINGW_HOME, new Path(minGWHome).toOSString(), IBuildEnvironmentVariable.ENVVAR_REPLACE); } + return null; + + } else if (variableName.equals(MinGW.ENV_MSYS_HOME)) { + IEnvironmentVariable varMsysHome = CCorePlugin.getDefault().getBuildEnvironmentManager().getVariable(MinGW.ENV_MSYS_HOME, null, false); + if (varMsysHome == null) { + // Contribute if the variable does not already come from workspace environment + String msysHome = MinGW.getMSysHome(); + if (msysHome == null) { + // If the variable is not defined still show it in the environment variables list as a hint to user + msysHome = ""; //$NON-NLS-1$ + } + return new BuildEnvVar(MinGW.ENV_MSYS_HOME, new Path(msysHome).toOSString(), IBuildEnvironmentVariable.ENVVAR_REPLACE); + } + return null; + + } else if (variableName.equals(ENV_PATH)) { + @SuppressWarnings("nls") + String path = "${" + MinGW.ENV_MINGW_HOME + "}" + BACKSLASH + "bin" + PATH_DELIMITER + + "${" + MinGW.ENV_MSYS_HOME + "}" + BACKSLASH + "bin"; + return new BuildEnvVar(ENV_PATH, path, IBuildEnvironmentVariable.ENVVAR_PREPEND); } return null; @@ -197,10 +106,11 @@ public class MingwEnvironmentVariableSupplier implements IConfigurationEnvironme @Override public IBuildEnvironmentVariable[] getVariables(IConfiguration configuration, IEnvironmentVariableProvider provider) { - IBuildEnvironmentVariable path = getVariable(ENV_PATH, configuration, provider); - return path != null - ? new IBuildEnvironmentVariable[] { path } - : new IBuildEnvironmentVariable[0]; + return new IBuildEnvironmentVariable[] { + getVariable(MinGW.ENV_MINGW_HOME, configuration, provider), + getVariable(MinGW.ENV_MSYS_HOME, configuration, provider), + getVariable(ENV_PATH, configuration, provider), + }; } } diff --git a/build/org.eclipse.cdt.managedbuilder.gnu.ui/src/org/eclipse/cdt/managedbuilder/gnu/mingw/MingwIsToolChainSupported.java b/build/org.eclipse.cdt.managedbuilder.gnu.ui/src/org/eclipse/cdt/managedbuilder/gnu/mingw/MingwIsToolChainSupported.java index da6312958aa..52619dc0f6d 100644 --- a/build/org.eclipse.cdt.managedbuilder.gnu.ui/src/org/eclipse/cdt/managedbuilder/gnu/mingw/MingwIsToolChainSupported.java +++ b/build/org.eclipse.cdt.managedbuilder.gnu.ui/src/org/eclipse/cdt/managedbuilder/gnu/mingw/MingwIsToolChainSupported.java @@ -1,30 +1,35 @@ /********************************************************************** - * Copyright (c) 2007, 2010 QNX Software Systems and others. + * Copyright (c) 2007, 2013 QNX Software Systems 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: - * QNX Software Systems - Initial API and implementation + * Doug Schaefer (QNX Software Systems) - Initial API and implementation + * Andrew Gvozdev - Ability to use different MinGW versions in different cfg **********************************************************************/ package org.eclipse.cdt.managedbuilder.gnu.mingw; +import org.eclipse.cdt.core.envvar.IEnvironmentVariable; +import org.eclipse.cdt.internal.core.MinGW; import org.eclipse.cdt.managedbuilder.core.IManagedIsToolChainSupported; import org.eclipse.cdt.managedbuilder.core.IToolChain; +import org.eclipse.cdt.managedbuilder.internal.envvar.EnvironmentVariableManagerToolChain; import org.osgi.framework.Version; /** - * @author Doug Schaefer - * * @noextend This class is not intended to be subclassed by clients. */ public class MingwIsToolChainSupported implements IManagedIsToolChainSupported { + private static final String ENV_PATH = "PATH"; //$NON-NLS-1$ + @Override public boolean isSupported(IToolChain toolChain, Version version, String instance) { - // Only supported if we can find the mingw bin dir to run the compiler - return MingwEnvironmentVariableSupplier.getBinDir() != null; + IEnvironmentVariable var = new EnvironmentVariableManagerToolChain(toolChain).getVariable(ENV_PATH, true); + String envPath = var != null ? var.getValue() : null; + return MinGW.isAvailable(envPath); } } diff --git a/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/internal/core/MinGW.java b/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/internal/core/MinGW.java new file mode 100644 index 00000000000..7d02aae3607 --- /dev/null +++ b/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/internal/core/MinGW.java @@ -0,0 +1,244 @@ +/******************************************************************************* + * Copyright (c) 2012, 2013 Andrew Gvozdev 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: + * Andrew Gvozdev - Initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.internal.core; + +import java.util.Collections; +import java.util.Map; +import java.util.WeakHashMap; + +import org.eclipse.cdt.core.CCorePlugin; +import org.eclipse.cdt.core.envvar.IEnvironmentVariable; +import org.eclipse.cdt.core.settings.model.util.CDataUtil; +import org.eclipse.cdt.utils.PathUtil; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.Path; +import org.eclipse.core.runtime.Platform; + +/** + * A collection of MinGW-related utility methods. + */ +public class MinGW { + public static final String ENV_MINGW_HOME = "MINGW_HOME"; //$NON-NLS-1$ + public static final String ENV_MSYS_HOME = "MSYS_HOME"; //$NON-NLS-1$ + private static final String ENV_PATH = "PATH"; //$NON-NLS-1$ + + private static final boolean isWindowsPlatform = Platform.getOS().equals(Platform.OS_WIN32); + + private static String envPathValueCached = null; + private static String envMinGWHomeValueCached = null; + private static String minGWLocation = null; + private static boolean isMinGWLocationCached = false; + + private static String envMinGWHomeValueCached_msys = null; + private static String mSysLocation = null; + private static boolean isMSysLocationCached = false; + + private final static Map mingwLocationCache = Collections.synchronizedMap(new WeakHashMap(1)); + + /** + * @return The absolute path to MinGW root folder or {@code null} if not found + */ + private static String findMinGWRoot(String envPathValue, String envMinGWHomeValue) { + String rootValue = null; + + // Check $MINGW_HOME + if (envMinGWHomeValue != null && !envMinGWHomeValue.isEmpty()) { + IPath mingwBinDir = new Path(envMinGWHomeValue + "\\bin"); //$NON-NLS-1$ + if (mingwBinDir.toFile().isDirectory()) { + rootValue = mingwBinDir.removeLastSegments(1).toOSString(); + } + } + + // Try the mingw directory in the platform install directory + // CDT distributions like Wascana may distribute MinGW like that + if (rootValue == null) { + IPath installPath = new Path(Platform.getInstallLocation().getURL().getFile()); + IPath mingwBinDir = installPath.append("mingw\\bin"); //$NON-NLS-1$ + if (mingwBinDir.toFile().isDirectory()) { + rootValue = mingwBinDir.removeLastSegments(1).toOSString(); + } + } + + // Look in PATH values. Look for mingw32-gcc.exe or x86_64-w64-mingw32-gcc.exe + if (rootValue == null) { + rootValue = findMingwInPath(envPathValue); + } + + // Try the default MinGW install dir + if (rootValue == null) { + IPath mingwBinDir = new Path("C:\\MinGW"); //$NON-NLS-1$ + if (mingwBinDir.toFile().isDirectory()) { + rootValue = mingwBinDir.toOSString(); + } + } + + return rootValue; + } + + private static String findMingwInPath(String envPath) { + if (envPath == null) { + // $PATH from user preferences + IEnvironmentVariable varPath = CCorePlugin.getDefault().getBuildEnvironmentManager().getVariable(ENV_PATH, null, true); + if (varPath != null) { + envPath = varPath.getValue(); + } + } + + String mingwLocation = mingwLocationCache.get(envPath); + // check if WeakHashMap contains the key as null may be the cached value + if (mingwLocation == null && !mingwLocationCache.containsKey(envPath)) { + // Check for MinGW-w64 on Windows 64 bit, see http://mingw-w64.sourceforge.net/ + if (Platform.ARCH_X86_64.equals(Platform.getOSArch())) { + IPath gcc64Loc = PathUtil.findProgramLocation("x86_64-w64-mingw32-gcc.exe", envPath); //$NON-NLS-1$ + if (gcc64Loc != null) { + mingwLocation = gcc64Loc.removeLastSegments(2).toOSString(); + } + } + + // Look for mingw32-gcc.exe + if (mingwLocation == null) { + IPath gccLoc = PathUtil.findProgramLocation("mingw32-gcc.exe", envPath); //$NON-NLS-1$ + if (gccLoc != null) { + mingwLocation = gccLoc.removeLastSegments(2).toOSString(); + } + } + mingwLocationCache.put(envPath, mingwLocation); + } + + return mingwLocation; + } + + private static String findMSysRoot(String envMinGWHomeValue) { + String msysHome = null; + + // Look in the install location parent dir + IPath installPath = new Path(Platform.getInstallLocation().getURL().getFile()); + IPath installMsysBin = installPath.append("msys\\bin"); //$NON-NLS-1$ + if (installMsysBin.toFile().isDirectory()) { + msysHome = installMsysBin.removeLastSegments(1).toOSString(); + } + + // Look under $MINGW_HOME + if (msysHome == null) { + if (envMinGWHomeValue != null && !envMinGWHomeValue.isEmpty()) { + IPath minGwMsysBin = new Path(envMinGWHomeValue + "\\msys\\1.0\\bin"); //$NON-NLS-1$ + if (minGwMsysBin.toFile().isDirectory()) { + msysHome = minGwMsysBin.removeLastSegments(1).toOSString(); + } + } + } + + // Try under default MinGW dir + if (msysHome == null) { + IPath minGwMsysBin = new Path("C:\\MinGW\\msys\\1.0\\bin"); //$NON-NLS-1$ + if (minGwMsysBin.toFile().isDirectory()) { + msysHome = minGwMsysBin.removeLastSegments(1).toOSString(); + } + } + + // Try in default MSYS root folder + if (msysHome == null) { + IPath defaultMsysBin = new Path("C:\\msys\\1.0\\bin"); //$NON-NLS-1$ + if (defaultMsysBin.toFile().isDirectory()) { + msysHome = defaultMsysBin.removeLastSegments(1).toOSString(); + } + } + return msysHome; + } + + /** + * Find location where MinGW is installed. A number of locations is being checked, + * such as environment variable $MINGW_HOME, $PATH, Windows registry et al. + *

+ * If you use this do not cache results to ensure user preferences are accounted for. + * Please rely on internal caching. + * + * @return MinGW root ("/") path in Windows format. + */ + public static String getMinGWHome() { + if (!isWindowsPlatform) { + return null; + } + + IEnvironmentVariable varPath = CCorePlugin.getDefault().getBuildEnvironmentManager().getVariable(ENV_PATH, null, true); + String envPathValue = varPath != null ? varPath.getValue() : null; + IEnvironmentVariable varMinGWHome = CCorePlugin.getDefault().getBuildEnvironmentManager().getVariable(ENV_MINGW_HOME, null, true); + String envMinGWHomeValue = varMinGWHome != null ? varMinGWHome.getValue() : null; + + // isMinGWLocationCached is used to figure fact of caching when all cached objects are null + if (isMinGWLocationCached && CDataUtil.objectsEqual(envPathValue, envPathValueCached) && CDataUtil.objectsEqual(envMinGWHomeValue, envMinGWHomeValueCached)) { + return minGWLocation; + } + + minGWLocation = findMinGWRoot(envPathValue, envMinGWHomeValue); + envPathValueCached = envPathValue; + envMinGWHomeValueCached = envMinGWHomeValue; + isMinGWLocationCached = true; + + return minGWLocation; + } + + /** + * Find location where MSys is installed. Environment variable $MSYS_HOME and + * some predetermined locations are being checked. + *

+ * If you use this do not cache results to ensure user preferences are accounted for. + * Please rely on internal caching. + * + * @return MSys root ("/") path in Windows format. + */ + public static String getMSysHome() { + if (!isWindowsPlatform) { + return null; + } + + // Use $MSYS_HOME if defined + IEnvironmentVariable varMsysHome = CCorePlugin.getDefault().getBuildEnvironmentManager().getVariable(ENV_MSYS_HOME, null, true); + String msysHomeValue = varMsysHome != null ? varMsysHome.getValue() : null; + if (msysHomeValue != null) { + return msysHomeValue; + } + + String envMinGWHomeValue = getMinGWHome(); + + // isMSysLocationCached is used to figure whether it was cached when all cached objects are null + if (isMSysLocationCached && CDataUtil.objectsEqual(envMinGWHomeValue, envMinGWHomeValueCached_msys)) { + return mSysLocation; + } + + mSysLocation = findMSysRoot(envMinGWHomeValue); + envMinGWHomeValueCached_msys = envMinGWHomeValue; + isMSysLocationCached = true; + + return mSysLocation; + } + + /** + * Check if MinGW is available in the path. + * + * @param envPath - list of directories to search for MinGW separated + * by path separator (format of environment variable $PATH) + * or {@code null} to use current $PATH. + * @return {@code true} if MinGW is available, {@code false} otherwise. + */ + public static boolean isAvailable(String envPath) { + return isWindowsPlatform && findMingwInPath(envPath) != null; + } + + /** + * Check if MinGW is available in $PATH. + * + * @return {@code true} if MinGW is available, {@code false} otherwise. + */ + public static boolean isAvailable() { + return isWindowsPlatform && findMingwInPath(null) != null; + } +}