// Copyright (c) 2003-2005 by Leif Frenzel - see http://leiffrenzel.de package net.sf.eclipsefp.haskell.core.builder; import java.util.Arrays; import java.util.Collection; import java.util.HashSet; import java.util.Map; import java.util.Set; import net.sf.eclipsefp.haskell.buildwrapper.BWFacade; import net.sf.eclipsefp.haskell.buildwrapper.BuildWrapperPlugin; import net.sf.eclipsefp.haskell.buildwrapper.JobFacade; import net.sf.eclipsefp.haskell.buildwrapper.SandboxHelper; import net.sf.eclipsefp.haskell.buildwrapper.WorkspaceFacade; import net.sf.eclipsefp.haskell.buildwrapper.types.BuildOptions; import net.sf.eclipsefp.haskell.core.HaskellCorePlugin; import net.sf.eclipsefp.haskell.core.cabalmodel.PackageDescription; import net.sf.eclipsefp.haskell.core.cabalmodel.PackageDescriptionLoader; import net.sf.eclipsefp.haskell.core.cabalmodel.PackageDescriptionStanza; import net.sf.eclipsefp.haskell.core.internal.util.CoreTexts; import net.sf.eclipsefp.haskell.core.util.ResourceUtil; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IMarker; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IProjectDescription; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IResourceDelta; import org.eclipse.core.resources.IWorkspaceRoot; import org.eclipse.core.resources.IncrementalProjectBuilder; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.jobs.ISchedulingRule; /** <p>The incremental builder for Haskell projects.</p> * * @author Leif Frenzel */ public class HaskellBuilder extends IncrementalProjectBuilder { public static final String BUILDER_ID = HaskellBuilder.class.getName(); @Override protected IProject[] build( final int kind, final Map<String,String> args, final IProgressMonitor monitor ) throws CoreException { //checkOutFolders( new SubProgressMonitor( monitor, 5 ) ); checkCabalFileExists(); // disable output if asked for boolean output=true; if (args!=null ){ Object o=args.get("output") ;//$NON-NLS-1$ if (o instanceof String){ String s=(String)o; if ("false".equalsIgnoreCase( s )){ //$NON-NLS-1$ output=false; } } } performBuild( kind,output, monitor);//new SubProgressMonitor( monitor, 95 ) ); //scheduleRefresh(); return null; } private void checkCabalFileExists() throws CoreException { IFile cabalFile = getCabalFile(); String id = HaskellCorePlugin.ID_PROJECT_PROBLEM_MARKER; if( !cabalFile.exists() ) { IMarker marker = getProject().createMarker( id ); marker.setAttribute( IMarker.MESSAGE, CoreTexts.cabalBuilder_noCabal ); marker.setAttribute( IMarker.SEVERITY, IMarker.SEVERITY_WARNING ); } else { getProject().deleteMarkers( id , false, IResource.DEPTH_ZERO ); } } private IFile getCabalFile() { return BuildWrapperPlugin.getCabalFile( getProject() ); } // helping methods ////////////////// /*private void checkOutFolders( final SubProgressMonitor monitor ) { IWorkspaceRunnable op = new CheckOutFoldersOperation( getProject() ); try { ResourcesPlugin.getWorkspace().run( op, monitor ); } catch( CoreException cex ) { String msg = "Problem while checking out and bin folder existence."; //$NON-NLS-1$ HaskellCorePlugin.log( msg, cex ); } }*/ private void performBuild( final int kind,final boolean output, final IProgressMonitor mon ) throws CoreException { if( kind == FULL_BUILD ) { fullBuild(true,output, mon ); } else if( kind == CLEAN_BUILD ) { clean(mon); } else { IResourceDelta delta = getDelta( getProject() ); if( delta == null ) { fullBuild(true,output, mon ); } else { incrementalBuild( delta,output, mon ); } } } @Override public ISchedulingRule getRule( final int kind, final Map<String, String> args ) { // prevent other project operations, but operations elsewhere in the workspace are fine return getProject(); } @Override protected void clean( final IProgressMonitor mon) throws CoreException { // need a workspace operation here WorkspaceFacade f=BuildWrapperPlugin.getWorkspaceFacade( getProject(),mon); if (f!=null){ // clean f.clean( mon ); } } public void fullBuild(final boolean synchronize,final boolean output, final IProgressMonitor mon ) { mon.beginTask( CoreTexts.haskellBuilder_full, 100 ); try { final JobFacade f=BuildWrapperPlugin.getJobFacade( getProject()); if (f!=null){ BuildWrapperPlugin.deleteProblems( getProject() ); cleanNonHaskellSources(); if (mon.isCanceled()){ return; } final BuildOptions bo=new BuildOptions().setOutput(output).setRecompile(false); Runnable r=new Runnable(){ @Override public void run() { if (synchronize){ addProjectDependencies( f.getRealFacade(), getProject() ); f.synchronizeAndBuild( false, bo, mon ); } else { f.build( bo,mon ); } } }; f.getRealFacade().waitForThread( r, mon ); } else { new Exception("JobFacade == null").printStackTrace(); //$NON-NLS-1$ } } finally { mon.done(); } } public static void addProjectDependencies(final BWFacade f,final IProject prj){ if (f!=null && f.getCabalImplDetails().isSandboxed() && f.getCabalImplDetails().isManageProjectDependencies()){ IWorkspaceRoot root=ResourcesPlugin.getWorkspace().getRoot(); try { PackageDescription pd=PackageDescriptionLoader.load( BuildWrapperPlugin.getCabalFile( prj ) ); Set<String> pkgs=new HashSet<>(); for (PackageDescriptionStanza pds:pd.getStanzas()){ pkgs.addAll( pds.getDependentPackages() ); } Set<IProject> deps=new HashSet<>(); for (String pkg:pkgs){ IProject p=root.getProject( pkg ); // avoid reference to itself if (ResourceUtil.hasHaskellNature( p ) && p!=prj){ deps.add(p); } } if (deps.size()>0){ IProjectDescription desc=prj.getDescription(); IProject[] oldDeps=desc.getReferencedProjects(); deps.addAll( Arrays.asList( oldDeps ) ); if (deps.size()>oldDeps.length){ IProject[] newDeps=deps.toArray( new IProject[deps.size()] ); desc.setReferencedProjects( newDeps ); prj.setDescription( desc, new NullProgressMonitor() ); /** * we need to make sure dependencies are added before the synchronize */ f.cabalFileChanged(); // if we have new internal dependencies SandboxHelper.installDeps(f); } } } catch(CoreException ce){ HaskellCorePlugin.log( ce ); } } } private void cleanNonHaskellSources(){ IFile cabal=BuildWrapperPlugin.getCabalFile( getProject() ); try { PackageDescription pd=PackageDescriptionLoader.load(cabal); Collection<String> nhfs=pd.getAllNonHaskellFiles(); for (String s:nhfs){ if (s!=null && s.length()>0){ IResource r=getProject().findMember( s ); if (r!=null){ BuildWrapperPlugin.deleteProblems( r ); } } } } catch (CoreException ce){ HaskellCorePlugin.log( ce ); } } private void incrementalBuild( final IResourceDelta delta, final boolean output, final IProgressMonitor mon ) throws CoreException { DeltaBuildVisitor v=new DeltaBuildVisitor(mon ) ; delta.accept(v); if (v.isNeedBuild()){ // we don't know if a save was done via the editor, which does the synchronize on the exact file for us // or via a refactoring, which doesn't // v.isNeedSynchronize() fullBuild(true,output, mon ); } } }