1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-04-29 19:45:01 +02:00

Cygwin Specs Detector using EFSExtensionProvider

This commit is contained in:
Andrew Gvozdev 2011-07-13 14:20:45 -04:00
parent cf55505f7b
commit 3bf0f468ad
9 changed files with 321 additions and 34 deletions

View file

@ -1026,17 +1026,13 @@ public class GCCBuildCommandParserTest extends TestCase {
// parse line // parse line
parser.startup(cfgDescription); parser.startup(cfgDescription);
parser.processLine("gcc " parser.processLine("gcc "
+ " -IC:\\path" + " -IX:\\path"
+ " file.cpp"); + " file.cpp");
parser.shutdown(); parser.shutdown();
// check populated entries // check populated entries
IPath path0 = new Path("C:\\path").setDevice(project.getLocation().getDevice());
{
List<ICLanguageSettingEntry> entries = parser.getSettingEntries(cfgDescription, file, languageId); List<ICLanguageSettingEntry> entries = parser.getSettingEntries(cfgDescription, file, languageId);
CIncludePathEntry expected = new CIncludePathEntry(path0, 0); assertEquals(new CIncludePathEntry(new Path("X:\\path"), 0), entries.get(0));
assertEquals(expected, entries.get(0));
}
} }
/** /**

View file

@ -242,18 +242,24 @@ public abstract class AbstractLanguageSettingsOutputScanner extends LanguageSett
currentResource = findResource(parsedResourceName); currentResource = findResource(parsedResourceName);
/**
* URI of directory where the build is happening. This URI could point to a remote filesystem
* for remote builds. Most often it is the same filesystem as for currentResource but
* it can be different filesystem (and different URI schema).
*/
URI buildDirURI = null;
/** /**
* Where source tree starts if mapped. This kind of mapping is useful for example in cases when * Where source tree starts if mapped. This kind of mapping is useful for example in cases when
* the absolute path to the source file on the remote system is simulated inside a project in the * the absolute path to the source file on the remote system is simulated inside a project in the
* workspace. * workspace.
* This URI is rooted on the same filesystem where currentResource resides. In general this filesystem
* (or even URI schema) does not have to match that of buildDirURI.
*/ */
URI mappedRootURI = null; URI mappedRootURI = null;
URI buildDirURI = null;
if (isResolvingPaths) { if (isResolvingPaths) {
if (currentResource!=null) {
mappedRootURI = getMappedRootURI(currentResource, parsedResourceName); mappedRootURI = getMappedRootURI(currentResource, parsedResourceName);
}
buildDirURI = getBuildDirURI(mappedRootURI); buildDirURI = getBuildDirURI(mappedRootURI);
} }
@ -346,7 +352,7 @@ public abstract class AbstractLanguageSettingsOutputScanner extends LanguageSett
ICLanguageSettingEntry entry; ICLanguageSettingEntry entry;
String resolvedPath = null; String resolvedPath = null;
URI uri = determineURI(parsedPath, baseURI); URI uri = determineMappedURI(parsedPath, baseURI);
IResource rc = null; IResource rc = null;
if (uri != null && uri.isAbsolute()) { if (uri != null && uri.isAbsolute()) {
rc = findResourceForLocationURI(uri, optionParser.kind, currentProject); rc = findResourceForLocationURI(uri, optionParser.kind, currentProject);
@ -417,7 +423,7 @@ public abstract class AbstractLanguageSettingsOutputScanner extends LanguageSett
return sourceFile; return sourceFile;
} }
private URI getBuildDirURI(URI mappedRootURI) { protected URI getBuildDirURI(URI mappedRootURI) {
URI buildDirURI = null; URI buildDirURI = null;
URI cwdURI = null; URI cwdURI = null;
@ -457,13 +463,13 @@ public abstract class AbstractLanguageSettingsOutputScanner extends LanguageSett
} }
/** /**
* Determine URI appending to baseURI when possible. * Determine URI on the local filesystem considering possible mapping.
* *
* @param pathStr - path to the resource, can be absolute or relative * @param pathStr - path to the resource, can be absolute or relative
* @param baseURI - base {@link URI} where path to the resource is rooted * @param baseURI - base {@link URI} where path to the resource is rooted
* @return {@link URI} of the resource * @return {@link URI} of the resource
*/ */
private static URI determineURI(String pathStr, URI baseURI) { private static URI determineMappedURI(String pathStr, URI baseURI) {
URI uri = null; URI uri = null;
if (baseURI==null) { if (baseURI==null) {
@ -476,9 +482,15 @@ public abstract class AbstractLanguageSettingsOutputScanner extends LanguageSett
// careful not to use Path here but 'pathStr' as String as we want to properly navigate symlinks // careful not to use Path here but 'pathStr' as String as we want to properly navigate symlinks
uri = resolvePathFromBaseLocation(pathStr, baseLocation); uri = resolvePathFromBaseLocation(pathStr, baseLocation);
} else { } else {
// use canonicalized path here, in particular replace all '\' with '/' for Windows paths // location on a remote filesystem
Path path = new Path(pathStr); IPath path = new Path(pathStr); // use canonicalized path here, in particular replace all '\' with '/' for Windows paths
uri = EFSExtensionManager.getDefault().append(baseURI, path.toString()); URI remoteUri = EFSExtensionManager.getDefault().append(baseURI, path.toString());
if (remoteUri!=null) {
String localPath = EFSExtensionManager.getDefault().getMappedPath(remoteUri);
if (localPath!=null) {
uri = org.eclipse.core.filesystem.URIUtil.toURI(localPath);
}
}
} }
if (uri == null) { if (uri == null) {
@ -642,7 +654,11 @@ public abstract class AbstractLanguageSettingsOutputScanner extends LanguageSett
* @param parsedResourceName - path as appears in the output * @param parsedResourceName - path as appears in the output
* @return mapped path as URI * @return mapped path as URI
*/ */
private static URI getMappedRootURI(IResource sourceFile, String parsedResourceName) { protected URI getMappedRootURI(IResource sourceFile, String parsedResourceName) {
if (currentResource==null) {
return null;
}
URI fileURI = sourceFile.getLocationURI(); URI fileURI = sourceFile.getLocationURI();
String mappedRoot = "/"; //$NON-NLS-1$ String mappedRoot = "/"; //$NON-NLS-1$
@ -674,11 +690,12 @@ public abstract class AbstractLanguageSettingsOutputScanner extends LanguageSett
private static URI resolvePathFromBaseLocation(String name, IPath baseLocation) { private static URI resolvePathFromBaseLocation(String name, IPath baseLocation) {
String pathName = name; String pathName = name;
if (baseLocation != null && !baseLocation.isEmpty()) { if (baseLocation != null && !baseLocation.isEmpty()) {
pathName = pathName.replace(File.separatorChar, '/');
String device = new Path(pathName).getDevice(); String device = new Path(pathName).getDevice();
if (device==null || device.equals(baseLocation.getDevice())) {
if (device != null && device.length() > 0) { if (device != null && device.length() > 0) {
pathName = pathName.substring(device.length()); pathName = pathName.substring(device.length());
} }
pathName = pathName.replace(File.separatorChar, '/');
baseLocation = baseLocation.addTrailingSeparator(); baseLocation = baseLocation.addTrailingSeparator();
if (pathName.startsWith("/")) { //$NON-NLS-1$ if (pathName.startsWith("/")) { //$NON-NLS-1$
@ -686,6 +703,7 @@ public abstract class AbstractLanguageSettingsOutputScanner extends LanguageSett
} }
pathName = baseLocation.toString() + pathName; pathName = baseLocation.toString() + pathName;
} }
}
try { try {
File file = new File(pathName); File file = new File(pathName);

View file

@ -1,5 +1,5 @@
/******************************************************************************* /*******************************************************************************
* Copyright (c) 2009 Andrew Gvozdev and others. * Copyright (c) 2010, 2011 Andrew Gvozdev and others.
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at * which accompanies this distribution, and is available at
@ -33,6 +33,7 @@ import org.eclipse.cdt.core.testplugin.ResourceHelper;
import org.eclipse.cdt.internal.core.XmlUtil; import org.eclipse.cdt.internal.core.XmlUtil;
import org.eclipse.cdt.managedbuilder.internal.scannerconfig.AbstractBuiltinSpecsDetector; import org.eclipse.cdt.managedbuilder.internal.scannerconfig.AbstractBuiltinSpecsDetector;
import org.eclipse.cdt.managedbuilder.internal.scannerconfig.GCCBuiltinSpecsDetector; import org.eclipse.cdt.managedbuilder.internal.scannerconfig.GCCBuiltinSpecsDetector;
import org.eclipse.cdt.managedbuilder.internal.scannerconfig.GCCBuiltinSpecsDetectorCygwin;
import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IPath;
@ -280,7 +281,6 @@ public class GCCBuiltinSpecsDetectorTest extends TestCase {
String projectName = getName(); String projectName = getName();
IProject project = ResourceHelper.createCDTProjectWithConfig(projectName); IProject project = ResourceHelper.createCDTProjectWithConfig(projectName);
ICConfigurationDescription[] cfgDescriptions = getConfigurationDescriptions(project); ICConfigurationDescription[] cfgDescriptions = getConfigurationDescriptions(project);
ICConfigurationDescription cfgDescription = cfgDescriptions[0]; ICConfigurationDescription cfgDescription = cfgDescriptions[0];
AbstractBuiltinSpecsDetector detector = new GCCBuiltinSpecsDetector() { AbstractBuiltinSpecsDetector detector = new GCCBuiltinSpecsDetector() {
@ -685,4 +685,62 @@ public class GCCBuiltinSpecsDetectorTest extends TestCase {
assertEquals(1, entries.size()); assertEquals(1, entries.size());
} }
public void testGCCBuiltinSpecsDetector_Cygwin_NoProject() throws Exception {
String windowsLocation;
String cygwinLocation = "/usr/include";
try {
windowsLocation = ResourceHelper.cygwinToWindowsPath(cygwinLocation);
} catch (UnsupportedOperationException e) {
// Skip the test if Cygwin is not available.
return;
}
assertTrue("windowsLocation=["+windowsLocation+"]", new Path(windowsLocation).getDevice()!=null);
AbstractBuiltinSpecsDetector detector = new GCCBuiltinSpecsDetectorCygwin();
detector.startup(null);
detector.processLine("#include <...> search starts here:");
detector.processLine(" /usr/include");
detector.processLine("End of search list.");
detector.shutdown();
// check populated entries
List<ICLanguageSettingEntry> entries = detector.getSettingEntries(null, null, null);
assertEquals(new CIncludePathEntry(new Path(windowsLocation), ICSettingEntry.BUILTIN | ICSettingEntry.READONLY), entries.get(0));
assertEquals(1, entries.size());
}
public void testGCCBuiltinSpecsDetector_Cygwin_Configuration() throws Exception {
String windowsLocation;
String cygwinLocation = "/usr/include";
try {
windowsLocation = ResourceHelper.cygwinToWindowsPath(cygwinLocation);
} catch (UnsupportedOperationException e) {
// Skip the test if Cygwin is not available.
return;
}
assertTrue("windowsLocation=["+windowsLocation+"]", new Path(windowsLocation).getDevice()!=null);
// Create model project and folders to test
String projectName = getName();
IProject project = ResourceHelper.createCDTProjectWithConfig(projectName);
ICConfigurationDescription[] cfgDescriptions = getConfigurationDescriptions(project);
ICConfigurationDescription cfgDescription = cfgDescriptions[0];
AbstractBuiltinSpecsDetector detector = new GCCBuiltinSpecsDetectorCygwin();
detector.startup(cfgDescription);
detector.processLine("#include <...> search starts here:");
detector.processLine(" /usr/include");
detector.processLine("End of search list.");
detector.shutdown();
// check populated entries
List<ICLanguageSettingEntry> entries = detector.getSettingEntries(null, null, null);
assertEquals(new CIncludePathEntry(new Path(windowsLocation), ICSettingEntry.BUILTIN | ICSettingEntry.READONLY), entries.get(0));
assertEquals(1, entries.size());
}
} }

View file

@ -609,7 +609,7 @@
<language-scope id="org.eclipse.cdt.core.g++"/> <language-scope id="org.eclipse.cdt.core.g++"/>
</provider> </provider>
<provider <provider
class="org.eclipse.cdt.managedbuilder.internal.scannerconfig.GCCBuiltinSpecsDetector" class="org.eclipse.cdt.managedbuilder.internal.scannerconfig.GCCBuiltinSpecsDetectorCygwin"
id="org.eclipse.cdt.managedbuilder.core.cygwin.gcc.specs.detector" id="org.eclipse.cdt.managedbuilder.core.cygwin.gcc.specs.detector"
name="CDT GCC Builtin Compiler Settings Cygwin" name="CDT GCC Builtin Compiler Settings Cygwin"
parameter="sh -c &quot;${COMMAND} -E -P -v -dD ${INPUTS}&quot;"> parameter="sh -c &quot;${COMMAND} -E -P -v -dD ${INPUTS}&quot;">

View file

@ -13,6 +13,7 @@ package org.eclipse.cdt.managedbuilder.internal.scannerconfig;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.net.URI;
import java.net.URL; import java.net.URL;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
@ -79,6 +80,9 @@ public abstract class AbstractBuiltinSpecsDetector extends AbstractLanguageSetti
protected java.io.File specFile = null; protected java.io.File specFile = null;
protected boolean preserveSpecFile = false; protected boolean preserveSpecFile = false;
protected URI mappedRootURI = null;
protected URI buildDirURI = null;
/** /**
* TODO * TODO
*/ */
@ -151,6 +155,22 @@ public abstract class AbstractBuiltinSpecsDetector extends AbstractLanguageSetti
return currentLanguageId; return currentLanguageId;
} }
@Override
protected URI getMappedRootURI(IResource sourceFile, String parsedResourceName) {
if (mappedRootURI==null) {
mappedRootURI = super.getMappedRootURI(sourceFile, parsedResourceName);
}
return mappedRootURI;
}
@Override
protected URI getBuildDirURI(URI mappedRootURI) {
if (buildDirURI==null) {
buildDirURI = super.getBuildDirURI(mappedRootURI);
}
return buildDirURI;
}
@Override @Override
public void startup(ICConfigurationDescription cfgDescription) throws CoreException { public void startup(ICConfigurationDescription cfgDescription) throws CoreException {
// for workspace provider cfgDescription is used to figure out the current project for build console // for workspace provider cfgDescription is used to figure out the current project for build console
@ -165,10 +185,16 @@ public abstract class AbstractBuiltinSpecsDetector extends AbstractLanguageSetti
specFile = null; specFile = null;
currentCommandResolved = resolveCommand(currentLanguageId); currentCommandResolved = resolveCommand(currentLanguageId);
mappedRootURI = null;
buildDirURI = null;
} }
@Override @Override
public void shutdown() { public void shutdown() {
buildDirURI = null;
mappedRootURI = null;
if (detectedSettingEntries!=null && detectedSettingEntries.size()>0) { if (detectedSettingEntries!=null && detectedSettingEntries.size()>0) {
groupEntries(detectedSettingEntries); groupEntries(detectedSettingEntries);
setSettingEntries(currentCfgDescription, currentResource, currentLanguageId, detectedSettingEntries); setSettingEntries(currentCfgDescription, currentResource, currentLanguageId, detectedSettingEntries);

View file

@ -29,10 +29,10 @@ public class GCCBuiltinSpecsDetector extends AbstractBuiltinSpecsDetector implem
private static final String GCC_TOOLCHAIN_ID = "cdt.managedbuild.toolchain.gnu.base"; //$NON-NLS-1$ private static final String GCC_TOOLCHAIN_ID = "cdt.managedbuild.toolchain.gnu.base"; //$NON-NLS-1$
private enum State {NONE, EXPECTING_LOCAL_INCLUDE, EXPECTING_SYSTEM_INCLUDE, EXPECTING_FRAMEWORKS} private enum State {NONE, EXPECTING_LOCAL_INCLUDE, EXPECTING_SYSTEM_INCLUDE, EXPECTING_FRAMEWORKS}
State state = State.NONE; private State state = State.NONE;
@SuppressWarnings("nls") @SuppressWarnings("nls")
static final AbstractOptionParser[] optionParsers = { private static final AbstractOptionParser[] optionParsers = {
new IncludePathOptionParser("#include \"(\\S.*)\"", "$1", ICSettingEntry.BUILTIN | ICSettingEntry.READONLY | ICSettingEntry.LOCAL), new IncludePathOptionParser("#include \"(\\S.*)\"", "$1", ICSettingEntry.BUILTIN | ICSettingEntry.READONLY | ICSettingEntry.LOCAL),
new IncludePathOptionParser("#include <(\\S.*)>", "$1", ICSettingEntry.BUILTIN | ICSettingEntry.READONLY), new IncludePathOptionParser("#include <(\\S.*)>", "$1", ICSettingEntry.BUILTIN | ICSettingEntry.READONLY),
new IncludePathOptionParser("#framework <(\\S.*)>", "$1", ICSettingEntry.BUILTIN | ICSettingEntry.READONLY | ICSettingEntry.FRAMEWORKS_MAC), new IncludePathOptionParser("#framework <(\\S.*)>", "$1", ICSettingEntry.BUILTIN | ICSettingEntry.READONLY | ICSettingEntry.FRAMEWORKS_MAC),

View file

@ -0,0 +1,81 @@
/*******************************************************************************
* Copyright (c) 2009, 2011 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.managedbuilder.internal.scannerconfig;
import java.net.URI;
import java.net.URISyntaxException;
import org.eclipse.cdt.core.settings.model.ICSettingEntry;
import org.eclipse.core.resources.IResource;
/**
* Class to detect built-in compiler settings.
* The paths are converted to cygwin "filesystem" representation. Then
*
*/
public class GCCBuiltinSpecsDetectorCygwin extends GCCBuiltinSpecsDetector {
private static final URI CYGWIN_ROOT;
static {
try {
CYGWIN_ROOT = new URI("cygwin:/"); //$NON-NLS-1$
} catch (URISyntaxException e) {
// hey we know this works
throw new IllegalStateException(e);
}
}
@SuppressWarnings("nls")
private static final AbstractOptionParser[] optionParsers = {
new IncludePathOptionParser("#include \"(\\S.*)\"", "$1", ICSettingEntry.BUILTIN | ICSettingEntry.READONLY | ICSettingEntry.LOCAL),
new IncludePathOptionParser("#include <(\\S.*)>", "$1", ICSettingEntry.BUILTIN | ICSettingEntry.READONLY),
new MacroOptionParser("#define (\\S*\\(.*?\\)) *(.*)", "$1", "$2", ICSettingEntry.BUILTIN | ICSettingEntry.READONLY),
new MacroOptionParser("#define (\\S*) *(.*)", "$1", "$2", ICSettingEntry.BUILTIN | ICSettingEntry.READONLY),
};
@Override
protected AbstractOptionParser[] getOptionParsers() {
return optionParsers;
}
@Override
protected URI getMappedRootURI(IResource sourceFile, String parsedResourceName) {
if (mappedRootURI==null) {
mappedRootURI = super.getMappedRootURI(sourceFile, parsedResourceName);
if (mappedRootURI==null) {
mappedRootURI = CYGWIN_ROOT;
}
}
return mappedRootURI;
}
@Override
protected URI getBuildDirURI(URI mappedRootURI) {
if (buildDirURI==null) {
buildDirURI = super.getBuildDirURI(mappedRootURI);
if (buildDirURI==null) {
buildDirURI = CYGWIN_ROOT;
}
}
return buildDirURI;
}
@Override
public GCCBuiltinSpecsDetectorCygwin cloneShallow() throws CloneNotSupportedException {
return (GCCBuiltinSpecsDetectorCygwin) super.cloneShallow();
}
@Override
public GCCBuiltinSpecsDetectorCygwin clone() throws CloneNotSupportedException {
return (GCCBuiltinSpecsDetectorCygwin) super.clone();
}
}

View file

@ -751,5 +751,12 @@
factoryClass="org.eclipse.cdt.internal.core.resources.ResourceExclusionFactory"> factoryClass="org.eclipse.cdt.internal.core.resources.ResourceExclusionFactory">
</exclusionFactory> </exclusionFactory>
</extension> </extension>
<extension
point="org.eclipse.cdt.core.EFSExtensionProvider">
<EFSExtensionProvider
class="org.eclipse.cdt.internal.core.resources.CygwinEFSExtensionProvider"
scheme="cygwin">
</EFSExtensionProvider>
</extension>
</plugin> </plugin>

View file

@ -0,0 +1,101 @@
/*******************************************************************************
* Copyright (c) 2011, 2011 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.resources;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URI;
import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.core.EFSExtensionProvider;
import org.eclipse.core.runtime.Platform;
public class CygwinEFSExtensionProvider extends EFSExtensionProvider {
@Override
public String getMappedPath(URI locationURI) {
String cygwinPath = getPathFromURI(locationURI);
String windowsPath = null;
try {
windowsPath = cygwinToWindowsPath(cygwinPath);
} catch (Exception e) {
CCorePlugin.log(e);
}
return windowsPath;
}
/**
* Conversion from Windows path to Cygwin path.
*
* @param windowsPath - Windows path.
* @return Cygwin style converted path.
* @throws UnsupportedOperationException if Cygwin is unavailable.
* @throws IOException on IO problem.
*
* See ResourceHelper.windowsToCygwinPath(...)
*/
public static String windowsToCygwinPath(String windowsPath) throws IOException, UnsupportedOperationException {
if (!Platform.getOS().equals(Platform.OS_WIN32)) {
// Don't run this on non-windows platforms
throw new UnsupportedOperationException("Not a Windows system, Cygwin is unavailable.");
}
@SuppressWarnings("nls")
String[] args = {"cygpath", "-u", windowsPath};
Process cygpath;
try {
cygpath = Runtime.getRuntime().exec(args);
} catch (IOException ioe) {
throw new UnsupportedOperationException("Cygwin utility cygpath is not in the system search path.");
}
BufferedReader stdout = new BufferedReader(new InputStreamReader(cygpath.getInputStream()));
String cygwinPath = stdout.readLine();
if (cygwinPath == null) {
throw new UnsupportedOperationException("Cygwin utility cygpath is not available.");
}
return cygwinPath.trim();
}
/**
* Conversion from Cygwin path to Windows path.
*
* @param cygwinPath - Cygwin path.
* @return Windows style converted path.
* @throws UnsupportedOperationException if Cygwin is unavailable.
* @throws IOException on IO problem.
*
* * See ResourceHelper.cygwinToWindowsPath(...)
*/
public static String cygwinToWindowsPath(String cygwinPath) throws IOException, UnsupportedOperationException {
if (!Platform.getOS().equals(Platform.OS_WIN32)) {
// Don't run this on non-windows platforms
throw new UnsupportedOperationException("Not a Windows system, Cygwin is unavailable.");
}
@SuppressWarnings("nls")
String[] args = {"cygpath", "-w", cygwinPath};
Process cygpath;
try {
cygpath = Runtime.getRuntime().exec(args);
} catch (IOException ioe) {
throw new UnsupportedOperationException("Cygwin utility cygpath is not in the system search path.");
}
BufferedReader stdout = new BufferedReader(new InputStreamReader(cygpath.getInputStream()));
String windowsPath = stdout.readLine();
if (windowsPath == null) {
throw new UnsupportedOperationException("Cygwin utility cygpath is not available.");
}
return windowsPath.trim();
}
}