// Copyright (c) 2003-2005 by Leif Frenzel - see http://leiffrenzel.de package net.sf.eclipsefp.haskell.core.compiler; import java.io.BufferedReader; import java.io.File; import java.io.FilenameFilter; import java.io.IOException; import java.io.StringReader; import java.io.StringWriter; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import net.sf.eclipsefp.haskell.core.HaskellCorePlugin; import net.sf.eclipsefp.haskell.core.internal.util.CoreTexts; import net.sf.eclipsefp.haskell.core.preferences.ICorePreferenceNames; import net.sf.eclipsefp.haskell.core.util.GHCSyntax; import net.sf.eclipsefp.haskell.util.FileUtil; import net.sf.eclipsefp.haskell.util.ProcessRunner; 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.IEclipsePreferences; import org.eclipse.core.runtime.preferences.IEclipsePreferences.IPreferenceChangeListener; import org.eclipse.core.runtime.preferences.IEclipsePreferences.PreferenceChangeEvent; import org.eclipse.core.runtime.preferences.IPreferencesService; import org.eclipse.osgi.util.NLS; import org.osgi.service.prefs.BackingStoreException; /** * <p> * manages the Haskell compilers. * </p> * * @author Leif Frenzel */ public class CompilerManager { /** Current Haskell implementation */ private static IHsImplementation currentHsImplementation; static { initHsImplementation(); if( currentHsImplementation == null ) { String msg = CoreTexts.hsImplementation_none; HaskellCorePlugin.log( msg, IStatus.WARNING ); } listenForImplPref(); } /** * get the path name to the compiler executable * @return */ public static String getCompilerExecutable() { IPath result = null; IHsImplementation impl = getCurrentHsImplementation(); if( impl != null && impl.getBinDir() != null ) { result = new Path( impl.getBinDir() ); result = result.append( GHCSyntax.GHC ); } return result == null ? GHCSyntax.GHC : result.toOSString(); } private static List<String> extensions; /** * get the extensions as given by GHC * @return the list of extensions known to the compiler */ public static List<String> getExtensions(){ if (extensions==null){ File f=new File(getCompilerExecutable()); if (f.exists()){ StringWriter sw=new StringWriter(); try { new ProcessRunner().executeBlocking( f.getParentFile(), sw, null, f.getAbsolutePath(),GHCSyntax.EXTENSIONS ); String s=sw.toString(); BufferedReader br=new BufferedReader( new StringReader( s ) ); extensions=new ArrayList<>(); String line=br.readLine(); while (line!=null){ extensions.add(line.trim()); line=br.readLine(); } Collections.sort( extensions, String.CASE_INSENSITIVE_ORDER ); } catch (IOException ioe){ HaskellCorePlugin.log( ioe ); } } } return extensions; } public static IHsImplementation getCurrentHsImplementation() { return currentHsImplementation; } private static void initHsImplementation() { IPreferencesService prefSvc = Platform.getPreferencesService(); String currentImplName = prefSvc.getString( HaskellCorePlugin.getPluginId(), ICorePreferenceNames.SELECTED_HS_IMPLEMENTATION, null, null ); if( currentImplName != null ) { Map<String, IHsImplementation> impls = loadImpls(); if (impls.size()>0){ currentHsImplementation = impls.get( currentImplName ); if (currentHsImplementation!=null){ return; } } } List<IHsImplementation> impls=autodetectGHCImpls(); if (impls.size()>0){ IHsImplementation def=impls.get( 0 ); if( currentImplName != null ) { for (IHsImplementation imp:impls){ if (imp.getName().equals( currentImplName )){ def=imp; break; } } } setHsImplementations(impls,def); initHsImplementation() ; } } public static void setHsImplementations(final List<IHsImplementation> impls,final IHsImplementation def){ IEclipsePreferences node = HaskellCorePlugin.instanceScopedPreferences(); node.put( ICorePreferenceNames.HS_IMPLEMENTATIONS, HsImplementationPersister.toXML( impls ) ); node.put( ICorePreferenceNames.SELECTED_HS_IMPLEMENTATION, def!=null?def.getName() :""); //$NON-NLS-1$ try { node.flush(); } catch( BackingStoreException ex ) { HaskellCorePlugin.log( ex ); } } private static Map<String, IHsImplementation> loadImpls() { List<IHsImplementation> impls = new ArrayList<>(); String xml = Platform.getPreferencesService().getString( HaskellCorePlugin.getPluginId(), ICorePreferenceNames.HS_IMPLEMENTATIONS, null, null ); HsImplementationPersister.fromXML( xml, impls ); Map<String, IHsImplementation> result = new HashMap<>(); for( IHsImplementation impl: impls ) { if (isValid( impl )){ result.put( impl.getName(), impl ); } } return result; } private static void listenForImplPref() { HaskellCorePlugin.instanceScopedPreferences().addPreferenceChangeListener( new IPreferenceChangeListener() { @Override public void preferenceChange( final PreferenceChangeEvent event ) { String key = event.getKey(); if( ICorePreferenceNames.HS_IMPLEMENTATIONS.equals( key ) || ICorePreferenceNames.SELECTED_HS_IMPLEMENTATION.equals( key ) ) { initHsImplementation(); } } }); } private static boolean isValid(final IHsImplementation impl){ IStatus[] statuss=impl.validate(); boolean err=false; for( int i = 0; i < statuss.length; i++ ) { IStatus curr = statuss[ i ]; if( curr.matches( IStatus.ERROR ) ) { err=true; } } return !err; } public static List<IHsImplementation> autodetectGHCImpls(){ ArrayList<File> candidateLocs = FileUtil.getCandidateLocations(); List<IHsImplementation> impls=new ArrayList<>(); for (File loc : candidateLocs) { File[] files = loc.listFiles( new FilenameFilter() { @Override public boolean accept( final File dir, final String name ) { return name.equals( GHCSyntax.GHC ); } }); if (files != null && files.length > 0) { for (File file : files) { HsImplementation impl=new HsImplementation(); impl.setType( HsImplementationType.GHC ); impl.setBinDir( file.getParent() ); impl.validate(); String vs=impl.getVersion(); String nameStub=NLS.bind( CoreTexts.hsImplementation_name_default,impl.getType().toString(),vs); int index=1; String nameFull=nameStub; while( isDuplicateName(impls,nameFull,impl ) ) { nameFull=NLS.bind(CoreTexts.hsImplementation_name_index,nameStub,String.valueOf(index)); index++; } impl.setName( nameFull ); if (isValid( impl )){ impls.add( impl ); } } } } return impls; } public static boolean isDuplicateName(final List<IHsImplementation> impls, final String name, final HsImplementation impl ) { boolean result = false; if( name != null && name.trim().length() > 0 ) { for( IHsImplementation inst: impls ) { result |= inst!=impl && name.equals( inst.getName() ); } } return result; } }