package net.sf.eclipsefp.haskell.ui.actions;
import java.io.File;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import net.sf.eclipsefp.haskell.buildwrapper.BWFacade;
import net.sf.eclipsefp.haskell.buildwrapper.BuildWrapperPlugin;
import net.sf.eclipsefp.haskell.buildwrapper.types.CabalImplDetails;
import net.sf.eclipsefp.haskell.buildwrapper.types.CabalImplDetails.SandboxType;
import net.sf.eclipsefp.haskell.core.cabal.CabalImplementationManager;
import net.sf.eclipsefp.haskell.core.util.ResourceUtil;
import net.sf.eclipsefp.haskell.debug.core.internal.launch.AbstractHaskellLaunchDelegate;
import net.sf.eclipsefp.haskell.ui.HaskellUIPlugin;
import net.sf.eclipsefp.haskell.ui.internal.backend.BackendManager;
import net.sf.eclipsefp.haskell.ui.internal.preferences.IPreferenceConstants;
import net.sf.eclipsefp.haskell.ui.internal.util.UITexts;
import net.sf.eclipsefp.haskell.ui.views.CabalPackagesView;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.widgets.DirectoryDialog;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IObjectActionDelegate;
import org.eclipse.ui.IWorkbenchPart;
/**
* <p>Cabal install action, contextual on projects</p>
*
* @author JP Moresmau
*/
public class CabalInstallAction implements IObjectActionDelegate {
protected final Set<IProject> projects=new LinkedHashSet<>();
private Shell currentShell;
/**
* remember last sandbox path
*/
private static String lastSandbox=null;
@Override
public void setActivePart( final IAction arg0, final IWorkbenchPart arg1 ) {
currentShell=arg1.getSite().getShell();
}
/**
* command for cabal-dev invocation
* @return
*/
protected String getCabalDevCommand(){
return "install";
}
/**
* run cabal dev into given sandbox
* @param sandbox
*/
protected void runCabalDev(final String sandbox){
final String cabalExecutable=CabalImplementationManager.getCabalExecutable();
IPreferenceStore preferenceStore = HaskellUIPlugin.getDefault().getPreferenceStore();
String cabalDev = preferenceStore.getString( IPreferenceConstants.CABALDEV_EXECUTABLE );
if (cabalExecutable!=null){
final List<String> commands = new ArrayList<>();
commands.add( cabalDev );
commands.add( getCabalDevCommand());
// options
// use a different build dir so we don't pollute the cabal config file with a wrong sandbox path
commands.add("--builddir="+BWFacade.DIST_FOLDER_CABAL+sandbox.hashCode());
BackendManager.addCabalInstallOptions( commands );
commands.add("--force-reinstalls");
commands.add("--sandbox="+sandbox);
commands.add("--with-cabal-install="+cabalExecutable);
addExtraParameters(commands);
for (final IProject p:projects){
try {
List<String> prjCommands = new ArrayList<>(commands);
BWFacade bf=BuildWrapperPlugin.getFacade( p );
// need to provide user supplied info
if(bf!=null){
String f=bf.getFlags();
if (f!=null && f.length()>0){
prjCommands.add("--flags="+f);
}
List<String> extraOpts=bf.getExtraOpts();
if (extraOpts!=null){
for (String eo:extraOpts){
prjCommands.add(eo);
}
}
}
AbstractHaskellLaunchDelegate.runInConsole(p, prjCommands, new File(p.getLocation().toOSString()), NLS.bind( getJobName(), p.getName() ),true,getAfter(p) );
} catch (Exception ioe){
HaskellUIPlugin.log(ioe);
final IStatus st=new Status( IStatus.ERROR, HaskellUIPlugin.getPluginId(),ioe.getLocalizedMessage(),ioe);
ErrorDialog.openError( currentShell, UITexts.install_error, UITexts.install_error_text, st);
}
}
}
}
/**
* add a snapshot source into given sandbox
* @param sandbox
*/
protected void addSnapshot(final String sandbox){
final String cabalExecutable=CabalImplementationManager.getCabalExecutable();
if (cabalExecutable!=null){
// as far as I can tell, add-source doesn't support the --sandbox flag
// so we need to init the sandbox to make sure we have a proper config file in place
// then we can add add-source
final List<String> icommands = new ArrayList<>();
icommands.add( cabalExecutable );
icommands.add( "sandbox" );
icommands.add( "init" );
File f=new File(sandbox);
/*if (f.getName().equals(".cabal-sandbox")){
f=f.getParentFile();
}*/
final File sandboxF=f;
icommands.add("--sandbox="+ sandboxF.getAbsolutePath());
final Display d=Display.getCurrent();
final Runnable r=new Runnable() {
@Override
public void run() {
final List<String> commands = new ArrayList<>();
commands.add( cabalExecutable );
commands.add( "sandbox" );
commands.add( "add-source" );
commands.add( "--snapshot" );
for (final IProject p:projects){
try {
List<String> prjCommands = new ArrayList<>(commands);
prjCommands.add( p.getLocation().toOSString() );
// BWFacade bf=BuildWrapperPlugin.getFacade( p );
// // need to provide user supplied info
// if(bf!=null){
// String f=bf.getFlags();
// if (f!=null && f.length()>0){
// prjCommands.add("--flags="+f);
// }
// List<String> extraOpts=bf.getExtraOpts();
// if (extraOpts!=null){
// for (String eo:extraOpts){
// prjCommands.add(eo);
// }
// }
// }
AbstractHaskellLaunchDelegate.runInConsole(p, prjCommands, sandboxF, NLS.bind( getJobName(), p.getName() ),true,getAfter(p) );
} catch (Exception ioe){
HaskellUIPlugin.log(ioe);
final IStatus st=new Status( IStatus.ERROR, HaskellUIPlugin.getPluginId(),ioe.getLocalizedMessage(),ioe);
ErrorDialog.openError( currentShell, UITexts.install_error, UITexts.install_error_text, st);
}
}
}
};
// we need to be in the UI thread
Runnable uir=new Runnable() {
@Override
public void run() {
d.asyncExec( r );
}
};
try {
AbstractHaskellLaunchDelegate.runInConsole(null, icommands, f, "sandbox init",true,uir);
} catch (Exception ioe){
HaskellUIPlugin.log(ioe);
final IStatus st=new Status( IStatus.ERROR, HaskellUIPlugin.getPluginId(),ioe.getLocalizedMessage(),ioe);
ErrorDialog.openError( currentShell, UITexts.install_error, UITexts.install_error_text, st);
}
}
}
@Override
public void run( final IAction arg0 ) {
// this action and its subclasses use cabal executable directly
// so ask for confirmation that we're getting out of the sandbox
// (dependencies and installation in dependent projects being handled automatically)
CabalImplDetails cid=BackendManager.getCabalImplDetails();
if (cid.isSandboxed()){
if (cid.getType().equals( SandboxType.CABAL_DEV ) || cid.getType().equals( SandboxType.CABAL)){
MessageDialog md=new MessageDialog( currentShell, UITexts.install_sandbox_title, null, getSandboxWarningMessage(), MessageDialog.QUESTION,
new String[] { IDialogConstants.OK_LABEL, IDialogConstants.CANCEL_LABEL, UITexts.install_sandbox_install_dest }, 2 );
int ret=md.open();
if (ret==1){
return;
} else if (ret==2){
DirectoryDialog dd=new DirectoryDialog( currentShell );
dd.setText( UITexts.install_sandbox_install_dest );
dd.setFilterPath( lastSandbox );
String fileName=dd.open();
if (fileName!=null){
lastSandbox=fileName;
if (cid.getType().equals( SandboxType.CABAL_DEV )){
runCabalDev(fileName);
} else {
addSnapshot(fileName);
}
}
return;
}
} else {
boolean confirm=MessageDialog.openConfirm( currentShell, UITexts.install_sandbox_title, getSandboxWarningMessage() );
if (!confirm){
return;
}
}
}
// do not ask for options, use our own dist folder and default cabal options
//WizardDialog wd=new WizardDialog( currentShell, new CabalInstallWizard( projects ) );
//wd.open();
final String cabalExecutable=CabalImplementationManager.getCabalExecutable();
if (cabalExecutable!=null){
final List<String> commands = new ArrayList<>();
commands.add( cabalExecutable );
commands.add("install");
BackendManager.addCabalInstallOptions( commands );
// options
commands.add("--builddir="+BWFacade.DIST_FOLDER_CABAL);
// commands.add( "--user" ); // use cabal default, which is now user
addExtraParameters(commands);
for (final IProject p:projects){
try {
List<String> prjCommands = new ArrayList<>(commands);
BWFacade bf=BuildWrapperPlugin.getFacade( p );
// need to provide user supplied info
if(bf!=null){
String f=bf.getFlags();
if (f!=null && f.length()>0){
prjCommands.add("--flags="+f);
}
List<String> extraOpts=bf.getExtraOpts();
if (extraOpts!=null){
for (String eo:extraOpts){
prjCommands.add(eo);
}
}
}
AbstractHaskellLaunchDelegate.runInConsole(p, prjCommands, new File(p.getLocation().toOSString()), NLS.bind( getJobName(), p.getName() ),true,getAfter(p) );
} catch (Exception ioe){
HaskellUIPlugin.log(ioe);
final IStatus st=new Status( IStatus.ERROR, HaskellUIPlugin.getPluginId(),ioe.getLocalizedMessage(),ioe);
ErrorDialog.openError( currentShell, UITexts.install_error, UITexts.install_error_text, st);
}
}
}
}
protected Runnable getAfter(final IProject p){
return new Runnable() {
@Override
public void run() {
/** refresh the cabal packages view **/
CabalPackagesView.refresh();
}
};
}
protected String getJobName(){
return UITexts.install_job;
}
protected String getSandboxWarningMessage(){
return UITexts.install_sandbox_install_text;
}
protected void addExtraParameters(final List<String> commands){
// force reinstall since we're probably reinstalling our development version
commands.add( "--reinstall" );
}
@Override
public void selectionChanged( final IAction arg0, final ISelection arg1 ) {
projects.clear();
projects.addAll( ResourceUtil.getProjects( arg1 ) );
}
}