Commit 5473376c authored by Michael Hanus 's avatar Michael Hanus

CPM updated

parent 2228d8e1
......@@ -347,23 +347,34 @@ This command will set the \code{CURRYPATH} environment variable
and then execute the command given after \ccode{exec}.
\subsection{Using Packages Without a Package}
\subsection{Using Packages Outside a Package}
\label{sec:meta-package}
In principle, packages can be used only inside another package
by declaring dependencies.
Thus, if you develop a Curry application which is not a package
but you want to use some package, you have to put
a package specification file into the source directory of
your application where you define the required packages.
As a shortcut for this, you can use the CPM command
\code{cypm add --dependency} (short: \code{cypm add -d})
to install the package specification file.
by declaring dependencies in the package specification file
\code{package.json}.
If you invoke \code{cypm} in a directory which contains
no package specification file, CPM searches for such a file
from the current directory to the parent directories (up to the
root of the file system).
Thus, if you are outside a package, such a file is not available.
In order to support the use other packages outside package,
CPM provides a meta-package which is usually stored in your home directory
at \code{\char126/.cpm/$Curry system$-homepackage}.\footnote{%
Use \code{cypm config} and look at \code{HOME_PACKAGE_PATH}
to see the current location of this meta-package.}
This meta-package is used when your are not inside another package.
Hence, if you write some Curry program which is not a package
but you want to use some package \code{P}, you have to add a dependency
to \code{P} to this meta-package.
CPM does this automatically for you with the CPM command
\code{cypm add --dependency} (short: \code{cypm add -d}).
For instance, to use the libraries of the JSON package
in your application, one can use the following commands:
%
\begin{lstlisting}
> cypm add -d json # generate package specification with 'json' dependency
> cypm add -d json # add 'json' dependency to meta-package
> cypm install # download and install all dependencies
> cypm curry # start Curry system with JSON libraries in load path
...
......@@ -391,8 +402,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
......@@ -632,6 +643,11 @@ The path to the package cache where packages are checked out if only
their binaries are installed (see Section~\ref{sec:installapp}).
Default value: \code{\$HOME/.cpm/app_packages}.
\item[\fbox{\code{HOME_PACKAGE_PATH}}]
The path to the meta-package which is used if you are outside another
package (see Section~\ref{sec:meta-package}).
Default value: \code{\$HOME/.cpm/$Curry system$-homepackage}.
\item[\fbox{\code{CURRY_BIN}}]
The name of the executable of the Curry system used
to compile and test packages.
......@@ -962,11 +978,11 @@ The option \ccode{--force} allows to overwrite existing copies
in the central package index.
\item[\fbox{\code{add --dependency $package$ [--force]}}]
Adds the package $package$ as a new dependency of the current package.
This command either modifies the package description file (\code{package.json})
of the current package and adds a dependency to the given package
in this file or, if there is no package description file,
generates a new one with this dependency.
Adds the package $package$ as a new dependency.
This command adds a dependency to the given package
either in the package description file (\code{package.json})
of the current package or in the meta-package
(see Section~\ref{sec:meta-package}).
The option \ccode{--force} allows to overwrite existing dependencies
in the package description file.
......
......@@ -6,7 +6,7 @@
module CPM.Config
( Config ( Config, packageInstallDir, binInstallDir, repositoryDir
, appPackageDir, packageIndexRepository, curryExec
, appPackageDir, packageIndexRepository, homePackageDir, curryExec
, compilerVersion, compilerBaseVersion, baseVersion )
, readConfigurationWith, defaultConfig
, showConfiguration, showCompilerVersion ) where
......@@ -45,6 +45,8 @@ data Config = Config {
, appPackageDir :: String
--- URL to the package index repository
, packageIndexRepository :: String
--- The directory where the default home package is stored
, homePackageDir :: String
--- The executable of the Curry system used to compile and check packages
, curryExec :: String
--- The compiler version (name,major,minor) used to compile packages
......@@ -64,6 +66,7 @@ defaultConfig = Config
, repositoryDir = "$HOME/.cpm/index"
, appPackageDir = "$HOME/.cpm/app_packages"
, packageIndexRepository = packageIndexURI
, homePackageDir = ""
, curryExec = Dist.installDir </> "bin" </> Dist.curryCompiler
, compilerVersion = ( Dist.curryCompiler
, Dist.curryCompilerMajorVersion
......@@ -83,6 +86,7 @@ showConfiguration cfg = unlines
, "PACKAGE_INSTALL_PATH : " ++ packageInstallDir cfg
, "BIN_INSTALL_PATH : " ++ binInstallDir cfg
, "APP_PACKAGE_PATH : " ++ appPackageDir cfg
, "HOME_PACKAGE_PATH : " ++ homePackageDir cfg
]
--- Shows the compiler version in the configuration.
......@@ -107,6 +111,19 @@ setCompilerExecutable cfg = do
maybe (error $ "Executable '" ++ exec ++ "' not found in path!")
(\absexec -> return cfg { curryExec = absexec })
--- Sets the `homePackageDir` depending on the compiler version.
setHomePackageDir :: Config -> IO Config
setHomePackageDir cfg
| null (homePackageDir cfg)
= do homedir <- getHomeDirectory
if null homedir
then return cfg
else let (cname,cmaj,cmin) = compilerVersion cfg
cvname = cname ++ "-" ++ show cmaj ++ "." ++ show cmin
homepkgdir = homedir </> ".cpm" </> cvname ++ "-homepackage"
in return cfg { homePackageDir = homepkgdir }
| otherwise = return cfg
--- Sets the correct compiler version in the configuration.
setCompilerVersion :: Config -> IO Config
setCompilerVersion cfg0 = do
......@@ -171,7 +188,8 @@ readConfigurationWith defsettings = do
Right s0 -> do s1 <- replaceHome s0
createDirectories s1
s2 <- setCompilerVersion s1
return $ Right s2
s3 <- setHomePackageDir s2
return $ Right s3
replaceHome :: Config -> IO Config
replaceHome cfg = do
......@@ -222,6 +240,7 @@ keySetters =
, ("PACKAGEINSTALLPATH" , \v c -> c { packageInstallDir = v })
, ("BININSTALLPATH" , \v c -> c { binInstallDir = v })
, ("APPPACKAGEPATH" , \v c -> c { appPackageDir = v })
, ("HOMEPACKAGEPATH" , \v c -> c { homePackageDir = v })
, ("CURRYBIN" , \v c -> c { curryExec = v })
, ("BASEVERSION" , \v c -> c { baseVersion = v })
]
......
......@@ -140,7 +140,7 @@ showLogEntry :: LogEntry -> IO ()
showLogEntry (LogEntry lvl msg) = do
minLevel <- getLogLevel
if levelGte lvl minLevel
then putStrLn $ pPrint $ lvlText <+> (text msg)
then putStrLn $ pPrint lvlText ++ msg
else return ()
where
lvlText = case lvl of
......
......@@ -53,7 +53,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 12/01/2018)"
bannerLine = take (length bannerText) (repeat '-')
main :: IO ()
......@@ -96,7 +96,7 @@ runWithArgs opts = do
PkgInfo o -> infoCmd o config
Link o -> linkCmd o config
Add o -> addCmd o config
Clean -> cleanPackage Info
Clean -> cleanPackage config Info
New o -> newPackage o
cmd -> do repo <- readRepository config (cmdWithLargeRepoCache cmd)
case optCommand opts of
......@@ -733,7 +733,7 @@ updateCmd cfg =
-- `deps` command:
depsCmd :: DepsOptions -> Config -> IO (ErrorLogger ())
depsCmd opts cfg =
getLocalPackageSpec "." |>= \specDir ->
getLocalPackageSpec cfg "." |>= \specDir ->
loadPackageSpec specDir |>= \pkg ->
checkCompiler cfg pkg >>
if depsPath opts -- show CURRYPATH only?
......@@ -750,7 +750,7 @@ infoCmd :: InfoOptions -> Config -> IO (ErrorLogger ())
infoCmd (InfoOptions Nothing (Just _) _ _) _ =
failIO "Must specify package name"
infoCmd (InfoOptions Nothing Nothing allinfos plain) cfg =
getLocalPackageSpec "." |>= \specDir ->
getLocalPackageSpec cfg "." |>= \specDir ->
loadPackageSpec specDir |>= \p ->
printInfo cfg allinfos plain p
infoCmd (InfoOptions (Just pkgname) Nothing allinfos plain) cfg =
......@@ -795,7 +795,7 @@ checkoutCmd (CheckoutOptions pkg (Just ver) _) cfg repo =
installCmd :: InstallOptions -> Config -> Repository -> IO (ErrorLogger ())
installCmd (InstallOptions Nothing Nothing _ instexec False) cfg repo =
getLocalPackageSpec "." |>= \pkgdir ->
getLocalPackageSpec cfg "." |>= \pkgdir ->
cleanCurryPathCache pkgdir |>
installLocalDependencies cfg repo pkgdir |>= \ (pkg,_) ->
saveBaseVersionToCache cfg pkgdir >>
......@@ -803,7 +803,7 @@ installCmd (InstallOptions Nothing Nothing _ instexec False) cfg repo =
if instexec then installExecutable cfg pkg else succeedIO ()
-- Install executable only:
installCmd (InstallOptions Nothing Nothing _ _ True) cfg _ =
getLocalPackageSpec "." |>= \pkgdir ->
getLocalPackageSpec cfg "." |>= \pkgdir ->
loadPackageSpec pkgdir |>= \pkg ->
installExecutable cfg pkg
installCmd (InstallOptions (Just pkg) vers pre _ _) cfg repo = do
......@@ -905,7 +905,7 @@ uninstall (UninstallOptions (Just pkgname) Nothing) cfg = do
uninstall (UninstallOptions Nothing (Just _)) _ =
log Error "Please provide a package and version number!"
uninstall (UninstallOptions Nothing Nothing) cfg =
getLocalPackageSpec "." |>= \pkgdir ->
getLocalPackageSpec cfg "." |>= \pkgdir ->
loadPackageSpec pkgdir |>= uninstallPackageExecutable cfg
uninstallPackageExecutable :: Config -> Package -> IO (ErrorLogger ())
......@@ -1013,20 +1013,20 @@ searchCmd (SearchOptions q smod sexec) cfg repo =
--- `upgrade` command.
upgradeCmd :: UpgradeOptions -> Config -> Repository -> IO (ErrorLogger ())
upgradeCmd (UpgradeOptions Nothing) cfg repo =
getLocalPackageSpec "." |>= \specDir ->
getLocalPackageSpec cfg "." |>= \specDir ->
cleanCurryPathCache specDir |>
log Info "Upgrading all packages" |>
upgradeAllPackages cfg repo specDir
upgradeCmd (UpgradeOptions (Just pkg)) cfg repo =
getLocalPackageSpec "." |>= \specDir ->
getLocalPackageSpec cfg "." |>= \specDir ->
log Info ("Upgrade " ++ pkg) |>
upgradeSinglePackage cfg repo specDir pkg
--- `link` command.
linkCmd :: LinkOptions -> Config -> IO (ErrorLogger ())
linkCmd (LinkOptions src) _ =
getLocalPackageSpec "." |>= \specDir ->
linkCmd (LinkOptions src) cfg =
getLocalPackageSpec cfg "." |>= \specDir ->
cleanCurryPathCache specDir |>
log Info ("Linking '" ++ src ++ "' into local package cache...") |>
linkToLocalCache src specDir
......@@ -1087,9 +1087,8 @@ addDependencyCmd pkgname force config =
[] -> packageNotFoundFailure pkgname
ps -> case filter (isCompatibleToCompiler config) ps of
[] -> compatPackageNotFoundFailure config pkgname useUpdateHelp
(p:_) -> searchLocalPackageSpec "." |>=
maybe (genNewLocalPackage (version p))
(addDepToLocalPackage (version p))
(p:_) -> getLocalPackageSpec config "." |>=
addDepToLocalPackage (version p)
where
addDepToLocalPackage vers pkgdir =
loadPackageSpec pkgdir |>= \pkgSpec ->
......@@ -1100,21 +1099,10 @@ addDependencyCmd pkgname force config =
then writePackageSpec newpkg (pkgdir </> "package.json") |>>
log Info ("Dependency '" ++ pkgname ++ " >= " ++
showVersion vers ++
"' added to current package specification")
"' added to package '" ++ pkgdir ++ "'")
else log Critical ("Dependency '" ++ pkgname ++
"' already exists!\n" ++ useForce)
genNewLocalPackage vers =
let newpkg = emptyPackage { name = "PSEUDO_PACKAGE"
, version = initialVersion
, author = "NN"
, synopsis = "UNKNOWN"
, dependencies = addDep [[VGte vers]] []
}
in writePackageSpec newpkg "package.json" |>>
log Info ("New package specification 'package.json' with dependency '" ++
pkgname ++ " >= " ++ showVersion vers ++ "' generated")
addDep vcs [] = [Dependency pkgname vcs]
addDep vcs (Dependency pn pvcs : deps) =
if pn == pkgname then Dependency pn vcs : deps
......@@ -1127,7 +1115,7 @@ addDependencyCmd pkgname force config =
--- or on all source modules of the package.
docCmd :: DocOptions -> Config -> IO (ErrorLogger ())
docCmd opts cfg =
getLocalPackageSpec "." |>= \specDir ->
getLocalPackageSpec cfg "." |>= \specDir ->
loadPackageSpec specDir |>= \pkg -> do
let docdir = maybe "cdoc" id (docDir opts) </> packageId pkg
absdocdir <- getAbsolutePath docdir
......@@ -1256,7 +1244,7 @@ baseDocURL = "http://www.informatik.uni-kiel.de/~mh/curry/cpm/DOC"
--- or all source modules of the package.
testCmd :: TestOptions -> Config -> IO (ErrorLogger ())
testCmd opts cfg =
getLocalPackageSpec "." |>= \specDir ->
getLocalPackageSpec cfg "." |>= \specDir ->
loadPackageSpec specDir |>= \pkg -> do
checkCompiler cfg pkg
aspecDir <- getAbsolutePath specDir
......@@ -1318,7 +1306,7 @@ curryModulesInDir dir = getModules "" dir
diffCmd :: DiffOptions -> Config -> Repository -> IO (ErrorLogger ())
diffCmd opts cfg repo =
readGlobalCache cfg repo |>= \gc ->
getLocalPackageSpec "." |>= \specDir ->
getLocalPackageSpec cfg "." |>= \specDir ->
loadPackageSpec specDir |>= \localSpec ->
checkCompiler cfg localSpec >>
let localname = name localSpec
......@@ -1366,7 +1354,7 @@ diffCmd opts cfg repo =
-- Implementation of the "curry" command.
compiler :: ExecOptions -> Config -> IO (ErrorLogger ())
compiler o cfg =
getLocalPackageSpec "." |>= \pkgdir ->
getLocalPackageSpec cfg "." |>= \pkgdir ->
loadPackageSpec pkgdir |>= \pkg ->
checkCompiler cfg pkg >>
execWithPkgDir
......@@ -1375,7 +1363,7 @@ compiler o cfg =
execCmd :: ExecOptions -> Config -> IO (ErrorLogger ())
execCmd o cfg =
getLocalPackageSpec "." |>= execWithPkgDir o cfg
getLocalPackageSpec cfg "." |>= execWithPkgDir o cfg
execWithPkgDir :: ExecOptions -> Config -> String -> IO (ErrorLogger ())
execWithPkgDir o cfg specDir =
......@@ -1410,9 +1398,9 @@ computePackageLoadPath cfg pkgdir =
showVersion (version pkg) /= compilerBaseVersion cfg
-- Clean auxiliary files in the current package
cleanPackage :: LogLevel -> IO (ErrorLogger ())
cleanPackage ll =
getLocalPackageSpec "." |>= \specDir ->
cleanPackage :: Config -> LogLevel -> IO (ErrorLogger ())
cleanPackage cfg ll =
getLocalPackageSpec cfg "." |>= \specDir ->
loadPackageSpec specDir |>= \pkg ->
let dotcpm = specDir </> ".cpm"
srcdirs = map (specDir </>) (sourceDirsOf pkg)
......
......@@ -9,7 +9,7 @@ module CPM.PackageCopy
, resolveDependencies
, upgradeAllPackages
, upgradeSinglePackage
, getLocalPackageSpec, searchLocalPackageSpec
, getLocalPackageSpec
, linkToLocalCache
, acquireAndInstallPackageWithDependencies
, installLocalDependencies
......@@ -21,7 +21,8 @@ import Directory ( doesFileExist, getAbsolutePath, createDirectoryIfMissing
, doesDirectoryExist, getTemporaryDirectory
, getCurrentDirectory, setCurrentDirectory, createDirectory
, removeDirectory, getDirectoryContents, copyFile )
import FilePath ((</>), takeExtension, takeBaseName, joinPath, takeDirectory )
import FilePath ( (</>), takeExtension, takeBaseName, joinPath, splitPath
, splitFileName, takeDirectory )
import AbstractCurry.Types (CurryProg)
import List ( intercalate, splitOn )
import Maybe ( mapMaybe, fromJust )
......@@ -30,8 +31,8 @@ import System ( system )
import Text.Pretty hiding ( (</>) )
import CPM.AbstractCurry
import CPM.Config (Config, packageInstallDir, baseVersion)
import CPM.Repository (Repository, allPackages, readRepository)
import CPM.Config ( Config, packageInstallDir, baseVersion, homePackageDir )
import CPM.Repository ( Repository, allPackages, readRepository )
import qualified CPM.LookupSet as LS
import CPM.ErrorLogger
import CPM.FileUtil ( copyDirectory, recreateDirectory )
......@@ -39,14 +40,7 @@ import CPM.Helpers ( strip )
import qualified CPM.PackageCache.Global as GC
import qualified CPM.PackageCache.Runtime as RuntimeCache
import qualified CPM.PackageCache.Local as LocalCache
import CPM.Package ( Package (..)
, readPackageSpec, packageId, readVersion, Version
, showVersion, PackageSource (..), showDependency
, showCompilerDependency, showPackageSource
, VersionConstraint (..)
, Dependency (..), GitRevision (..), PackageExecutable (..)
, PackageTest (..), PackageDocumentation (..)
, packageIdEq, loadPackageSpec)
import CPM.Package
import CPM.Resolution
--- Resolves dependencies for a package copy.
......@@ -168,30 +162,48 @@ linkToLocalCache src pkgDir = do
else log Critical ("Directory '" ++ src ++ "' does not exist.") |>
succeedIO ()
--- 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.
--- Tries to find a package specification in the given directory or one of its
--- ancestors. If there is no package specifiction in these directories,
--- the home package specification (i.e., `~/.cpm/home-package/package.json`
--- is returned (and created if it does not exist).
--- In order to avoid infinite loops due to cyclic file structures,
--- the search is limited to 10 ancestor hierarchies.
searchLocalPackageSpec :: String -> IO (ErrorLogger (Maybe String))
searchLocalPackageSpec = searchLocalSpec 10
--- the search is limited to the number of directories occurring in the
--- current absolute path.
getLocalPackageSpec :: Config -> String -> IO (ErrorLogger String)
getLocalPackageSpec cfg dir = do
adir <- getAbsolutePath dir
searchLocalSpec (length (splitPath adir)) dir
>>= maybe returnHomePackage succeedIO
where
returnHomePackage = do
let homepkgdir = homePackageDir cfg
homepkgspec = homepkgdir </> "package.json"
specexists <- doesFileExist homepkgspec
unless (specexists || null homepkgdir) $ do
createDirectoryIfMissing True homepkgdir
let newpkg = emptyPackage
{ name = snd (splitFileName homepkgdir)
, version = initialVersion
, author = "CPM"
, synopsis = "Default home package"
, dependencies = []
}
writePackageSpec newpkg homepkgspec
infoMessage $ "New empty package specification '" ++ homepkgspec ++
"' generated"
succeedIO homepkgdir
searchLocalSpec m 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 m>0 && parentExists
then searchLocalSpec (m-1) $ dir </> ".."
else succeedIO Nothing
then return (Just dir)
else do
debugMessage ("No package.json in " ++ show dir ++ ", trying " ++
show (dir </> ".."))
parentExists <- doesDirectoryExist $ dir </> ".."
if m>0 && parentExists
then searchLocalSpec (m-1) $ dir </> ".."
else return Nothing
--- Resolves the dependencies for a package copy and fills the package caches.
resolveAndCopyDependencies :: Config -> Repository -> GC.GlobalCache -> String
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment