2 {-# LANGUAGE LambdaCase #-}
3 {-# LANGUAGE NamedFieldPuns #-}
4 {-# LANGUAGE RecordWildCards #-}
6 -- | This module exposes functions to build and register unpacked packages.
8 -- Mainly, unpacked packages are either:
9 -- * Built and registered in-place
10 -- * Built and installed
12 -- The two cases differ significantly for there to be a distinction.
13 -- For instance, we only care about file monitoring and re-building when dealing
14 -- with "inplace" registered packages, whereas for installed packages we don't.
15 module Distribution
.Client
.ProjectBuilding
.UnpackedPackage
16 ( buildInplaceUnpackedPackage
17 , buildAndInstallUnpackedPackage
19 -- ** Auxiliary definitions
20 , buildAndRegisterUnpackedPackage
21 , PackageBuildingPhase
25 , annotateFailureNoLog
28 import Distribution
.Client
.Compat
.Prelude
31 import Distribution
.Client
.PackageHash
(renderPackageHashInputs
)
32 import Distribution
.Client
.ProjectBuilding
.Types
33 import Distribution
.Client
.ProjectConfig
34 import Distribution
.Client
.ProjectConfig
.Types
35 import Distribution
.Client
.ProjectPlanning
36 import Distribution
.Client
.ProjectPlanning
.Types
37 import Distribution
.Client
.RebuildMonad
38 import Distribution
.Client
.Store
40 import Distribution
.Client
.DistDirLayout
41 import Distribution
.Client
.FileMonitor
42 import Distribution
.Client
.JobControl
43 import Distribution
.Client
.Setup
44 ( filterConfigureFlags
49 import Distribution
.Client
.SetupWrapper
50 import Distribution
.Client
.SourceFiles
51 import Distribution
.Client
.SrcDist
(allPackageSourceFiles
)
52 import qualified Distribution
.Client
.Tar
as Tar
53 import Distribution
.Client
.Types
hiding
59 import Distribution
.Client
.Utils
61 , findOpenProgramLocation
65 import Distribution
.Compat
.Lens
66 import Distribution
.InstalledPackageInfo
(InstalledPackageInfo
)
67 import qualified Distribution
.InstalledPackageInfo
as Installed
68 import Distribution
.Package
69 import qualified Distribution
.PackageDescription
as PD
70 import Distribution
.Simple
.BuildPaths
(haddockDirName
)
71 import Distribution
.Simple
.Command
(CommandUI
)
72 import Distribution
.Simple
.Compiler
76 import qualified Distribution
.Simple
.InstallDirs
as InstallDirs
77 import Distribution
.Simple
.LocalBuildInfo
81 import Distribution
.Simple
.Program
82 import qualified Distribution
.Simple
.Register
as Cabal
83 import qualified Distribution
.Simple
.Setup
as Cabal
84 import Distribution
.Types
.BuildType
85 import Distribution
.Types
.PackageDescription
.Lens
(componentModules
)
87 import Distribution
.Simple
.Utils
88 import Distribution
.Version
90 import qualified Data
.ByteString
as BS
91 import qualified Data
.ByteString
.Lazy
as LBS
92 import qualified Data
.ByteString
.Lazy
.Char8
as LBS
.Char8
93 import qualified Data
.List
.NonEmpty
as NE
95 import Control
.Exception
(Handler
(..), SomeAsyncException
, assert
, catches
)
96 import System
.Directory
(canonicalizePath
, createDirectoryIfMissing
, doesDirectoryExist, doesFileExist, removeFile)
97 import System
.FilePath (dropDrive
, normalise
, takeDirectory
, (<.>), (</>))
98 import System
.IO (Handle, IOMode (AppendMode
), withFile
)
99 import System
.Semaphore
(SemaphoreName
(..))
101 import Distribution
.Client
.Errors
102 import Distribution
.Compat
.Directory
(listDirectory
)
104 import Distribution
.Client
.ProjectBuilding
.PackageFileMonitor
106 -- | Each unpacked package is processed in the following phases:
110 -- * Install phase (copy + register)
117 -- Depending on whether we are installing the package or building it inplace,
118 -- the phases will be carried out differently. For example, when installing,
119 -- the test, benchmark, and repl phase are ignored.
120 data PackageBuildingPhase
121 = PBConfigurePhase
{runConfigure
:: IO ()}
122 | PBBuildPhase
{runBuild
:: IO ()}
123 | PBHaddockPhase
{runHaddock
:: IO ()}
125 { runCopy
:: FilePath -> IO ()
128 -> Cabal
.RegisterOptions
129 -> IO InstalledPackageInfo
131 | PBTestPhase
{runTest
:: IO ()}
132 | PBBenchPhase
{runBench
:: IO ()}
133 | PBReplPhase
{runRepl
:: IO ()}
135 -- | Structures the phases of building and registering a package amongst others
136 -- (see t'PackageBuildingPhase'). Delegates logic specific to a certain
137 -- building style (notably, inplace vs install) to the delegate function that
138 -- receives as an argument t'PackageBuildingPhase')
139 buildAndRegisterUnpackedPackage
142 -> Maybe SemaphoreName
143 -- ^ Whether to pass a semaphore to build process
144 -- this is different to BuildTimeSettings because the
145 -- name of the semaphore is created freshly each time.
149 -> ElaboratedSharedConfig
150 -> ElaboratedInstallPlan
151 -> ElaboratedReadyPackage
155 -- ^ The path to an /initialized/ log file
156 -> (PackageBuildingPhase
-> IO ())
158 buildAndRegisterUnpackedPackage
160 distDirLayout
@DistDirLayout
{distTempDirectory
}
162 BuildTimeSettings
{buildSettingNumJobs
}
165 pkgshared
@ElaboratedSharedConfig
166 { pkgConfigCompiler
= compiler
167 , pkgConfigCompilerProgs
= progdb
170 rpkg
@(ReadyPackage pkg
)
178 annotateFailure mlogFile ConfigureFailed
$
179 setup configureCommand configureFlags configureArgs
184 annotateFailure mlogFile BuildFailed
$
185 setup buildCommand buildFlags buildArgs
191 annotateFailure mlogFile HaddocksFailed
$ do
192 setup haddockCommand haddockFlags haddockArgs
197 { runCopy
= \destdir
->
198 annotateFailure mlogFile InstallFailed
$
199 setup Cabal
.copyCommand
(copyFlags destdir
) (const [])
200 , runRegister
= \pkgDBStack registerOpts
->
201 annotateFailure mlogFile InstallFailed
$ do
202 -- We register ourselves rather than via Setup.hs. We need to
203 -- grab and modify the InstalledPackageInfo. We decide what
204 -- the installed package id is, not the build system.
205 ipkg0
<- generateInstalledPackageInfo
206 let ipkg
= ipkg0
{Installed
.installedUnitId
= uid
}
207 criticalSection registerLock
$
208 Cabal
.registerPackage
222 annotateFailure mlogFile TestsFailed
$
223 setup testCommand testFlags testArgs
229 annotateFailure mlogFile BenchFailed
$
230 setup benchCommand benchFlags benchArgs
236 annotateFailure mlogFile ReplFailed
$
237 setupInteractive replCommand replFlags replArgs
241 uid
= installedUnitId rpkg
243 comp_par_strat
= case maybe_semaphore
of
244 Just sem_name
-> Cabal
.toFlag
(getSemaphoreName sem_name
)
248 |
null (elabTestTargets pkg
) = return ()
252 |
null (elabBenchTargets pkg
) = return ()
256 |
null (elabReplTarget pkg
) = return ()
260 | hasValidHaddockTargets pkg
= action
261 |
otherwise = return ()
263 configureCommand
= Cabal
.configureCommand defaultProgramDb
265 flip filterConfigureFlags v
$
266 setupHsConfigureFlags
272 configureArgs _
= setupHsConfigureArgs pkg
274 buildCommand
= Cabal
.buildCommand defaultProgramDb
275 buildFlags _
= setupHsBuildFlags comp_par_strat pkg pkgshared verbosity builddir
276 buildArgs _
= setupHsBuildArgs pkg
278 copyFlags destdir _
=
286 testCommand
= Cabal
.testCommand
-- defaultProgramDb
288 flip filterTestFlags v
$
293 testArgs _
= setupHsTestArgs pkg
295 benchCommand
= Cabal
.benchmarkCommand
302 benchArgs _
= setupHsBenchArgs pkg
304 replCommand
= Cabal
.replCommand defaultProgramDb
311 replArgs _
= setupHsReplArgs pkg
313 haddockCommand
= Cabal
.haddockCommand
315 flip filterHaddockFlags v
$
322 flip filterHaddockArgs v
$
323 setupHsHaddockArgs pkg
333 (isParallelBuild buildSettingNumJobs
)
338 -> (Version
-> flags
)
339 -> (Version
-> [String])
341 setup cmd flags args
=
342 withLogging
$ \mLogFileHandle
->
346 { useLoggingHandle
= mLogFileHandle
347 , useExtraEnvOverrides
=
348 dataDirsEnvironmentForPlan
352 (Just
(elabPkgDescription pkg
))
359 -> (Version
-> flags
)
360 -> (Version
-> [String])
362 setupInteractive cmd flags args
=
365 scriptOptions
{isInteractive
= True}
366 (Just
(elabPkgDescription pkg
))
371 generateInstalledPackageInfo
:: IO InstalledPackageInfo
372 generateInstalledPackageInfo
=
373 withTempInstalledPackageInfoFile
377 let registerFlags _
=
384 setup Cabal
.registerCommand registerFlags
(const [])
386 withLogging
:: (Maybe Handle -> IO r
) -> IO r
389 Nothing
-> action Nothing
390 Just logFile
-> withFile logFile AppendMode
(action
. Just
)
392 --------------------------------------------------------------------------------
396 --------------------------------------------------------------------------------
398 buildInplaceUnpackedPackage
401 -> Maybe SemaphoreName
405 -> ElaboratedSharedConfig
406 -> ElaboratedInstallPlan
407 -> ElaboratedReadyPackage
408 -> BuildStatusRebuild
412 buildInplaceUnpackedPackage
414 distDirLayout
@DistDirLayout
415 { distPackageCacheDirectory
417 , distHaddockOutputDir
420 buildSettings
@BuildTimeSettings
{buildSettingHaddockOpen
}
423 pkgshared
@ElaboratedSharedConfig
{pkgConfigPlatform
= platform
}
425 rpkg
@(ReadyPackage pkg
)
429 -- TODO: [code cleanup] there is duplication between the
430 -- distdirlayout and the builddir here builddir is not
431 -- enough, we also need the per-package cachedir
432 createDirectoryIfMissingVerbose verbosity
True builddir
433 createDirectoryIfMissingVerbose
436 (distPackageCacheDirectory dparams
)
438 let docsResult
= DocsNotTried
439 testsResult
= TestsNotTried
441 buildResult
:: BuildResultMisc
442 buildResult
= (docsResult
, testsResult
)
444 buildAndRegisterUnpackedPackage
456 Nothing
-- no log file for inplace builds!
458 PBConfigurePhase
{runConfigure
} -> do
461 invalidatePackageRegFileMonitor packageFileMonitor
462 updatePackageConfigFileMonitor packageFileMonitor srcdir pkg
463 PBBuildPhase
{runBuild
} -> do
465 timestamp
<- beginUpdateFileMonitor
469 execRebuild srcdir
(needElaboratedConfiguredPackage pkg
)
471 fmap (map monitorFileHashed
) $
472 allPackageSourceFiles verbosity srcdir
475 if null xs
then m
' else return xs
476 monitors
<- case PD
.buildType
(elabPkgDescription pkg
) of
478 -- If a Custom setup was used, AND the Cabal is recent
479 -- enough to have sdist --list-sources, use that to
480 -- determine the files that we need to track. This can
481 -- cause unnecessary rebuilding (for example, if README
482 -- is edited, we will try to rebuild) but there isn't
483 -- a more accurate Custom interface we can use to get
484 -- this info. We prefer not to use listSimple here
485 -- as it can miss extra source files that are considered
486 -- by the Custom setup.
488 | elabSetupScriptCliVersion pkg
>= mkVersion
[1, 17] ->
489 -- However, sometimes sdist --list-sources will fail
490 -- and return an empty list. In that case, fall
491 -- back on the (inaccurate) simple tracking.
492 listSdist `ifNullThen` listSimple
497 map monitorFileHashed
$
498 elabInplaceDependencyBuildCacheFiles
503 updatePackageBuildFileMonitor
509 (monitors
++ dep_monitors
)
511 PBHaddockPhase
{runHaddock
} -> do
513 let haddockTarget
= elabHaddockForHackage pkg
514 when (haddockTarget
== Cabal
.ForHackage
) $ do
515 let dest
= distDirectory
</> name
<.> "tar.gz"
516 name
= haddockDirName haddockTarget
(elabPkgDescription pkg
)
518 distBuildDirectory distDirLayout dparams
521 Tar
.createTarGzFile dest docDir name
522 notice verbosity
$ "Documentation tarball created: " ++ dest
524 when (buildSettingHaddockOpen
&& haddockTarget
/= Cabal
.ForHackage
) $ do
525 let dest
= docDir
</> "index.html"
526 name
= haddockDirName haddockTarget
(elabPkgDescription pkg
)
527 docDir
= case distHaddockOutputDir
of
528 Nothing
-> distBuildDirectory distDirLayout dparams
</> "doc" </> "html" </> name
530 exe
<- findOpenProgramLocation platform
532 Right open
-> runProgramInvocation verbosity
(simpleProgramInvocation open
[dest
])
533 Left err
-> dieWithException verbosity
$ FindOpenProgramLocationErr err
534 PBInstallPhase
{runCopy
= _runCopy
, runRegister
} -> do
535 -- PURPOSELY omitted: no copy!
540 if elabRequiresRegistration pkg
544 (elabRegisterPackageDBStack pkg
)
545 Cabal
.defaultRegisterOptions
549 updatePackageRegFileMonitor packageFileMonitor srcdir mipkg
550 PBTestPhase
{runTest
} -> runTest
551 PBBenchPhase
{runBench
} -> runBench
552 PBReplPhase
{runRepl
} -> runRepl
556 { buildResultDocs
= docsResult
557 , buildResultTests
= testsResult
558 , buildResultLogFile
= Nothing
561 dparams
= elabDistDirParams pkgshared pkg
563 packageFileMonitor
= newPackageFileMonitor pkgshared distDirLayout dparams
565 whenReConfigure action
= case buildStatus
of
566 BuildStatusConfigure _
-> action
570 |
null (elabBuildTargets pkg
)
571 , -- NB: we have to build the test/bench suite!
572 null (elabTestTargets pkg
)
573 , null (elabBenchTargets pkg
) =
577 whenReRegister action
=
579 -- We registered the package already
580 BuildStatusBuild
(Just _
) _
->
581 info verbosity
"whenReRegister: previously registered"
582 -- There is nothing to register
584 |
null (elabBuildTargets pkg
) ->
585 info verbosity
"whenReRegister: nothing to register"
586 |
otherwise -> action
588 --------------------------------------------------------------------------------
590 -- * Build and Install
592 --------------------------------------------------------------------------------
594 buildAndInstallUnpackedPackage
598 -> Maybe SemaphoreName
599 -- ^ Whether to pass a semaphore to build process
600 -- this is different to BuildTimeSettings because the
601 -- name of the semaphore is created freshly each time.
605 -> ElaboratedSharedConfig
606 -> ElaboratedInstallPlan
607 -> ElaboratedReadyPackage
611 buildAndInstallUnpackedPackage
614 storeDirLayout
@StoreDirLayout
615 { storePackageDBStack
618 buildSettings
@BuildTimeSettings
{buildSettingNumJobs
, buildSettingLogFile
}
621 pkgshared
@ElaboratedSharedConfig
622 { pkgConfigCompiler
= compiler
623 , pkgConfigPlatform
= platform
626 rpkg
@(ReadyPackage pkg
)
629 createDirectoryIfMissingVerbose verbosity
True (srcdir
</> builddir
)
631 -- TODO: [code cleanup] deal consistently with talking to older
632 -- Setup.hs versions, much like we do for ghc, with a proper
633 -- options type and rendering step which will also let us
634 -- call directly into the lib, rather than always going via
635 -- the lib's command line interface, which would also allow
636 -- passing data like installed packages, compiler, and
637 -- program db for a quicker configure.
639 -- TODO: [required feature] docs and tests
640 -- TODO: [required feature] sudo re-exec
644 buildAndRegisterUnpackedPackage
658 PBConfigurePhase
{runConfigure
} -> do
659 noticeProgress ProgressStarting
661 PBBuildPhase
{runBuild
} -> do
662 noticeProgress ProgressBuilding
664 PBHaddockPhase
{runHaddock
} -> do
665 noticeProgress ProgressHaddock
667 PBInstallPhase
{runCopy
, runRegister
} -> do
668 noticeProgress ProgressInstalling
671 |
not (elabRequiresRegistration pkg
) =
673 "registerPkg: elab does NOT require registration for "
677 ( elabRegisterPackageDBStack pkg
678 == storePackageDBStack compid
683 (storePackageDBStack compid
)
684 Cabal
.defaultRegisterOptions
685 { Cabal
.registerMultiInstance
= True
686 , Cabal
.registerSuppressFilesCheck
= True
690 -- Actual installation
697 (copyPkgFiles verbosity pkgshared pkg runCopy
)
700 -- No tests on install
701 PBTestPhase
{} -> return ()
702 -- No bench on install
703 PBBenchPhase
{} -> return ()
704 -- No repl on install
705 PBReplPhase
{} -> return ()
707 -- TODO: [nice to have] we currently rely on Setup.hs copy to do the right
708 -- thing. Although we do copy into an image dir and do the move into the
709 -- final location ourselves, perhaps we ought to do some sanity checks on
710 -- the image dir first.
712 -- TODO: [required eventually] note that for nix-style
713 -- installations it is not necessary to do the
714 -- 'withWin32SelfUpgrade' dance, but it would be necessary for a
717 -- TODO: [required feature] docs and test phases
718 let docsResult
= DocsNotTried
719 testsResult
= TestsNotTried
721 noticeProgress ProgressCompleted
725 { buildResultDocs
= docsResult
726 , buildResultTests
= testsResult
727 , buildResultLogFile
= mlogFile
730 uid
= installedUnitId rpkg
731 pkgid
= packageId rpkg
732 compid
= compilerId compiler
735 dispname
= case elabPkgOrComp pkg
of
738 ++ " (all, legacy fallback)"
739 ElabComponent comp
->
742 ++ maybe "custom" prettyShow
(compComponentName comp
)
745 noticeProgress
:: ProgressPhase
-> IO ()
746 noticeProgress
phase =
747 when (isParallelBuild buildSettingNumJobs
) $
748 progressMessage verbosity
phase dispname
750 mlogFile
:: Maybe FilePath
752 case buildSettingLogFile
of
754 Just mkLogFile
-> Just
(mkLogFile compiler platform pkgid uid
)
761 createDirectoryIfMissing
True (takeDirectory logFile
)
762 exists
<- doesFileExist logFile
763 when exists
$ removeFile logFile
765 -- | The copy part of the installation phase when doing build-and-install
768 -> ElaboratedSharedConfig
769 -> ElaboratedConfiguredPackage
770 -> (FilePath -> IO ())
771 -- ^ The 'runCopy' function which invokes ./Setup copy for the
774 -- ^ The temporary dir file path
775 -> IO (FilePath, [FilePath])
776 copyPkgFiles verbosity pkgshared pkg runCopy tmpDir
= do
777 let tmpDirNormalised
= normalise tmpDir
778 runCopy tmpDirNormalised
779 -- Note that the copy command has put the files into
780 -- @$tmpDir/$prefix@ so we need to return this dir so
781 -- the store knows which dir will be the final store entry.
784 dropDrive
(InstallDirs
.prefix
(elabInstallDirs pkg
))
785 entryDir
= tmpDirNormalised
</> prefix
787 -- if there weren't anything to build, it might be that directory is not created
788 -- the @setup Cabal.copyCommand@ above might do nothing.
789 -- https://github.com/haskell/cabal/issues/4130
790 createDirectoryIfMissingVerbose verbosity
True entryDir
792 let hashFileName
= entryDir
</> "cabal-hash.txt"
793 outPkgHashInputs
= renderPackageHashInputs
(packageHashInputs pkgshared pkg
)
796 "creating file with the inputs used to compute the package hash: " ++ hashFileName
798 LBS
.writeFile hashFileName outPkgHashInputs
800 debug verbosity
"Package hash inputs:"
802 (debug verbosity
. ("> " ++))
803 (lines $ LBS
.Char8
.unpack outPkgHashInputs
)
805 -- Ensure that there are no files in `tmpDir`, that are
806 -- not in `entryDir`. While this breaks the
807 -- prefix-relocatable property of the libraries, it is
808 -- necessary on macOS to stay under the load command limit
809 -- of the macOS mach-o linker. See also
810 -- @PackageHash.hashedInstalledPackageIdVeryShort@.
812 -- We also normalise paths to ensure that there are no
813 -- different representations for the same path. Like / and
814 -- \\ on windows under msys.
816 filter (not . isPrefixOf entryDir
)
817 <$> listFilesRecursive tmpDirNormalised
818 -- Here's where we could keep track of the installed files
819 -- ourselves if we wanted to by making a manifest of the
820 -- files in the tmp dir.
821 return (entryDir
, otherFiles
)
823 listFilesRecursive
:: FilePath -> IO [FilePath]
824 listFilesRecursive path
= do
825 files
<- fmap (path
</>) <$> (listDirectory path
)
826 allFiles
<- for files
$ \file
-> do
827 isDir
<- doesDirectoryExist file
829 then listFilesRecursive file
831 return (concat allFiles
)
833 --------------------------------------------------------------------------------
837 --------------------------------------------------------------------------------
839 {- FOURMOLU_DISABLE -}
840 annotateFailureNoLog
:: (SomeException
-> BuildFailureReason
)
842 annotateFailureNoLog annotate action
=
843 annotateFailure Nothing annotate action
845 annotateFailure
:: Maybe FilePath
846 -> (SomeException
-> BuildFailureReason
)
848 annotateFailure mlogFile annotate action
=
850 -- It's not just IOException and ExitCode we have to deal with, there's
851 -- lots, including exceptions from the hackage-security and tar packages.
852 -- So we take the strategy of catching everything except async exceptions.
854 #if MIN_VERSION_base
(4,7,0)
855 Handler
$ \async
-> throwIO
(async
:: SomeAsyncException
)
857 Handler
$ \async
-> throwIO
(async
:: AsyncException
)
859 , Handler
$ \other
-> handler
(other
:: SomeException
)
862 handler
:: Exception e
=> e
-> IO a
863 handler
= throwIO
. BuildFailure mlogFile
. annotate
. toException
865 --------------------------------------------------------------------------------
867 --------------------------------------------------------------------------------
869 hasValidHaddockTargets
:: ElaboratedConfiguredPackage
-> Bool
870 hasValidHaddockTargets ElaboratedConfiguredPackage
{..}
871 |
not elabBuildHaddocks
= False
872 |
otherwise = any componentHasHaddocks components
874 components
:: [ComponentTarget
]
880 ++ elabHaddockTargets
882 componentHasHaddocks
:: ComponentTarget
-> Bool
883 componentHasHaddocks
(ComponentTarget name _
) =
885 CLibName LMainLibName
-> hasHaddocks
886 CLibName
(LSubLibName _
) -> elabHaddockInternal
&& hasHaddocks
887 CFLibName _
-> elabHaddockForeignLibs
&& hasHaddocks
888 CExeName _
-> elabHaddockExecutables
&& hasHaddocks
889 CTestName _
-> elabHaddockTestSuites
&& hasHaddocks
890 CBenchName _
-> elabHaddockBenchmarks
&& hasHaddocks
892 hasHaddocks
= not (null (elabPkgDescription ^
. componentModules name
))
894 withTempInstalledPackageInfoFile
897 -> (FilePath -> IO ())
898 -> IO InstalledPackageInfo
899 withTempInstalledPackageInfoFile verbosity tempdir action
=
900 withTempDirectory verbosity tempdir
"package-registration-" $ \dir
-> do
901 -- make absolute since @action@ will often change directory
902 abs_dir
<- canonicalizePath dir
904 let pkgConfDest
= abs_dir
</> "pkgConf"
907 readPkgConf
"." pkgConfDest
909 pkgConfParseFailed
:: String -> IO a
910 pkgConfParseFailed perror
=
911 dieWithException verbosity
$ PkgConfParseFailed perror
913 readPkgConf
:: FilePath -> FilePath -> IO InstalledPackageInfo
914 readPkgConf pkgConfDir pkgConfFile
= do
915 pkgConfStr
<- BS
.readFile (pkgConfDir
</> pkgConfFile
)
916 (warns
, ipkg
) <- case Installed
.parseInstalledPackageInfo pkgConfStr
of
917 Left perrors
-> pkgConfParseFailed
$ unlines $ NE
.toList perrors
918 Right
(warns
, ipkg
) -> return (warns
, ipkg
)
920 unless (null warns
) $