/**
* Copyright (c) 2012 by JP Moresmau
* This code is made available under the terms of the Eclipse Public License,
* version 1.0 (EPL). See http://www.eclipse.org/legal/epl-v10.html
*/
package net.sf.eclipsefp.haskell.ui.internal.backend;
import java.io.File;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import net.sf.eclipsefp.haskell.core.cabal.CabalImplementationManager;
import net.sf.eclipsefp.haskell.core.compiler.CompilerManager;
import net.sf.eclipsefp.haskell.core.util.GHCSyntax;
import net.sf.eclipsefp.haskell.debug.core.internal.launch.AbstractHaskellLaunchDelegate;
import net.sf.eclipsefp.haskell.ui.HaskellUIPlugin;
import net.sf.eclipsefp.haskell.ui.internal.util.UITexts;
import net.sf.eclipsefp.haskell.ui.views.CabalPackagesView;
import net.sf.eclipsefp.haskell.util.FileUtil;
import net.sf.eclipsefp.haskell.util.ProcessRunner;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.osgi.util.NLS;
/**
* this runnable runs cabal update, then installs buildwrapper and/or scion-browser
* @author JP Moresmau
*
*/
public class InstallExecutableRunnable implements Runnable {
private boolean cabalUpdate=true;
//private boolean buildWrapper=true;
//private boolean scionBrowser=true;
private boolean global=false;
private final List<Package> packages=new ArrayList<>();
private Runnable nextRunnable;
private final List<String> errors=new ArrayList<>();
private final Map<String,File> files=new HashMap<>();
public InstallExecutableRunnable( ) {
super( );
}
private void logError(final String msg){
errors.add(msg);
HaskellUIPlugin.log( msg, IStatus.ERROR );
}
private void logError(final Throwable t){
errors.add(t.getLocalizedMessage());
HaskellUIPlugin.log( t);
}
@Override
public void run(){
errors.clear();
files.clear();
final String cabalExecutable=CabalImplementationManager.getCabalExecutable();
if (cabalExecutable==null){
logError( UITexts.noCabalImplementationForInstall_error);
if (getNextRunnable()!=null){
getNextRunnable().run();
}
return;
}
File folder=new File(cabalExecutable).getParentFile();
final LinkedList<Command> commands=new LinkedList<>();
File binDir=new File(CompilerManager.getCurrentHsImplementation().getBinDir());
if (cabalUpdate){
commands.add(new Command(UITexts.cabalUpdateProgress,Arrays.asList( cabalExecutable , "update" )));
}
File sandbox = BackendManager.getToolSandbox();
if (sandbox!=null){
folder=sandbox;
commands.add(new Command(UITexts.cabalInitProgress,Arrays.asList( cabalExecutable , "sandbox","init","--sandbox="+ folder )));
binDir=new File(folder,"bin");
} else {
if (!global){
File exe=new File(binDir,GHCSyntax.GHC);
StringWriter sw=new StringWriter();
try {
// we run ghc in execution mode to find the directory cabal is going to use
// no quotes around last arguments, as it breaks unixes (returns the command, as I suppose ghc evaluates the argument as a string value)
// windows: use old version!
new ProcessRunner().executeBlocking( binDir, sw, null, exe.getAbsolutePath(),"-e",ProcessRunner.getGHCArgument( "System.Directory.getAppUserDataDirectory \"cabal\"" ));
String s=sw.toString().trim(); // line return at end
if (s.startsWith( "\"" )){ // quotes
s=s.substring( 1 );
}
if (s.endsWith( "\"" )){// quotes
s=s.substring( 0,s.length()-1 );
}
binDir=new File(s,"bin");
} catch (Exception e){
logError( e );
if (getNextRunnable()!=null){
getNextRunnable().run();
}
return;
}
}
}
/*
if (buildWrapper){
commands.add(new Command(UITexts.builWrapperInstallProgress,"buildwrapper",IPreferenceConstants.BUILDWRAPPER_EXECUTABLE,Arrays.asList( cabalExecutable , "install","buildwrapper", global?"--global": "--user" )));
}
if (scionBrowser){
commands.add(new Command(UITexts.scionBrowserInstallProgress,"scion-browser",IPreferenceConstants.SCION_BROWSER_SERVER_EXECUTABLE,Arrays.asList( cabalExecutable , "install","scion-browser", global?"--global": "--user" )));
}*/
for (Package p:packages){
List<String> args=new ArrayList<>(Arrays.asList( cabalExecutable , "install",p.getPkgName() ));
if (!CabalImplementationManager.getInstance().getDefaultCabalImplementation().allowsSandbox()){
args.add(global?"--global": "--user");
}
args.add("--with-ghc="+CompilerManager.getCompilerExecutable());
BackendManager.addCabalInstallOptions( args );
File f=new File(binDir,FileUtil.makeExecutableName( p.getExeName() ));
if (!f.exists()){ // the exe does not exist, we force reinstall to make sure it wasn't deleted manually
args.add( "--reinstall" );
}
commands.add(new Command(NLS.bind( UITexts.installExecutableProgress,p.getExeName()),p.getExeName(),p.getPreference(),args));
}
final File fBinDir=binDir;
final File ffolder=folder;
Runnable r=new Runnable(){
@Override
public void run() {
if (commands.size()>0){
final Command c=commands.removeFirst();
final Runnable orig=this;
Runnable next=this;
if (c.exeName!=null){
next=new Runnable() {
@Override
public void run() {
/** refresh the cabal packages view **/
CabalPackagesView.refresh();
File f=new File(fBinDir,FileUtil.makeExecutableName( c.exeName ));
if (f.exists()){
// set preference
if (c.prefName!=null){
HaskellUIPlugin.getDefault().getPreferenceStore().setValue(c.prefName,f.getAbsolutePath());
}
files.put( c.exeName, f);
} else {
// oops, write message
logError( NLS.bind( UITexts.installExecutableMissing, f.getAbsolutePath() ) );
}
orig.run();
}
};
}
final Runnable fNext=next;
HaskellUIPlugin.getStandardDisplay().asyncExec(new Runnable() {
@Override
public void run() {
try {
AbstractHaskellLaunchDelegate.runInConsole( null, c.commands, ffolder, c.title, true,fNext );
} catch (CoreException ce){
logError( ce );
}
}
});
} else {
if (nextRunnable!=null){
nextRunnable.run();
}
}
}
};
HaskellUIPlugin.getStandardDisplay().asyncExec( r );
//r.run();
}
public boolean isCabalUpdate() {
return cabalUpdate;
}
public void setCabalUpdate( final boolean cabalUpdate ) {
this.cabalUpdate = cabalUpdate;
}
// public boolean isBuildWrapper() {
// return buildWrapper;
// }
//
// public void setBuildWrapper( final boolean buildWrapper ) {
// this.buildWrapper = buildWrapper;
// }
//
// public boolean isScionBrowser() {
// return scionBrowser;
// }
//
// public void setScionBrowser( final boolean scionBrowser ) {
// this.scionBrowser = scionBrowser;
// }
public boolean isGlobal() {
return global;
}
public void setGlobal( final boolean global ) {
this.global = global;
}
/**
* @return the packages
*/
public List<Package> getPackages() {
return packages;
}
public Runnable getNextRunnable() {
return nextRunnable;
}
public void setNextRunnable( final Runnable nextRunnable ) {
this.nextRunnable = nextRunnable;
}
/**
* @return the files
*/
public Map<String, File> getFiles() {
return files;
}
/**
* @return the errors
*/
public List<String> getErrors() {
return errors;
}
/**
* internal structure for a command
*
*/
private static class Command {
/**
* title to display on top of console
*/
private final String title;
/**
* commands to run
*/
private final List<String> commands;
/**
* name of exe built, if any
*/
private String exeName;
/**
* preference to set at end, if any
*/
private String prefName;
public Command( final String title, final List<String> commands ) {
super();
this.title = title;
this.commands = commands;
}
public Command( final String title, final String exeName,
final String prefName, final List<String> commands ) {
super();
this.title = title;
this.commands = commands;
this.exeName = exeName;
this.prefName = prefName;
}
}
public static class Package {
private String exeName;
/**
* the package name may be different than the exe name
* example: yesod-bin is the package, installs the yesod exe
*/
private String pkgName;
private String preference;
public Package( final String exeName, final String preference ) {
this(exeName,exeName,preference);
}
public Package( final String exeName,final String pkgName, final String preference ) {
super();
this.exeName = exeName;
this.pkgName = pkgName;
this.preference = preference;
}
public String getExeName() {
return exeName;
}
public void setExeName( final String exeName ) {
this.exeName = exeName;
}
public String getPkgName() {
return pkgName;
}
public void setPkgName( final String pkgName ) {
this.pkgName = pkgName;
}
public String getPreference() {
return preference;
}
public void setPreference( final String preference ) {
this.preference = preference;
}
}
}