package net.sf.eclipsefp.haskell.core.cabal;
import java.io.IOException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import net.sf.eclipsefp.haskell.core.compiler.IHsImplementation;
import net.sf.eclipsefp.haskell.util.FileUtil;
import net.sf.eclipsefp.haskell.util.QueryUtil;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.osgi.framework.Version;
/**
* Cabal implementation manager container.
*
* This class keeps track of various bits of important information related to
* running Cabal. For example, ScionManager needs to know the current version
* of the installed cabal in order to set installer flags.
*
* @author Scott Michel (bscottm@ieee.org)
*/
public class CabalImplementation {
/** Base cabal executable name */
public final static String CABAL_BASENAME = "cabal"; //$NON-NLS-1$
/** Cabal's executable name (with ".exe" appended if running on Windows) */
public final static String CABAL_EXECUTABLE = FileUtil.makeExecutableName( CABAL_BASENAME );
/** Path to the cabal executable */
private IPath fCabalExecutablePath;
/** User identifier for this implementation */
private String fCabalIdentifier;
/** cabal-install version */
private String fCabalInstallVersion;
/** Cabal library version */
private String fCabalLibraryVersion;
/** Default constructor */
public CabalImplementation() {
fCabalIdentifier = null;
fCabalExecutablePath = null;
resetVersions();
}
/** Constructor with the cabal executable's Path */
public CabalImplementation(final String ident, final IPath cabalExecPath) {
fCabalIdentifier = ident;
fCabalExecutablePath = cabalExecPath;
probeVersionInternal(fCabalExecutablePath);
}
/** Copy constructor
*
* @param src The source CabalImplementation object
*/
public CabalImplementation (final CabalImplementation src) {
this.copy(src);
}
/** Probe the version numbers of the "cabal" executable, in the given
* directory.
*
* @param directory The directory in which to search for the cabal executable.
*/
public void probeVersion(final String directory) {
probeVersionInternal(new Path(directory).append( CABAL_EXECUTABLE ) );
}
/** Probe the version numbers of a proposed cabal executable, in the given
* directory.
*
* @param directory The directory in which to search for the cabal executable.
* @param exeName The name of the cabal executable.
*/
public void probeVersion( final String directory, final String exeName ) {
probeVersionInternal( FileUtil.makeExecutableName( new Path( directory ).append( exeName ) ));
}
/** Probe the version numbers of a cabal executable using the binary directory
* of the Haskell implementation. Note that this defaults to using a cabal
* executable name {@link #CABAL_EXECUTABLE}.
*
* @param hsImpl The Haskell implementation
*/
public void probeVersion( final IHsImplementation hsImpl ) {
if( hsImpl != null ) {
IPath cabalBinPath = new Path( hsImpl.getBinDir() ).append( CABAL_EXECUTABLE );
fCabalExecutablePath = cabalBinPath;
probeVersionInternal(cabalBinPath);
}
}
/**
* Query version identifiers from the cabal executable.
*
* NOTE: This method is expensive to call repeatedly because it involves forking a process to query
* the CABAL_EXECUTABLE's version numbers, which then need to be parsed.
*/
private void probeVersionInternal( final IPath cabalExecutable ) {
boolean validImpl = false;
try {
String version = QueryUtil.queryEx( cabalExecutable.toOSString(), "--version" ); //$NON-NLS-1$
if (version != null && version.length() > 0) {
String[] vlines = version.split( "(\\r\\n)|\\r|\\n" ); //$NON-NLS-1$
if( vlines[ 0 ].startsWith( "cabal-install" ) //$NON-NLS-1$
&& vlines[ 1 ].startsWith( "using" ) ) { //$NON-NLS-1$
// Looks like we might have a winner...
Pattern vPat = Pattern.compile( "(\\d+\\.)+\\d+" ); //$NON-NLS-1$
Matcher vMatch = vPat.matcher( vlines[ 0 ] );
if( vMatch.find() ) {
fCabalInstallVersion = vMatch.group();
}
vMatch = vPat.matcher( vlines[ 1 ] );
if( vMatch.find() ) {
fCabalLibraryVersion = vMatch.group();
}
if( fCabalInstallVersion.length() > 0
|| fCabalLibraryVersion.length() > 0 ) {
validImpl = true;
}
}
}
} catch( IOException e ) {
// Paranoia: ensure validImpl is still false
validImpl = false;
}
if( !validImpl ) {
resetVersions();
}
}
/** Return the operational cabal executable name */
public IPath getCabalExecutableName() {
return fCabalExecutablePath;
}
/** Set the operational cabal executable's name */
public void setCabalExecutableName(final IPath executable) {
fCabalExecutablePath = executable;
probeVersionInternal( fCabalExecutablePath );
}
/** Reset the version strings to empty string */
private void resetVersions() {
fCabalInstallVersion = new String();
fCabalLibraryVersion = new String();
}
/** Accessor for the user identifier */
public final String getUserIdentifier () {
return fCabalIdentifier;
}
/** Set the user identifier */
public void setUserIdentifier (final String identifier) {
fCabalIdentifier = identifier;
}
/** Accessor for cabal-install version string */
public final String getInstallVersion() {
return fCabalInstallVersion;
}
/** Set the cabal-install version string (hopefully, this was acquired
* via probeVersion...)
*/
public void setInstallVersion(final String installVersion) {
fCabalInstallVersion = installVersion;
}
/** Accessor for Cabal library version string */
public final String getLibraryVersion() {
return fCabalLibraryVersion;
}
/** Set the Cabal library version string */
public void setLibraryVersion(final String libVersion) {
fCabalLibraryVersion = libVersion;
}
/** Copy method, used by the copy constructor and elsewhere */
public void copy( final CabalImplementation theImpl ) {
if (theImpl != null) {
this.fCabalIdentifier = theImpl.fCabalIdentifier;
this.fCabalExecutablePath = theImpl.fCabalExecutablePath;
this.fCabalInstallVersion = theImpl.fCabalInstallVersion;
this.fCabalLibraryVersion = theImpl.fCabalLibraryVersion;
} else {
fCabalIdentifier = null;
fCabalExecutablePath = null;
resetVersions();
}
}
public boolean allowsSandbox(){
return new Version(getInstallVersion()).compareTo( new Version("1.18.0") )>=0;
}
}