{-# LANGUAGE PatternGuards,ScopedTypeVariables,CPP #-}-- |-- Module : Language.Haskell.BuildWrapper.Cabal-- Author : JP Moresmau-- Copyright : (c) JP Moresmau 2011-- License : BSD3-- -- Maintainer : jpmoresmau@gmail.com-- Stability : beta-- Portability : portable-- -- Cabal operations: configure, build, retrieve information from build info, parse errors and warningsmoduleLanguage.Haskell.BuildWrapper.CabalwhereimportLanguage.Haskell.BuildWrapper.BaseimportLanguage.Haskell.BuildWrapper.PackagesimportControl.Monad.StateimportData.CharimportData.Ord(comparing)importData.ListimportData.Maybe#if MIN_VERSION_Cabal(1,15,0) importData.Version(parseVersion)importText.ParserCombinators.ReadP(readP_to_S)#endifimportqualifiedData.MapasDMimportException(ghandle)importDistribution.ModuleNameimportDistribution.PackageDescription(otherModules,library,executables,testSuites,Library,hsSourceDirs,libBuildInfo,Executable(..),exeName,modulePath,buildInfo,TestSuite(..),testName,TestSuiteInterface(..),testInterface,testBuildInfo,BuildInfo,cppOptions,defaultExtensions,otherExtensions,oldExtensions)importDistribution.Simple.GHC#if MIN_VERSION_Cabal(1,15,0) importDistribution.Simple.Program.GHC#endifimportDistribution.Simple.LocalBuildInfoimportDistribution.Simple.Compiler(OptimisationLevel(..))importqualifiedDistribution.PackageDescriptionasPDimportDistribution.PackageimportDistribution.InstalledPackageInfoasIPIimportDistribution.VersionimportDistribution.Text(display,simpleParse)importqualifiedDistribution.Simple.ConfigureasDSCimportqualifiedDistribution.VerbosityasVimportText.Regex.TDFAimportSystem.DirectoryimportSystem.ExitimportSystem.FilePathimportSystem.ProcessimportData.Functor.Identity(runIdentity)getCabalLibraryVersion::StringgetCabalLibraryVersion=VERSION_CabalgetFilesToCopy::BuildWrapper(OpResult[FilePath])getFilesToCopy=do(mfps,bwns)<-withCabalSourcegetAllFilesreturn$casemfpsofJustfps->(nub$concatMap(mapsnd.cbiModulePaths)fps,bwns)Nothing->([],bwns);cabalV::BuildWrapperV.VerbositycabalV=dov<-getsverbosityreturn$toCabalVvwheretoCabalVSilent=V.silenttoCabalVNormal=V.normaltoCabalVVerbose=V.verbosetoCabalVDeafening=V.deafening-- | run cabal buildcabalBuild::Bool-- ^ do we want output (True) or just compilation without linking?->WhichCabal-- ^ use original cabal or temp cabal file->BuildWrapper(OpResultBuildResult)cabalBuild=cabalBuild'True-- | run cabal buildcabalBuild'::Bool-- ^ can we rerun configure again->Bool-- ^ do we want output (True) or just compilation without linking?->WhichCabal-- ^ use original cabal or temp cabal file->BuildWrapper(OpResultBuildResult)cabalBuild'reRunoutputsrcOrTgt=dodist_dir<-getDistDir(mr,n)<-withCabalsrcOrTgt(\_->docf<-getCabalFilesrcOrTgtcp<-getscabalPathv<-cabalVletargs=["build","--verbose="++show(fromEnumv),"--builddir="++dist_dir]++(ifoutputthen[]else["--ghc-option=-c"])liftIO$docd<-getCurrentDirectorysetCurrentDirectory(takeDirectorycf)(ex,out,err)<-readProcessWithExitCodecpargs""-- putStrLn errifisInfixOf"cannot satisfy -package-id"err||isInfixOf"re-run the 'configure'"errthenreturnNothingelsedoletfps=mapMaybegetBuiltPath(linesout)letret=parseBuildMessages(takeFileNamecf)(takeFileNamecp)dist_direrrsetCurrentDirectorycdreturn$Just(ex==ExitSuccess,ret,fps))casemrofNothing->return(BuildResultFalse[],n)JustNothing->ifreRunthendoletsetup_config=DSC.localBuildInfoFiledist_dirliftIO$removeFilesetup_configcabalBuild'FalseoutputsrcOrTgtelsereturn(BuildResultFalse[],n)Just(Just(r,n2,fps))->return(BuildResultrfps,n++n2)-- | run cabal configurecabalConfigure::WhichCabal-- ^ use original cabal or temp cabal file->BuildWrapper(OpResult(MaybeLocalBuildInfo))-- ^ return the build info on success, or Nothing on failurecabalConfiguresrcOrTgt=docf<-getCabalFilesrcOrTgtcp<-getscabalPathokCF<-liftIO$doesFileExistcfifokCFthendov<-cabalVdist_dir<-getDistDiruf<-getscabalFlagscopts<-getscabalOptsletargs=["configure","--verbose="++show(fromEnumv),"--user","--enable-tests","--builddir="++dist_dir]++(ifnullufthen[]else["--flags="++uf])++coptsliftIO$docd<-getCurrentDirectorysetCurrentDirectory(takeDirectorycf)(ex,_,err)<-readProcessWithExitCodecpargs""putStrLnerrletmsgs=parseCabalMessages(takeFileNamecf)(takeFileNamecp)err-- ++ (parseCabalMessages (takeFileName cf) out)ret<-caseexofExitSuccess->ifanyisBWNoteErrormsgsthenreturn(Nothing,msgs)elsedolbi<-DSC.getPersistBuildConfigdist_dirreturn(Justlbi,msgs)ExitFailureec->ifnullmsgsthenreturn(Nothing,[BWNoteBWError("Cabal configure returned error code "++showec)(mkEmptySpancf01)])elsereturn(Nothing,msgs)setCurrentDirectorycdreturnretelsedoleterr="Cabal file"++cf++" does not exist"liftIO$putStrLnerrreturn(Nothing,[BWNoteBWErrorerr(mkEmptySpancf01)])-- | get the full path to the cabal filegetCabalFile::WhichCabal-- ^ use original cabal or temp cabal file->BuildWrapperFilePathgetCabalFileSource=getscabalFilegetCabalFileTarget=fmaptakeFileName(getscabalFile)>>=getTargetPath-- | get Cabal build info, running configure if neededcabalInit::WhichCabal-- ^ use original cabal or temp cabal file->BuildWrapper(OpResult(MaybeLocalBuildInfo))cabalInitsrcOrTgt=docabal_file<-getCabalFilesrcOrTgtdist_dir<-getDistDirletsetup_config=DSC.localBuildInfoFiledist_dirconf'd<-liftIO$doesFileExistsetup_configifnotconf'dthendoliftIO$putStrLn"configuring because setup_config not present"cabalConfiguresrcOrTgtelsedocabal_time<-liftIO$getModificationTimecabal_fileconf_time<-liftIO$getModificationTimesetup_configifcabal_time>conf_timethendoliftIO$putStrLn"configuring because setup_config too old"cabalConfiguresrcOrTgtelsedomb_lbi<-liftIO$DSC.maybeGetPersistBuildConfigdist_dircasemb_lbiofNothing->doliftIO$putStrLn"configuring because persist build config not present"cabalConfiguresrcOrTgtJust_lbi->return(Just_lbi,[])-- | run a action with the cabal build infowithCabal::WhichCabal-- ^ use original cabal or temp cabal file->(LocalBuildInfo->BuildWrappera)-- ^ action to run if we get a build info->BuildWrapper(OpResult(Maybea))-- ^ the result of the action, or Nothing if we could get Cabal infowithCabalsrcOrTgtf=do(mlbi,notes)<-cabalInitsrcOrTgtcasemlbiofNothing->return(Nothing,notes)Justlbi->dor<-flbireturn(Justr,notes)-- | parse cabal error messages and transform them in notreparseCabalMessages::FilePath-- ^ cabal file->FilePath-- ^ path to cabal executable->String-- ^ error output->[BWNote]parseCabalMessagescfcabalExes=let(m,ls)=foldlparseCabalLine(Nothing,[])$linessinnub$casemofNothing->lsJust(bwn,msgs)->ls++[makeNotebwnmsgs]whereparseCabalLine::(Maybe(BWNote,[String]),[BWNote])->String->(Maybe(BWNote,[String]),[BWNote])parseCabalLine(currentNote,ls)l|"Error:"`isPrefixOf`l=(Just(BWNoteBWError""(mkEmptySpancf11),[dropWhileisSpace$drop6l]),addCurrentcurrentNotels)|"Warning:"`isPrefixOf`l=letmsg=(dropWhileisSpace$drop8l)msg2=ifcf`isPrefixOf`msgthendropWhileisSpace$drop(lengthcf+1)msgelsemsgin(Just(BWNoteBWWarning""(mkEmptySpancf(extractLinemsg2)1),[msg2]),addCurrentcurrentNotels)|Just(bw,n)<-cabalErrorLinecfcabalExel=(Just(bw,n),addCurrentcurrentNotels)|Just(jcn,msgs)<-currentNote=ifnot$nulllthen(Just(jcn,l:msgs),ls)else(Nothing,ls++[makeNotejcnmsgs])|otherwise=(Nothing,ls)extractLineel=let(_,_,_,ls)=el=~"\\(line ([0-9]*)\\)"::(String,String,String,[String])inifnulllsthen1elsereadInt(headls)1setupExe::FilePath-- ^ path to cabal executable->FilePathsetupExecabalExe=addExtension"setup"$takeExtensioncabalExedropPrefixes::[String]->String->MaybeStringdropPrefixesprfxss2=foldr(stripPrefixIfNeededs2)NothingprfxsstripPrefixIfNeeded::String->String->MaybeString->MaybeStringstripPrefixIfNeeded__j@(Just_)=jstripPrefixIfNeededs3prfx_=stripPrefixprfxs3addCurrent::Maybe(BWNote,[String])->[BWNote]->[BWNote]addCurrentNothingxs=xsaddCurrent(Just(n,msgs))xs=xs++[makeNotenmsgs]cabalErrorLine::FilePath-- ^ cabal file->FilePath-- ^ path to cabal executable->String-- ^ line->Maybe(BWNote,[String])cabalErrorLinecfcabalExel|Justs4<-dropPrefixes[cabalExe,setupExecabalExe]l=lets2=dropWhileisSpace$drop1s4-- drop 1 for ":" that follows file nameinif"At least the following"`isPrefixOf`s2thenJust(BWNoteBWError""(mkEmptySpancf11),[s2])elselet(loc,rest)=span(/=':')s2(realloc,line,msg)=ifnullrest||":"==restthen(cf,"1",s2)elselettr=tailrest(line',msg')=span(/=':')trinifnullmsg'then(loc,"1",tr)elseifreadIntline'(-1)==(-1)then(cf,"1",s2)else(loc,line',tailmsg')inJust(BWNoteBWError""(mkEmptySpanrealloc(readIntline1)1),[msg])|otherwise=Nothing-- | parse messages from buildparseBuildMessages::FilePath-- ^ cabal file->FilePath-- ^ path to cabal executable->FilePath-- ^ the dist directory->String-- ^ the build output ->[BWNote]parseBuildMessagescfcabalExedistDirs=let(m,ls)=foldlparseBuildLine(Nothing,[])$linessin(nub$casemofNothing->lsJust(bwn,msgs)->ls++[makeNotebwnmsgs])whereparseBuildLine::(Maybe(BWNote,[String]),[BWNote])->String->(Maybe(BWNote,[String]),[BWNote])parseBuildLine(currentNote,ls)l|Just(jcn,msgs)<-currentNote=ifnot(nulll)&&((' '==headl)||(')'==lastl))then(Just(jcn,l:msgs),ls)else(Nothing,ls++[makeNotejcnmsgs])-- | Just fp<-getBuiltPath l=(currentNote,ls,fp:fps)|Justn<-extractLocationl=(Just(n,[bwnTitlen]),ls)|Just(bw,n)<-cabalErrorLinecfcabalExel=(Just(bw,n),addCurrentcurrentNotels)|otherwise=(Nothing,ls)extractLocationel=let(_,_,aft,ls)=el=~"(.+):([0-9]+):([0-9]+):"::(String,String,String,[String])incaselsof(loc:line:col:[])->Just$BWNoteBWError(dropWhileisSpaceaft)(mkEmptySpanloc(readIntline1)(readIntcol1))_->let(_,_,_,ls2)=el=~"(.+)(\\(.+\\)):(.+):(.+):"::(String,String,String,[String])incasels2of(loc2:ext1:_:_:[])->Just$BWNoteBWError(drop(lengthloc2+lengthext1+1)el)(mkEmptySpan(validLoccfdistDirloc2)11)_->NothingvalidLoc::FilePath-- ^ the cabal file ->FilePath-- ^ the dist dir->FilePath->FilePathvalidLoccfdistDirf=ifdistDir`isPrefixOf`fthencfelsefreadInt::String->Int->IntreadIntsdef=letparses=readss::[(Int,String)]inifnullparsesthendefelsefst$headparses-- | add a message to the notemakeNote::BWNote-- ^ original note->[String]-- ^ message lines->BWNotemakeNotebwnmsgs=lettitle=dropWhileisSpace$unlines$reversemsgsinif"Warning:"`isPrefixOf`titlethenbwn{bwnTitle=dropWhileisSpace$drop8title,bwnStatus=BWWarning}elsebwn{bwnTitle=title}-- | get the path of a file getting compiledgetBuiltPath::String-- ^ the message line->MaybeFilePath-- ^ the path if we could parse itgetBuiltPathline=let(_,_,_,ls)=line=~"\\[[0-9]+ of [0-9]+\\] Compiling .+\\( (.+), (.+)\\)"::(String,String,String,[String])incaselsof(src:_:[])->Justsrc_->Nothing-- | the cabal build info for a specific componentdataCabalBuildInfo=CabalBuildInfo{cbiBuildInfo::BuildInfo-- ^ the build info,cbiComponentBuildInfo::ComponentLocalBuildInfo-- ^ the component local build info,cbiBuildFolder::FilePath-- ^ the folder to build that component into,cbiIsLibrary::Bool-- ^ is the component the library,cbiModulePaths::[(MaybeModuleName,FilePath)]-- ^ the module name and corresponding source file for each contained Haskell module,cbiComponent::CabalComponent-- ^ the component handle}deriving(Read,Show)-- | canonicalize the paths in the build infocanonicalizeBuildInfo::CabalBuildInfo->BuildWrapperCabalBuildInfocanonicalizeBuildInfo=onModulePathsM(mapM(\(m,path)->dopathC<-canonicalizeFullPathpathreturn(m,pathC)))-- | apply a function on the build info modules and paths, in a monadonModulePathsM::(Monada)=>([(MaybeModuleName,FilePath)]->a[(MaybeModuleName,FilePath)])-- ^ the function to apply->CabalBuildInfo-- ^ the original build info->aCabalBuildInfo-- ^ the resultonModulePathsMfcbi=doletls=cbiModulePathscbifls<-flsreturncbi{cbiModulePaths=fls}-- | apply a function on the build info modules and pathsonModulePaths::([(MaybeModuleName,FilePath)]->[(MaybeModuleName,FilePath)])-- ^ the function to apply->CabalBuildInfo-- ^ the original build info->CabalBuildInfo-- ^ the resultonModulePathsf=runIdentity.onModulePathsM(return.f)-- | get the build info for a given source file-- if a source file is in several component, get the first one getBuildInfo::FilePath-- ^the source file->MaybeString-- ^ the cabal component to use, or Nothing if not specified->BuildWrapper(OpResult(Maybe(LocalBuildInfo,CabalBuildInfo)))getBuildInfofpmccn=docasemccnofNothing->do(mmr,bwns)<-gogetReferencedFilesNothingcasemmrofJust(Justa)->return(Justa,bwns)_->do(mmr2,bwns2)<-gogetAllFilesNothing-- no component found for the asked namereturn$casemmr2ofJust(Justa)->(Justa,bwns2)_->(Nothing,bwns)_->do(mmr2,bwns2)<-gogetAllFilesmccnreturn$casemmr2ofJust(Justa)->(Justa,bwns2)_->(Nothing,bwns2)wheregofmccn2=withCabalSource(\lbi->dofps<-flbifpsC<-mapMcanonicalizeBuildInfofpsok<-getCompmccn2fpsCreturn$ifnullokthenNothingelseJust(lbi,headok))getComp::MaybeString->[CabalBuildInfo]->BuildWrapper[CabalBuildInfo]getCompNothingfps=dofpC<-canonicalizeFullPathfpfpsC<-mapMcanonicalizeBuildInfofpsreturn$filter(not.null.cbiModulePaths)$map(onModulePaths(filter(\(_,b)->equalFilePathfpCb)))fpsCgetComp(Justccn)fps=return$filter(\cbi->cabalComponentName(cbiComponentcbi)==ccn)fps-- | get GHC options for a file fileGhcOptions::(LocalBuildInfo,CabalBuildInfo)-- ^ the cabal info->BuildWrapper[String]-- ^ the module name and the options to pass GHCfileGhcOptions(lbi,CabalBuildInfobiclbifpisLib__)=dodist_dir<-getDistDirletinplace=dist_dir</>"package.conf.inplace"inplaceExist<-liftIO$doesFileExistinplace#if MIN_VERSION_Cabal(1,15,0) v<-cabalVletoptslbcf=renderGhcOptions((fst$head$readP_to_SparseVersionVERSION_ghc)::Version)$componentGhcOptionsvlbcf#elseletopts=ghcOptions#endif letpkg|isLib=["-package-name",display$packageId$localPkgDescrlbi]|inplaceExist=["-package-conf",inplace]|otherwise=[]return(pkg++opts(lbi{withOptimization=NoOptimisation})biclbifp)-- | get CPP options for a filefileCppOptions::CabalBuildInfo-- ^ the cabal info->[String]-- ^ the list of CPP optionsfileCppOptionscbi=cppOptions$cbiBuildInfocbi-- | get the cabal extensionscabalExtensions::CabalBuildInfo-- ^ the cabal info->(ModuleName,[String])-- ^ the module name and cabal extensionscabalExtensionsCabalBuildInfo{cbiBuildInfo=bi,cbiModulePaths=ls}=(fromJust$fst$headls,mapshow(otherExtensionsbi++defaultExtensionsbi++oldExtensionsbi))-- | get the source directory from a build infogetSourceDirs::BuildInfo-- ^ the build info->[FilePath]-- ^ the source paths, guaranteed non nullgetSourceDirsbi=lethsd=hsSourceDirsbiincasehsdof[]->["."]_->hsd-- | get all components, referencing all the files found in the source folders getAllFiles::LocalBuildInfo-- ^ the build info->BuildWrapper[CabalBuildInfo]getAllFileslbi=doletpd=localPkgDescrlbiletlibs=maybe[]extractFromLib$librarypdletexes=mapextractFromExe$executablespdlettests=mapextractFromTest$testSuitespdcbis<-mapM(\(a,b,c,isLib,d,cc)->domf<-copyAlldreturn(CabalBuildInfoabcisLibmfcc))(libs++exes++tests)cbis2<-getReferencedFileslbireturn$zipWith(\c1@CabalBuildInfo{cbiModulePaths=cb1}CabalBuildInfo{cbiModulePaths=cb2}->c1{cbiModulePaths=nubOrd$cb1++cb2})cbiscbis2-- return cbiswhereextractFromLib::Library->[(BuildInfo,ComponentLocalBuildInfo,FilePath,Bool,[FilePath],CabalComponent)]extractFromLibl=letlib=libBuildInfolin[(lib,fromJustDebug"extractFromLibAll"$libraryConfiglbi,buildDirlbi,True,getSourceDirslib,cabalComponentFromLibraryl)]extractFromExe::Executable->(BuildInfo,ComponentLocalBuildInfo,FilePath,Bool,[FilePath],CabalComponent)extractFromExee@Executable{exeName=exeName'}=letebi=buildInfoetargetDir=buildDirlbi</>exeName'exeDir=targetDir</>(exeName'++"-tmp")hsd=getSourceDirsebiin(ebi,fromJustDebug"extractFromExeAll"$lookupexeName'$executableConfigslbi,exeDir,False,hsd,cabalComponentFromExecutablee)extractFromTest::TestSuite->(BuildInfo,ComponentLocalBuildInfo,FilePath,Bool,[FilePath],CabalComponent)extractFromTestt@TestSuite{testName=testName'}=lettbi=testBuildInfottargetDir=buildDirlbi</>testName'testDir=targetDir</>(testName'++"-tmp")hsd=getSourceDirstbiin(tbi,fromJustDebug"extractFromTestAll"$lookuptestName'$testSuiteConfigslbi,testDir,False,hsd,cabalComponentFromTestSuitet)copyAll::[FilePath]->BuildWrapper[(MaybeModuleName,FilePath)]copyAllfps=doallF<-mapMcopyAll'fpsreturn$concatallFcopyAll'::FilePath->BuildWrapper[(MaybeModuleName,FilePath)]copyAll'fp=docf<-getscabalFileletdir=takeDirectorycffullFP<-getFullSrcfpallF<-liftIO$getRecursiveContentsfullFPtf<-getstempFolderletcabalDist=takeDirectorytf</>"dist"-- exclude every file containing the temp folder name (".buildwrapper" by default)-- which may happen if . is a source pathletnotMyself=filter(not.isInfixOfcabalDist)$filter(not.isInfixOftf)allFreturn$map(\f->(simpleParse$fileToModule$makeRelativefullFPf,makeRelativedirf))notMyself-- return $ map (\(x,y)->(fromJust x,y)) $ filter (isJust . fst) $ map (\f->(simpleParse $ fileToModule $ makeRelative fullFP f,makeRelative dir f)) notMyself-- | get all components, referencing only the files explicitely indicated in the cabal filegetReferencedFiles::LocalBuildInfo->BuildWrapper[CabalBuildInfo]getReferencedFileslbi=doletpd=localPkgDescrlbiletlibs=maybe[]extractFromLib$librarypdletexes=mapextractFromExe$executablespdlettests=mapextractFromTest$testSuitespdletcbis=libs++exes++testsmapM(\c1@CabalBuildInfo{cbiModulePaths=cb1}->docb2<-filterM(\(_,f)->dofs<-getFullSrcfliftIO$doesFileExistfs)cb1returnc1{cbiModulePaths=cb2})cbiswhereextractFromLib::Library->[CabalBuildInfo]extractFromLibl=letlib=libBuildInfolmodules=PD.exposedModulesl++otherModuleslibin[CabalBuildInfolib(fromJustDebug"extractFromLibRef"$libraryConfiglbi)(buildDirlbi)True(copyModulesmodules(getSourceDirslib))(cabalComponentFromLibraryl)]extractFromExe::Executable->CabalBuildInfoextractFromExee@Executable{exeName=exeName'}=letebi=buildInfoetargetDir=buildDirlbi</>exeName'exeDir=targetDir</>(exeName'++"-tmp")modules=(otherModulesebi)hsd=getSourceDirsebiinCabalBuildInfoebi(fromJustDebug"extractFromExeRef"$lookupexeName'$executableConfigslbi)exeDirFalse(copyMain(modulePathe)hsd++copyModulesmoduleshsd)(cabalComponentFromExecutablee)extractFromTest::TestSuite->CabalBuildInfoextractFromTestt@TestSuite{testName=testName'}=lettbi=testBuildInfottargetDir=buildDirlbi</>testName'testDir=targetDir</>(testName'++"-tmp")modules=(otherModulestbi)hsd=getSourceDirstbiextras=casetestInterfacetof(TestSuiteExeV10_mp)->copyMainmphsd(TestSuiteLibV09_mn)->copyModules[mn]hsd_->[]inCabalBuildInfotbi(fromJustDebug("extractFromTestRef:"++testName'++show(testSuiteConfigslbi))$lookuptestName'$testSuiteConfigslbi)testDirFalse(extras++copyModulesmoduleshsd)(cabalComponentFromTestSuitet)copyModules::[ModuleName]->[FilePath]->[(MaybeModuleName,FilePath)]copyModulesmods=copyFiles(concatMap(\m->[toFilePathm<.>"hs",toFilePathm<.>"lhs"])mods)copyFiles::[FilePath]->[FilePath]->[(MaybeModuleName,FilePath)]copyFilesmodsdirs=letrmods=filter(isJust.snd)$map(\x->(x,simpleParse$fileToModulex))modsin[(Justmodu,d</>m)|(m,Justmodu)<-rmods,d<-dirs]copyMain::FilePath->[FilePath]->[(MaybeModuleName,FilePath)]copyMainfs=map(\d->(Just$fromString"Main",d</>fs))stringToModuleName::String->MaybeModuleNamestringToModuleName=simpleParse-- | convert a ModuleName to a String moduleToString::ModuleName->StringmoduleToString=intercalate".".components-- | get all components in the Cabal filecabalComponents::BuildWrapper(OpResult[CabalComponent])cabalComponents=do(rs,ns)<-withCabalSource(return.cabalComponentsFromDescription.localPkgDescr)return(fromMaybe[]rs,ns)-- | get all the dependencies in the cabal filecabalDependencies::BuildWrapper(OpResult[(FilePath,[CabalPackage])])-- ^ the result is an array of tuples: the path to the package database, the list of packages in that db that the Cabal file referencescabalDependencies=do(rs,ns)<-withCabalSource(\lbi->liftIO$ghandle(\(e::IOError)->doprintereturn[])$dopkgs<-liftIOgetPkgInfosreturn$dependencies(localPkgDescrlbi)pkgs)return(fromMaybe[]rs,ns)-- | get all dependencies from the package description and the list of installed packages dependencies::PD.PackageDescription-- ^ the cabal description->[(FilePath,[InstalledPackageInfo])]-- ^ the installed packages, by package database location->[(FilePath,[CabalPackage])]-- ^ the referenced packages, by package database locationdependenciespdpkgs=letpkgsMap=foldrbuildPkgMapDM.emptypkgs-- build the map of package by name with ordered version (more recent first)allC=cabalComponentsFromDescriptionpdgdeps=PD.buildDependspdcpkgs=concat$DM.elems$DM.map(\ipis->getDepallCipisgdeps[])pkgsMapinDM.assocs$DM.fromListWith(++)(map(\(a,b)->(a,[b]))cpkgs++map(\(a,_)->(a,[]))pkgs)wherebuildPkgMap::(FilePath,[InstalledPackageInfo])->DM.MapString[(FilePath,InstalledPackageInfo)]->DM.MapString[(FilePath,InstalledPackageInfo)]buildPkgMap(fp,ipis)m=foldr(\idm->letkey=display$pkgName$sourcePackageIdivals=DM.lookupkeydmnewvals=casevalsofNothing->[(fp,i)]Justl->sortBy(flip(comparing(pkgVersion.sourcePackageId.snd)))((fp,i):l)inDM.insertkeynewvalsdm)mipisgetDep::[CabalComponent]->[(FilePath,InstalledPackageInfo)]->[Dependency]->[(FilePath,CabalPackage)]->[(FilePath,CabalPackage)]getDep_[]_acc=accgetDepallC((fp,InstalledPackageInfo{sourcePackageId=i,exposed=e,IPI.exposedModules=ems,IPI.hiddenModules=hms}):xs)depsacc=let(ds,deps2)=partition(\(Dependencynv)->(pkgNamei==n)&&withinRange(pkgVersioni)v)deps-- find if version is referenced, remove the referencing component so that it doesn't match an older versioncps=ifnulldsthen[]elseallCmns=mapdisplay(ems++hms)ingetDepallCxsdeps2((fp,CabalPackage(display$pkgNamei)(display$pkgVersioni)ecpsmns):acc)-- build CabalPackage structure-- | get all components from the package description cabalComponentsFromDescription::PD.PackageDescription-- ^ the package description->[CabalComponent]cabalComponentsFromDescriptionpd=[cabalComponentFromLibrary$fromJust(PD.librarypd)|isJust(PD.librarypd)]++mapcabalComponentFromExecutable(PD.executablespd)++mapcabalComponentFromTestSuite(PD.testSuitespd)cabalComponentFromLibrary::Library->CabalComponentcabalComponentFromLibrary=CCLibrary.PD.buildable.PD.libBuildInfocabalComponentFromExecutable::Executable->CabalComponentcabalComponentFromExecutablee=CCExecutable(PD.exeNamee)(PD.buildable$PD.buildInfoe)cabalComponentFromTestSuite::TestSuite->CabalComponentcabalComponentFromTestSuitets=CCTestSuite(PD.testNamets)(PD.buildable$PD.testBuildInfots)