Commit afa166f5 authored by Michael Hanus's avatar Michael Hanus
Browse files

CPM updated

parent 18aa07bd
......@@ -87,7 +87,8 @@ To install and use CPM, a working installation of either
PAKCS in version 1.14.1 or greater, or
KiCS2 in version 0.5.1 or greater is required. Additionally, CPM requires
\emph{Git}\footnote{\url{http://www.git-scm.com}},
\emph{curl}\footnote{\url{https://curl.haxx.se}}
\emph{curl}\footnote{\url{https://curl.haxx.se}},
\emph{tar},
and \emph{unzip} to be available on the \code{PATH} during installation and
operation. You also need to ensure that your Haskell installations reads files
using UTF-8 encoding by default. Haskell uses the system locale charmap for its
......@@ -390,8 +391,8 @@ your own local copy.
\code{cypm link} takes a directory containing a copy of one of the current
package's dependencies as its argument. It creates a symbolic link from that
directory the the current package's local package cache. If you had a copy of
\code{A-1.0.3} in the \code{~/src/A-1.0.3} directory, you could use
\code{cypm link ~/src/A-1.0.3} to ensure that any time \code{A-1.0.3} is used
\code{A-1.0.3} in the \code{\char126/src/A-1.0.3} directory, you could use
\code{cypm link \char126/src/A-1.0.3} to ensure that any time \code{A-1.0.3} is used
from the current package, your local copy is used instead of the one from the
global package cache. To remove any links, use \code{cypm upgrade} without any
arguments, which will clear the local package cache. See
......@@ -551,7 +552,9 @@ There are three things that need to be done to publish a package: make the
package accessible somewhere, add the location to the package specification, and
add the package specification to the central package index.
CPM supports ZIP files accessible over HTTP as well as Git repositories as
CPM supports ZIP (suffix \ccode{.zip}) or
compressed TAR (suffix \ccode{.tar.gz}) files
accessible over HTTP as well as Git repositories as
package sources. You are free to choose one of those, but a publicly accessible
Git repository is preferred. To add the location to the package specification,
use the \code{source} key. For a HTTP source, use:
......@@ -710,6 +713,12 @@ This option adds the elapsed time to every info or debug output line.
The available commands of CPM are:
\begin{description}
\item[\fbox{\code{config}}]
Shows the current configuration of CPM
(see also Section~\ref{sec:config}).
The option \code{--all} shows also the names and version
of the packages installed in the global package cache.
\item[\fbox{\code{info}}] Gives information on the current package, e.g. the
package's name, author, synopsis and its dependency specifications.
......@@ -865,6 +874,8 @@ Using the option \code{--docdir}, one can specify the
target directory where the documentation should be stored.
If this option is not provided, \ccode{cdoc} is used as the documentation
directory.
The actual documentation will be stored in the subdirectory
\code{$name$-$version$} of the documentation directory.
The API documentation in HTML format is generated with CurryDoc.
If the package specification contains a list of exported modules
......@@ -878,6 +889,17 @@ are documented.
Using the option \code{--modules}, one can also specify a comma-separated
list of module names to be documented.
In the default case, modules contained in packages used by the current package
are not documented. Instead, it is assumed that these packages are
already documented\footnote{See
\url{http://www.informatik.uni-kiel.de/~curry/cpm/}
for the documentation of all packages. This default location
can be changed with the option \code{--url}.}
so that links to these package documentations are generated.
Using the option \code{--full}, one can generate also the documentation
of packages used by the current package. This might be reasonable
if one uses packages which are only locally installed.
The manual is generated only if the package specification contains
a field \code{documentation} where the main file of the manual
is specified (see Section~\ref{sec:reference} for more details).
......
......@@ -11,11 +11,14 @@ import Directory ( doesFileExist, getAbsolutePath, doesDirectoryExist
, getCurrentDirectory, getDirectoryContents
, getModificationTime
, renameFile, removeFile, setCurrentDirectory )
import Distribution ( stripCurrySuffix, addCurrySubdir )
import Distribution ( curryCompiler, installDir, stripCurrySuffix
, addCurrySubdir )
import Either
import FilePath ( (</>), splitSearchPath, replaceExtension, takeExtension )
import FilePath ( (</>), splitSearchPath, replaceExtension, takeExtension
, pathSeparator, isPathSeparator )
import IO ( hFlush, stdout )
import List ( groupBy, intercalate, isSuffixOf, nub, split, splitOn )
import List ( groupBy, intercalate, isPrefixOf, isSuffixOf, nub, split
, splitOn )
import Sort ( sortBy )
import System ( getArgs, getEnviron, setEnviron, unsetEnviron, exitWith
, system )
......@@ -33,6 +36,7 @@ import CPM.PackageCache.Global ( GlobalCache, readInstalledPackagesFromDir
, installFromZip, checkoutPackage, allPackages
, uninstallPackage, isPackageInstalled )
import CPM.Package
import CPM.Package.Helpers
import CPM.Resolution ( isCompatibleToCompiler, showResult )
import CPM.Repository ( Repository, readRepository, findVersion, listPackages
, findAllVersions, findLatestVersion, updateRepository
......@@ -48,7 +52,7 @@ cpmBanner :: String
cpmBanner = unlines [bannerLine,bannerText,bannerLine]
where
bannerText =
"Curry Package Manager <curry-language.org/tools/cpm> (version of 23/12/2017)"
"Curry Package Manager <curry-language.org/tools/cpm> (version of 02/05/2018)"
bannerLine = take (length bannerText) (repeat '-')
main :: IO ()
......@@ -101,7 +105,6 @@ runWithArgs opts = do
Search o -> searchCmd o config repo
_ -> do globalCache <- getGlobalCache config repo
case optCommand opts of
--PkgInfo o -> infoCmd o config repo globalCache
Checkout o -> checkout o config repo globalCache
InstallApp o -> installapp o config repo globalCache
Install o -> install o config repo globalCache
......@@ -220,6 +223,10 @@ data DocOptions = DocOptions
, docModules :: Maybe [String] -- modules to be documented
, docPrograms :: Bool -- generate documentation for programs
, docManual :: Bool -- generate manual (if specified)
, docGenImports :: Bool -- generate documentation for imported pkgs
-- (otherwise, use their standard docs)
, docPackageURL :: String -- the URL prefix where all repository
-- packages are documented
}
data TestOptions = TestOptions
......@@ -300,7 +307,12 @@ execOpts s = case optCommand s of
docOpts :: Options -> DocOptions
docOpts s = case optCommand s of
Doc opts -> opts
_ -> DocOptions Nothing Nothing True True
_ -> DocOptions Nothing Nothing True True False defaultBaseDocURL
-- The default URL prefix where all repository packages are documented.
-- Can be overwritten with a doc command option.
defaultBaseDocURL :: String
defaultBaseDocURL = "https://www.informatik.uni-kiel.de/~curry/cpm/DOC"
testOpts :: Options -> TestOptions
testOpts s = case optCommand s of
......@@ -576,6 +588,19 @@ optionParser allargs = optParser
<> long "text"
<> help "Generate only manual (according to package specification)"
<> optional )
<.> flag (\a -> Right $ a { optCommand = Doc (docOpts a)
{ docGenImports = True } })
( short "f"
<> long "full"
<> help "Generate full program documentation (i.e., also imported packages)"
<> optional )
<.> option (\s a -> Right $ a { optCommand =
Doc (docOpts a) { docPackageURL = s } })
( long "url"
<> short "u"
<> help ("The URL prefix where all repository packages are " ++
"documented. Default: " ++ defaultBaseDocURL)
<> optional )
testArgs =
option (\s a -> Right $ a { optCommand = Test (testOpts a)
......@@ -1087,6 +1112,7 @@ addPackageCmd pkgdir force config = do
createDirectoryIfMissing True pkgRepositoryDir
copyFile (pkgdir </> "package.json") (pkgRepositoryDir </> "package.json")
copyDirectory pkgdir pkgInstallDir
inDirectory pkgInstallDir (cleanPackage Debug)
updateRepositoryCache config
useForce :: String
......@@ -1144,18 +1170,18 @@ docCmd :: DocOptions -> Config -> IO (Repository,GlobalCache)
docCmd opts cfg getRepoGC =
getLocalPackageSpec "." |>= \specDir ->
loadPackageSpec specDir |>= \pkg -> do
let docdir = maybe "cdoc" id (docDir opts)
let docdir = maybe "cdoc" id (docDir opts) </> packageId pkg
absdocdir <- getAbsolutePath docdir
createDirectoryIfMissing True absdocdir
(if docManual opts then genPackageManual opts cfg pkg absdocdir
(if docManual opts then genPackageManual pkg specDir absdocdir
else succeedIO ()) |>
(if docPrograms opts then genDocForPrograms opts cfg getRepoGC specDir pkg
(if docPrograms opts then genDocForPrograms opts cfg getRepoGC
absdocdir specDir pkg
else succeedIO ())
--- Generate manual according to documentation specification of package.
genPackageManual :: DocOptions -> Config -> Package -> String
-> IO (ErrorLogger ())
genPackageManual _ _ pkg outputdir = case documentation pkg of
genPackageManual :: Package -> String -> String -> IO (ErrorLogger ())
genPackageManual pkg specDir outputdir = case documentation pkg of
Nothing -> succeedIO ()
Just (PackageDocumentation docdir docmain doccmd) -> do
let formatcmd = replaceSubString "OUTDIR" outputdir $
......@@ -1166,7 +1192,7 @@ genPackageManual _ _ pkg outputdir = case documentation pkg of
docmain ++ "' (unknown kind)"
else do
debugMessage $ "Executing command: " ++ formatcmd
inDirectory docdir $ system formatcmd
inDirectory (specDir </> docdir) $ system formatcmd
let outfile = outputdir </> replaceExtension docmain ".pdf"
system ("chmod -f 644 " ++ quote outfile) -- make it readable
infoMessage $ "Package documentation written to '" ++ outfile ++ "'."
......@@ -1200,11 +1226,11 @@ replaceSubString sub newsub s = replString s
--- package), on the main executable (if specified in the package),
--- or on all source modules of the package.
genDocForPrograms :: DocOptions -> Config -> IO (Repository,GlobalCache)
-> String -> Package -> IO (ErrorLogger ())
genDocForPrograms opts cfg getRepoGC specDir pkg = do
-> String -> String -> Package -> IO (ErrorLogger ())
genDocForPrograms opts cfg getRepoGC docdir specDir pkg = do
abspkgdir <- getAbsolutePath specDir
checkCompiler cfg pkg
let docdir = maybe "cdoc" id (docDir opts)
exports = exportedModules pkg
let exports = exportedModules pkg
mainmod = maybe Nothing
(\ (PackageExecutable _ emain _) -> Just emain)
(executableSpec pkg)
......@@ -1220,26 +1246,49 @@ genDocForPrograms opts cfg getRepoGC specDir pkg = do
if null docmods
then putStrLn "No modules to be documented!" >> succeedIO ()
else
getCurryLoadPath cfg specDir getRepoGC |>= \currypath ->
let pkgurls = path2packages abspkgdir currypath in
if apidoc
then foldEL (\_ -> docModule specDir docdir) () docmods |>
runDocCmd specDir
([currydoc, "--title", apititle, "--onlyindexhtml",
docdir] ++ docmods) |>
then foldEL (\_ -> docModule currypath pkgurls) () docmods |>
runDocCmd currypath pkgurls
(["--title", apititle, "--onlyindexhtml", docdir]
++ docmods) |>
log Info ("Documentation generated in '"++docdir++"'")
else runDocCmd specDir [currydoc, docdir, head docmods]
else runDocCmd currypath pkgurls [docdir, head docmods]
where
apititle = "\"API Documentation of Package '" ++ name pkg ++ "'\""
currydoc = curryExec cfg ++ " doc"
docModule pkgdir docdir mod =
runDocCmd pkgdir [currydoc, "--noindexhtml", docdir, mod]
docModule currypath uses mod =
runDocCmd currypath uses ["--noindexhtml", docdir, mod]
runDocCmd pkgdir doccmd = do
let cmd = unwords doccmd
runDocCmd currypath uses docparams = do
let useopts = if docGenImports opts
then []
else map (\ (d,u) -> "--use "++d++"@"++u) uses
cmd = unwords (currydoc : useopts ++ docparams)
infoMessage $ "Running CurryDoc: " ++ cmd
execWithPkgDir (ExecOptions cmd) cfg getRepoGC pkgdir
execWithCurryPath (ExecOptions cmd) cfg currypath
-- translates a path into a list of package paths and their doc URLs:
path2packages absdir path =
let dirs = splitSearchPath path
importPkgDir = absdir </> ".cpm" </> "packages" ++ [pathSeparator]
isImportedPackage d = importPkgDir `isPrefixOf` d
impPkg2URL p =
let (d,_) = break isPathSeparator (drop (length importPkgDir) p)
in docPackageURL opts ++ "/" ++ d
in map (\ip -> (ip,impPkg2URL ip)) (filter isImportedPackage dirs) ++
if name pkg == "base"
then [] -- in order to generate base package documentation
else [(installDir </> "lib",
-- docPackageURL opts ++ "/base-" ++ compilerBaseVersion cfg
if curryCompiler == "kics2" then kics2LibDocs else pakcsLibDocs
)]
where
pakcsLibDocs = "https://www.informatik.uni-kiel.de/~pakcs/lib/"
kics2LibDocs = "https://www-ps.informatik.uni-kiel.de/kics2/lib/"
------------------------------------------------------------------------------
--- `test` command: run `curry check` on the modules provided as an argument
......@@ -1376,7 +1425,10 @@ execWithPkgDir :: ExecOptions -> Config -> IO (Repository,GlobalCache)
execWithPkgDir o cfg getRepoGC specDir =
loadCurryPathFromCache specDir |>=
maybe (computePackageLoadPath cfg specDir getRepoGC)
succeedIO |>= \currypath ->
succeedIO |>= execWithCurryPath o cfg
execWithCurryPath :: ExecOptions -> Config -> String -> IO (ErrorLogger ())
execWithCurryPath o _ currypath =
log Debug ("Setting CURRYPATH to " ++ currypath) |>
do setEnviron "CURRYPATH" currypath
ecode <- showExecCmd (exeCommand o)
......@@ -1396,22 +1448,6 @@ computePackageLoadPath cfg pkgdir getRepoGC =
in saveCurryPathToCache pkgdir currypath >> succeedIO currypath
-- Clean auxiliary files in the current package
cleanPackage :: LogLevel -> IO (ErrorLogger ())
cleanPackage ll =
getLocalPackageSpec "." |>= \specDir ->
loadPackageSpec specDir |>= \pkg ->
let dotcpm = specDir </> ".cpm"
srcdirs = map (specDir </>) (sourceDirsOf pkg)
testdirs = map (specDir </>)
(maybe []
(map (\ (PackageTest m _ _ _) -> m))
(testSuite pkg))
rmdirs = nub (dotcpm : map addCurrySubdir (srcdirs ++ testdirs))
in log ll ("Removing directories: " ++ unwords rmdirs) |>
(showExecCmd (unwords $ ["rm", "-rf"] ++ rmdirs) >> succeedIO ())
--- Creates a new package.
newPackage :: NewOptions -> IO (ErrorLogger ())
newPackage (NewOptions pname) = do
......@@ -1489,6 +1525,14 @@ saveCurryPathToCache pkgdir path = do
createDirectoryIfMissing False cpmdir
writeFile (curryPathCacheFile pkgdir) (path ++ "\n")
--- Gets CURRYPATH of the given package (either from the local cache file
--- in the package dir or compute it).
getCurryLoadPath :: Config -> String -> IO (Repository,GlobalCache)
-> IO (ErrorLogger String)
getCurryLoadPath cfg pkgdir getRepoGC =
loadCurryPathFromCache pkgdir |>=
maybe (computePackageLoadPath cfg pkgdir getRepoGC) succeedIO
--- Restores package CURRYPATH from local cache file in the given package dir,
--- if it is still up-to-date, i.e., it exists and is newer than the package
--- specification.
......
--------------------------------------------------------------------------------
--- This module contains some operations for processing packages,
--- like installing package sources, cleaning packages,
--- rendering package infos.
--------------------------------------------------------------------------------
module CPM.Package.Helpers
( installPackageSourceTo
, renderPackageInfo
, cleanPackage
, getLocalPackageSpec, searchLocalPackageSpec
) where
import Directory
import Distribution ( addCurrySubdir )
import FilePath
import List ( splitOn, nub )
import Pretty hiding ((</>))
import CPM.ErrorLogger
import CPM.FileUtil ( inDirectory, inTempDir, quote
, removeDirectoryComplete, tempDir, whenFileExists )
import CPM.Helpers ( strip )
import CPM.Package
------------------------------------------------------------------------------
--- Installs the source of the package from the given source location
--- into the subdirectory `packageId pkg` of the given directory.
installPackageSourceTo :: Package -> PackageSource -> String
-> IO (ErrorLogger ())
---
--- @param pkg - the package specification of the package
--- @param source - the source of the package
--- @param installdir - the directory where the package subdirectory should be
--- installed
installPackageSourceTo pkg (Git url rev) installdir = do
let pkgDir = installdir </> pkgid
c <- inDirectory installdir $ execQuietCmd cloneCommand
if c == 0
then case rev of
Nothing -> checkoutGitRef pkgDir "HEAD"
Just (Tag tag) -> checkoutGitRef pkgDir
(replaceVersionInTag pkg tag)
Just (Ref ref) -> checkoutGitRef pkgDir ref
Just VersionAsTag ->
let tag = "v" ++ (showVersion $ version pkg)
in checkoutGitRef pkgDir tag |>
log Info ("Package '" ++ packageId pkg ++ "' installed")
else removeDirectoryComplete pkgDir >>
failIO ("Failed to clone repository from '" ++ url ++
"', return code " ++ show c)
where
pkgid = packageId pkg
cloneCommand q = unwords ["git clone", q, quote url, quote $ pkgid]
installPackageSourceTo pkg (FileSource zipfile) installdir =
installPkgFromFile pkg zipfile (installdir </> packageId pkg) False
installPackageSourceTo pkg (Http url) installdir = do
let pkgDir = installdir </> packageId pkg
revurl = reverse url
pkgfile = if take 4 revurl == "piz." then "package.zip" else
if take 7 revurl == "zg.rat." then "package.tar.gz" else ""
if null pkgfile
then failIO $ "Illegal URL (only .zip or .tar.gz allowed):\n" ++ url
else do
tmpdir <- tempDir
let tmppkgfile = tmpdir </> pkgfile
c <- inTempDir $ showExecCmd $ "curl -s -o " ++ tmppkgfile ++
" " ++ quote url
if c == 0
then installPkgFromFile pkg tmppkgfile pkgDir True
else failIO $ "`curl` failed with exit status " ++ show c
--- Installs a package from a .zip or .tar.gz file into the specified
--- package directory. If the last argument is true, the file will be
--- deleted after unpacking.
installPkgFromFile :: Package -> String -> String -> Bool -> IO (ErrorLogger ())
installPkgFromFile pkg pkgfile pkgDir rmfile = do
let iszip = take 4 (reverse pkgfile) == "piz."
absfile <- getAbsolutePath pkgfile
createDirectory pkgDir
c <- if iszip
then inTempDir $ showExecCmd $ "unzip -qq -d " ++ quote pkgDir ++
" " ++ quote absfile
else inDirectory pkgDir $ showExecCmd $
"tar -xzf " ++ quote absfile
when rmfile (showExecCmd ("rm -f " ++ absfile) >> done)
if c == 0
then log Info $ "Installed " ++ packageId pkg
else do removeDirectoryComplete pkgDir
failIO ("Failed to unzip package, return code " ++ show c)
--- Checks out a specific ref of a Git repository and deletes
--- the Git auxiliary files (i.e., `.git` and `.gitignore`).
---
--- @param dir - the directory containing the repo
--- @param ref - the ref to check out
checkoutGitRef :: String -> String -> IO (ErrorLogger ())
checkoutGitRef dir ref = do
c <- inDirectory dir $ execQuietCmd (\q -> unwords ["git checkout", q, ref])
if c == 0
then removeGitFiles >> succeedIO ()
else removeDirectoryComplete dir >>
failIO ("Failed to check out " ++ ref ++ ", return code " ++ show c)
where
removeGitFiles = do
removeDirectoryComplete (dir </> ".git")
let gitignore = dir </> ".gitignore"
whenFileExists gitignore (removeFile gitignore)
------------------------------------------------------------------------------
--- Cleans auxiliary files in the local package, i.e., the package
--- containing the current working directory.
cleanPackage :: LogLevel -> IO (ErrorLogger ())
cleanPackage ll =
getLocalPackageSpec "." |>= \specDir ->
loadPackageSpec specDir |>= \pkg ->
let dotcpm = specDir </> ".cpm"
srcdirs = map (specDir </>) (sourceDirsOf pkg)
testdirs = map (specDir </>)
(maybe []
(map (\ (PackageTest m _ _ _) -> m))
(testSuite pkg))
rmdirs = nub (dotcpm : map addCurrySubdir (srcdirs ++ testdirs))
in log ll ("Removing directories: " ++ unwords rmdirs) |>
(showExecCmd (unwords $ ["rm", "-rf"] ++ rmdirs) >> succeedIO ())
------------------------------------------------------------------------------
--- Renders information about a package.
renderPackageInfo :: Bool -> Bool -> Maybe Bool -> Package -> String
renderPackageInfo allinfos plain mbinstalled pkg = pPrint doc
where
boldText s = (if plain then id else bold) $ text s
maxLen = 12
doc = vcat $ [ heading, rule
, maybe empty instTxt mbinstalled
, ver, auth, maintnr, synop
, cats, deps, compilers, descr, execspec ] ++
if allinfos
then [ srcdirs, expmods, cfgmod ] ++ testsuites ++
[ docuspec, src, licns, licfl, copyrt, homepg
, reposy, bugrep ]
else []
pkgId = packageId pkg
heading = text pkgId
instTxt i = if i || plain then empty
else red $ text "Not installed"
rule = text (take (length pkgId) $ repeat '-')
ver = fill maxLen (boldText "Version") <+>
(text $ showVersion $ version pkg)
auth = fill maxLen (boldText "Author") <+>
indent 0 (fillSep (map (text . strip) (splitOn "," $ author pkg)))
synop = fill maxLen (boldText "Synopsis") <+>
indent 0 (fillSep (map text (words (synopsis pkg))))
deps = boldText "Dependencies" <$$>
(vcat $ map (indent 4 . text . showDependency) $ dependencies pkg)
maintnr = case maintainer pkg of
Nothing -> empty
Just s -> fill maxLen (boldText "Maintainer") <+>
indent 0 (fillSep (map (text . strip) (splitOn "," s)))
cats =
if null (category pkg)
then empty
else fill maxLen (boldText "Category") <+>
indent 0 (fillSep (map text (category pkg)))
execspec = case executableSpec pkg of
Nothing -> empty
Just (PackageExecutable n m eopts) ->
if allinfos
then boldText "Executable" <$$>
indent 4 (boldText "Name " <+> text n) <$$>
indent 4 (boldText "Main module " <+> text m) <$$>
if null eopts
then empty
else indent 4 (boldText "Options ") <+>
align (vsep (map (\ (c,o) -> text $ c ++ ": " ++ o) eopts))
else fill maxLen (boldText "Executable") <+> text n
testsuites = case testSuite pkg of
Nothing -> []
Just tests ->
map (\ (PackageTest dir mods opts script) ->
let check = if null script then "Check" else "Test" in
boldText "Test suite" <$$>
indent 4 (boldText "Directory " <+> text dir) <$$>
(if null script
then empty
else indent 4 (boldText "Test script " <+> text script)) <$$>
(if null opts
then empty
else indent 4 (boldText (check++" options") <+>
text opts)) <$$>
(if null mods
then empty
else indent 4 (boldText "Test modules " <+>
align (fillSep (map text mods)))))
tests
docuspec = case documentation pkg of
Nothing -> empty
Just (PackageDocumentation docdir docmain doccmd) ->
boldText "Documentation" <$$>
indent 4 (boldText "Directory " <+> text docdir) <$$>
indent 4 (boldText "Main file " <+> text docmain) <$$>
if null doccmd
then empty
else indent 4 (boldText "Command ") <+> text doccmd
descr = showParaField description "Description"
licns = showLineField license "License"
licfl = showLineField licenseFile "License file"
copyrt = showParaField copyright "Copyright"
homepg = showLineField homepage "Homepage"
reposy = showLineField repository "Repository"
bugrep = showLineField bugReports "Bug reports"
cfgmod = showLineField configModule "Config module"
src = maybe empty
(\_ -> boldText "Source" <$$>
indent 4 (text $ showPackageSource pkg))
(source pkg)
srcdirs =
if null (sourceDirs pkg)
then empty
else boldText "Source directories" <$$>
indent 4 (fillSep (map text (sourceDirs pkg)))
expmods =
if null (exportedModules pkg)
then empty
else boldText "Exported modules" <$$>
indent 4 (fillSep (map text (exportedModules pkg)))
compilers =
if null (compilerCompatibility pkg)
then empty
else boldText "Compiler compatibility" <$$>
(vcat $ map (indent 4 . text . showCompilerDependency)
$ compilerCompatibility pkg)
showLineField fgetter fname = case fgetter pkg of
Nothing -> empty
Just s -> boldText fname <$$> indent 4 (text s)
showParaField fgetter fname = case fgetter pkg of
Nothing -> empty
Just s -> boldText fname <$$>
indent 4 (fillSep (map text (words s)))
------------------------------------------------------------------------------
--- Tries to find a package specification in the current directory or one of its
--- ancestors.
getLocalPackageSpec :: String -> IO (ErrorLogger String)
getLocalPackageSpec dir =
searchLocalPackageSpec dir |>=
maybe (failIO "No package.json found") succeedIO
--- Tries to find a package specification in the current directory or one of its
--- ancestors. Returns `Nothing` if there is not package specifiction.
searchLocalPackageSpec :: String -> IO (ErrorLogger (Maybe String))
searchLocalPackageSpec dir = do
existsLocal <- doesFileExist $ dir </> "package.json"
if existsLocal
then succeedIO (Just dir)
else log Debug ("No package.json in " ++ show dir ++ ", trying " ++
show (dir </> "..")) |> do
parentExists <- doesDirectoryExist $ dir </> ".."
if parentExists
then searchLocalPackageSpec $ dir </> ".."
else succeedIO Nothing
------------------------------------------------------------------------------
......@@ -33,12 +33,14 @@ import FilePath
import CPM.Config ( Config, packageInstallDir )
import CPM.ErrorLogger
import CPM.FileUtil ( copyDirectory, inTempDir, recreateDirectory, inDirectory
, removeDirectoryComplete, tempDir
, removeDirectoryComplete, tempDir, whenFileExists
, checkAndGetDirectoryContents, quote )
import CPM.Package
import CPM.Package.Helpers ( installPackageSourceTo )
import CPM.Repository
--- The global package cache.
------------------------------------------------------------------------------
--- The data type representing the global package cache.
data GlobalCache = GlobalCache [Package]
--- An empty package cache.
......@@ -49,6 +51,7 @@ emptyCache = GlobalCache []
allPackages :: GlobalCache -> [Package]
allPackages (GlobalCache ps) = ps
------------------------------------------------------------------------------
--- Finds all versions of a package in the global package cache.
---
--- @param gc - the global package cache
......@@ -75,11 +78,12 @@ findNewestVersion db p = if length pkgs > 0
--- Finds a specific version of a package.
findVersion :: GlobalCache -> String -> Version -> Maybe Package
findVersion (GlobalCache ps) p v = if length hits == 0
findVersion (GlobalCache ps) p v =
if null hits
then Nothing
else Just $ head hits
where
hits = filter ((== v) . version) $ filter ((== p) . name) ps
where
hits = filter ((== v) . version) $ filter ((== p) . name) ps
--- Checks whether a package is installed.
isPackageInstalled :: GlobalCache -> Package -> Bool
......@@ -106,77 +110,25 @@ copyPackage cfg pkg dir = do
--- Acquires a package from the source specified in its specification and
--- installs it to the global package cache.
acquireAndInstallPackage :: Config -> Package -> IO (ErrorLogger ())
acquireAndInstallPackage cfg pkg = case (source pkg) of
Nothing -> failIO $ "No source specified for " ++ packageId pkg
Just s -> log Info ("Installing package from " ++ showPackageSource pkg) |>
installFromSource cfg pkg s