0 | module Pack.Config.Environment
  1 |
  2 | import Data.Maybe
  3 | import Data.SortedMap as SM
  4 | import Data.String
  5 | import Idris.Package.Types
  6 | import Pack.CmdLn
  7 | import Pack.Config.TOML
  8 | import Pack.Config.Types
  9 | import public Pack.Config.Environment.Variable
 10 | import Pack.Core
 11 | import Pack.Database
 12 | import System
 13 | import System.Clock
 14 |
 15 | %default total
 16 |
 17 | --------------------------------------------------------------------------------
 18 | --          Files and Directories
 19 | --------------------------------------------------------------------------------
 20 |
 21 | ||| File body of all `pack.toml` files.
 22 | export
 23 | packToml : Body
 24 | packToml = "pack.toml"
 25 |
 26 | ||| Directory where databases are stored.
 27 | export %inline
 28 | dbDir : (pd : PackDirs) => Path Abs
 29 | dbDir = pd.state /> "db"
 30 |
 31 | ||| Directory where cached data is stored
 32 | export %inline
 33 | cacheDir : (pd : PackDirs) => Path Abs
 34 | cacheDir = pd.cache
 35 |
 36 | ||| Path to cached `.ipkg` file.
 37 | export
 38 | ipkgCachePath : PackDirs => PkgName -> Commit -> File Rel -> File Abs
 39 | ipkgCachePath p com = toAbsFile (cacheDir <//> p <//> com)
 40 |
 41 | ||| Path where the cached compiler version is stored.
 42 | export
 43 | versionCachePath : PackDirs => (db : DB) => File Abs
 44 | versionCachePath = MkF (cacheDir <//> IdrisApi <//> db.idrisCommit) "version"
 45 |
 46 | ||| Path to cached core library `.ipkg` file
 47 | export
 48 | coreCachePath : PackDirs => (db : DB) => CorePkg -> File Abs
 49 | coreCachePath n =
 50 |   MkF (cacheDir <//> n <//> db.idrisCommit) (coreIpkgFile n)
 51 |
 52 | ||| Directory where user settings are stored.
 53 | export %inline
 54 | userDir : (pd : PackDirs) => Path Abs
 55 | userDir = pd.user
 56 |
 57 | ||| Path to global `pack.toml` file.
 58 | export %inline
 59 | globalPackToml : PackDirs => File Abs
 60 | globalPackToml = MkF userDir packToml
 61 |
 62 | ||| Path to global `pack.toml` file containing the global pack collection.
 63 | export %inline
 64 | collectionToml : (pd : PackDirs) => File Abs
 65 | collectionToml = MkF pd.state packToml
 66 |
 67 | ||| File where package DB is located
 68 | export
 69 | dbFile : PackDirs => (c : MetaConfig) => File Abs
 70 | dbFile = MkF dbDir $ c.collection.value <+> ".toml"
 71 |
 72 | ||| Directory where wrapper scripts to binaries
 73 | ||| managed by pack are being stored. The only exception
 74 | ||| is pack itself, which is stored as a symbolic link.
 75 | export %inline
 76 | packBinDir : (pd : PackDirs) => Path Abs
 77 | packBinDir = pd.bin
 78 |
 79 | ||| Where packages and built applications will be installed
 80 | export %inline
 81 | installDir : (pd : PackDirs) => Path Abs
 82 | installDir = pd.state </> "install"
 83 |
 84 | ||| Where all pack versions are installed
 85 | export %inline
 86 | packParentDir : PackDirs => Path Abs
 87 | packParentDir = installDir </> "pack"
 88 |
 89 | ||| Where the actual pack application is installed.
 90 | export %inline
 91 | packInstallDir : PackDirs => Commit -> Path Abs
 92 | packInstallDir com = packParentDir </> cast com
 93 |
 94 | ||| Executable for an application
 95 | export %inline
 96 | pathExec : PackDirs => Body -> File Abs
 97 | pathExec b = MkF packBinDir b
 98 |
 99 | ||| Symbolic link to the current pack executable.
100 | export
101 | packExec : PackDirs => File Abs
102 | packExec = pathExec "pack"
103 |
104 | ||| Directory where `.ipkg` patches are stored.
105 | export
106 | patchesDir : PackDirs => Path Abs
107 | patchesDir = userDir /> "patches"
108 |
109 | export
110 | commitsDir : PackDirs => Path Abs
111 | commitsDir = cacheDir </> "commits"
112 |
113 | ||| Path to file storing the last fetched commit of a Git
114 | ||| repo given as a URL and branch name.
115 | export
116 | commitFile : PackDirs => URL -> Branch -> File Abs
117 | commitFile url b =
118 |   let relPath := the (Path Rel) $ cast "\{url}/\{b}"
119 |    in MkF (commitsDir </> relPath) "commit"
120 |
121 | ||| File where the patch (if any) for an `.ipkg` file is stored.
122 | export
123 | patchFile : PackDirs => (c : Config) => PkgName -> File Rel -> File Abs
124 | patchFile n (MkF p b) =
125 |   MkF
126 |     (patchesDir //> c.collection <//> n </> p)
127 |     (b <+> ".patch")
128 |
129 | ||| Directory where all packages (and Idris2) built with the
130 | ||| current Idris2 commit will be installed.
131 | export %inline
132 | commitDir : PackDirs => (db : DB) => Path Abs
133 | commitDir = installDir <//> db.idrisCommit
134 |
135 | ||| The directory where Idris2 and core libraries will be installed.
136 | export %inline
137 | idrisPrefixDir : PackDirs => DB => Path Abs
138 | idrisPrefixDir = commitDir /> "idris2"
139 |
140 | ||| The directory where the Idris2 binary will be installed.
141 | export %inline
142 | idrisBinDir : PackDirs => DB => Path Abs
143 | idrisBinDir = idrisPrefixDir /> "bin"
144 |
145 | ||| Location of the Idris2 executable used to build packages.
146 | |||
147 | ||| Notice that if you need an Idris command, you may need `idrisCmd` function
148 | ||| instead because it takes extra arguments into account.
149 | export
150 | idrisExec : PackDirs => DB => File Abs
151 | idrisExec = MkF idrisBinDir "idris2"
152 |
153 | export %inline
154 | idrisDir : (db : DB) => Body
155 | idrisDir = the Body "idris2" <-> db.idrisVersion
156 |
157 | ||| The directory where Idris2 packages will be installed.
158 | export %inline
159 | idrisInstallDir : PackDirs => DB => Path Abs
160 | idrisInstallDir = idrisPrefixDir /> idrisDir
161 |
162 | ||| The `lib` directory in the Idris2 installation folder
163 | export %inline
164 | idrisLibDir : PackDirs => DB => Path Abs
165 | idrisLibDir = idrisInstallDir /> "lib"
166 |
167 | ||| The `support` directory in the Idris2 installation folder
168 | export %inline
169 | idrisDataDir : PackDirs => DB => Path Abs
170 | idrisDataDir = idrisInstallDir /> "support"
171 |
172 | ||| Directory where an installed library or app goes
173 | export %inline
174 | pkgPrefixDir : PackDirs => DB => PkgName -> Hash -> Package -> Path Abs
175 | pkgPrefixDir n h (Git {})   = commitDir <//> n <//> h
176 | pkgPrefixDir n h (Local {}) = commitDir <//> n <//> h
177 | pkgPrefixDir n h (Core _)   = idrisPrefixDir
178 |
179 | ||| Directory to be used with the `IDRIS2_PACKAGE_PATH` variable, so that
180 | ||| Idris finds a library even though it is being installed in a
181 | ||| custom location.
182 | export %inline
183 | pkgPathDir : PackDirs => DB => PkgName -> Hash -> Package -> Path Abs
184 | pkgPathDir n h p = pkgPrefixDir n h p /> idrisDir
185 |
186 | ||| Directory where the binary of an Idris application is installed.
187 | export %inline
188 | pkgBinDir : PackDirs => DB => PkgName -> Hash -> Package -> Path Abs
189 | pkgBinDir n h p = pkgPrefixDir n h p /> "bin"
190 |
191 | ||| Directory to be used with the `IDRIS2_LIBS` variable, so that
192 | ||| Idris finds a libraries `.so` files even though they have been
193 | ||| installed in a custom location.
194 | export %inline
195 | pkgLibDir : PackDirs => DB => PkgName -> Hash -> Package -> Path Abs
196 | pkgLibDir n h p = pkgPathDir n h p /> "lib"
197 |
198 | ||| Directory to be used with the `IDRIS2_DATA` variable, so that
199 | ||| Idris finds a libraries support files even though they have been
200 | ||| installed in a custom location.
201 | export %inline
202 | pkgDataDir : PackDirs => DB => PkgName -> Hash -> Package -> Path Abs
203 | pkgDataDir n h p = pkgPathDir n h p /> "support"
204 |
205 | ||| Timestamp used to monitor if a local library has been
206 | ||| modified and requires reinstalling.
207 | export %inline
208 | libTimestamp :  PackDirs => DB => PkgName -> File Abs
209 | libTimestamp n = MkF (cacheDir </> "local" <//> n) ".timestamp"
210 |
211 | ||| Directory where the sources of a local package are
212 | ||| stored.
213 | export %inline
214 | localSrcDir : Desc t -> Path Abs
215 | localSrcDir d = sourcePath d
216 |
217 | pkgRelDir : Desc t -> Path Rel
218 | pkgRelDir d = case Body.parse d.desc.name of
219 |   Just b  => neutral /> (b <-> d.desc.version)
220 |   Nothing => cast d.desc.name //> d.desc.version
221 |
222 | ||| Returns the directory where a package for the current
223 | ||| package collection is installed.
224 | export
225 | pkgInstallDir : PackDirs => (db : DB) => PkgName -> Hash -> Package -> Desc t -> Path Abs
226 | pkgInstallDir n h p d =
227 |   let vers := db.idrisVersion
228 |       dir  := pkgPrefixDir n h p /> idrisDir
229 |    in case p of
230 |         Core c          => dir /> (c <-> vers)
231 |         Git _ _ _ _ _ _ => dir </> pkgRelDir d
232 |         Local _ _ _ _   => dir </> pkgRelDir d
233 |
234 | ||| Directory where the API docs of the package will be installed.
235 | export %inline
236 | pkgDocs : PackDirs => DB => PkgName -> Hash -> Package -> Desc t -> Path Abs
237 | pkgDocs n h p d = pkgInstallDir n h p d /> "docs"
238 |
239 | ||| Location of an executable of the given name.
240 | export
241 | pkgExec : PackDirs => DB => PkgName -> Hash -> Package -> (exe : Body) -> File Abs
242 | pkgExec n h p exe = MkF (pkgBinDir n h p) exe
243 |
244 | ||| Path to the executable of an Idris application
245 | export
246 | resolvedExec : PackDirs => DB => ResolvedApp t -> File Abs
247 | resolvedExec (RA p h n d _ exe _) = pkgExec n h p exe
248 |
249 | ||| URL of the pack repository to use
250 | export %inline
251 | packRepo : (c : Config) => URL
252 | packRepo = fromMaybe defaultPackRepo c.packURL
253 |
254 | ||| Commit of pack to use
255 | export %inline
256 | packCommit : (c : Config) => Maybe Commit
257 | packCommit = c.packCommit
258 |
259 | ||| True if the path to the scheme executable actually points
260 | ||| to `racket`.
261 | export
262 | useRacket : (c : Config) => Bool
263 | useRacket = map snd (split c.scheme) == Just "racket"
264 |
265 | ||| Bootstrap command to use
266 | export
267 | bootstrapCmd : (c : Config) => String
268 | bootstrapCmd = if useRacket then "bootstrap-racket" else "bootstrap"
269 |
270 | --------------------------------------------------------------------------------
271 | --          Environment Variables
272 | --------------------------------------------------------------------------------
273 |
274 | ||| `$PREFIX` variable during Idris2 installation, unquoted
275 | export
276 | prefixVar : PackDirs => DB => EnvVar
277 | prefixVar = PrefixVar idrisPrefixDir
278 |
279 | ||| `$IDRIS2_BOOT` variable during Idris2 installation, unquoted
280 | export
281 | idrisBootVar : PackDirs => DB => EnvVar
282 | idrisBootVar = IdrisBootVar idrisExec
283 |
284 | ||| `$SCHEME` variable during Idris2 installation, unquoted
285 | export
286 | schemeVar : (c : Config) => EnvVar
287 | schemeVar = if useRacket then IdrisCodegenVar Racket else SchemeVar c.scheme
288 |
289 | ||| `IDRIS2_PREFIX` to be used with Idris when installing a library
290 | ||| to a custom location.
291 | export
292 | libInstallPrefix : PackDirs => DB => ResolvedLib t -> List EnvVar
293 | libInstallPrefix rl = [IdrisPrefixVar $ pkgPrefixDir rl.name rl.hash rl.pkg]
294 |
295 | ||| Idris executable with extra arguments, if they are present in the config.
296 | export
297 | idrisCmd : (e : Env) => CmdArgList
298 | idrisCmd = idrisExec :: e.config.extraArgs
299 |
300 | ||| Idris executable to use together with the
301 | ||| `--cg` (codegen) command line option.
302 | export
303 | idrisWithCG : (e : Env) => CmdArgList
304 | idrisWithCG = case e.config.codegen of
305 |   Default => idrisCmd
306 |   cg      => idrisCmd ++ ["--cg", cg]
307 |
308 | --------------------------------------------------------------------------------
309 | --          Environment
310 | --------------------------------------------------------------------------------
311 |
312 | getEnvPath : HasIO io => String -> io (Maybe (Path Abs))
313 | getEnvPath s = (>>= parse) <$> getEnv s
314 |
315 | getUserDir  : HasIO io => (home : Path Abs) -> io (Path Abs)
316 | getUserDir home = do
317 |   Nothing <- getEnvPath "PACK_USER_DIR"   | Just p => pure p
318 |   Nothing <- getEnvPath "XDG_CONFIG_HOME" | Just p => pure (p </> "pack")
319 |   pure (home </> ".config/pack")
320 |
321 | getStateDir : HasIO io => (home : Path Abs) -> io (Path Abs)
322 | getStateDir home = do
323 |   Nothing <- getEnvPath "PACK_STATE_DIR" | Just p => pure p
324 |   Nothing <- getEnvPath "XDG_STATE_HOME" | Just p => pure (p </> "pack")
325 |   pure (home </> ".local/state/pack")
326 |
327 | getCacheDir : HasIO io => (home : Path Abs) -> io (Path Abs)
328 | getCacheDir home = do
329 |   Nothing <- getEnvPath "PACK_CACHE_DIR" | Just p => pure p
330 |   Nothing <- getEnvPath "XDG_CACHE_HOME" | Just p => pure (p </> "pack")
331 |   pure (home </> ".cache/pack")
332 |
333 | getBinDir   : HasIO io => (home : Path Abs) -> io (Path Abs)
334 | getBinDir home = do
335 |   Nothing <- getEnvPath "PACK_BIN_DIR"   | Just p => pure p
336 |   pure (home </> ".local/bin")
337 |
338 | ||| Return the path of the pack root directory,
339 | ||| either from environment variable `$PACK_DIR`, or
340 | ||| as `$HOME/.pack`.
341 | export
342 | getPackDirs : HasIO io => EitherT PackErr io PackDirs
343 | getPackDirs = do
344 |   Just h <- getEnvPath "HOME" | Nothing => throwE NoPackDir
345 |   u      <- getUserDir h
346 |   s      <- getStateDir h
347 |   c      <- getCacheDir h
348 |   b      <- getBinDir h
349 |   pure (PD u s c b)
350 |
351 | ||| Update the package database.
352 | export
353 | updateDB : HasIO io => TmpDir => PackDirs => EitherT PackErr io ()
354 | updateDB = do
355 |   rmDir dbDir
356 |   commit <- gitLatest dbRepo "main"
357 |   withGit packDB dbRepo commit True $ \d =>
358 |     copyDir (d /> "collections") dbDir
359 |
360 | ||| Extract the name of the latest collection from a directory
361 | export
362 | latestCollection : HasIO io => (dir : Path Abs) -> EitherT PackErr io DBName
363 | latestCollection dir = do
364 |   (x :: xs) <- filter ("HEAD.toml" /=) <$> tomlFiles dir
365 |     | [] => pure Head
366 |   pure
367 |     . maybe Head MkDBName
368 |     . fileStem
369 |     $ foldl max x xs
370 |
371 | ||| Update the package database.
372 | export
373 | copyLatest : HasIO io => TmpDir => PackDirs => EitherT PackErr io DBName
374 | copyLatest = do
375 |   commit <- gitLatest dbRepo "main"
376 |   withGit packDB dbRepo commit True $ \d => do
377 |     db <- latestCollection (d /> "collections")
378 |     let body := cast {to = Body} db <+> ".toml"
379 |     copyFile (d /> "collections" /> body) (dbDir /> body)
380 |     pure db
381 |
382 | ||| Loads the name of the default collection (currently the latest
383 | ||| nightly)
384 | export
385 | defaultColl : HasIO io => TmpDir => PackDirs => EitherT PackErr io DBName
386 | defaultColl = do
387 |   when !(missing dbDir) updateDB
388 |   latestCollection dbDir
389 |
390 | ||| Resolve a meta commit by fetching the hash of the latest commit
391 | ||| from a Git repo in case of an `Fetch x` commit. In case of a `Latest x`
392 | ||| meta commit, the hash is only fetched, if the corresponding commit
393 | ||| file is missing or `fetch` is set to `True`.
394 | export
395 | resolveMeta :
396 |      {auto _ : HasIO io}
397 |   -> {auto _ : PackDirs}
398 |   -> (fetch : Bool)
399 |   -> URL
400 |   -> MetaCommit
401 |   -> EitherT PackErr io Commit
402 | resolveMeta _ u (MC x)     = pure x
403 | resolveMeta _ u (Fetch x)  = gitLatest u x
404 | resolveMeta b u (Latest x) = do
405 |   let cfile := commitFile u x
406 |   commitMissing <- fileMissing cfile
407 |   case commitMissing || b of
408 |     True  => do
409 |       c <- gitLatest u x
410 |       write cfile c.value
411 |       pure c
412 |     False => (\s => MkCommit $ trim s) <$> read cfile
413 |
414 | -- make adjustments to package map from user-defined packages
415 | adjPkgMap :
416 |      {auto _ : HasIO io}
417 |   -> {auto _ : PackDirs}
418 |   -> (fetch : Bool)
419 |   -> List (PkgName, UserPackage)
420 |   -> PackageMap
421 |   -> EitherT PackErr io PackageMap
422 | adjPkgMap fetch []            m = pure m
423 | adjPkgMap fetch ((nm,up)::ps) m = adj >>= adjPkgMap fetch ps
424 |   where
425 |     adj : EitherT PackErr io PackageMap
426 |     adj =
427 |       case up of
428 |         Local d i p t => pure $ insert nm (Local d i p t) m
429 |         Core c        => pure $ insert nm (Core c) m
430 |         Git (Just u) (Just c) (Just i) p t n =>
431 |           (\x => insert nm (Git u x i (fromMaybe False p) t n) m) <$> resolveMeta fetch u c
432 |         Git mu mc mi mp mt mn => case lookup nm m of
433 |           Just (Git u c i p t n) =>
434 |            let u2 := fromMaybe u mu
435 |                i2 := fromMaybe i mi
436 |                p2 := fromMaybe p mp
437 |                t2 := mt <|> t
438 |                n2 := mn <|> n
439 |             in case mc of
440 |                  Nothing => pure $ insert nm (Git u2 c i2 p2 t2 n2) m
441 |                  Just y  =>
442 |                    (\x => insert nm (Git u2 x i2 p2 t2 n2) m) <$> resolveMeta fetch u y
443 |           _ => throwE (IncompletePkg nm)
444 |
445 | ||| Content of pack-generated `pack.toml` containing the globally
446 | ||| set pack collection.
447 | export
448 | collectionTomlContent : DBName -> String
449 | collectionTomlContent db =
450 |   """
451 |   # Warning: This file was auto-generated and is maintained by pack.
452 |   #          Any changes could be overwritten by pack at any time.
453 |   #          Custom settings should go to the global `pack.toml` file
454 |   #          or any `pack.toml` file local to a project.
455 |   collection = \{quote db}
456 |   """
457 |
458 | ||| Read application config from command line arguments.
459 | export covering
460 | getConfig :
461 |      (0 c : Type)
462 |   -> {auto _   : Command c}
463 |   -> (args : ParsedArgs c)
464 |   -> {auto _   : HasIO io}
465 |   -> {auto pd  : PackDirs}
466 |   -> {auto td  : TmpDir}
467 |   -> EitherT PackErr io MetaConfig
468 | getConfig c args = do
469 |   -- relevant directories
470 |   coll       <- defaultColl
471 |   let cur = args.curDir
472 |
473 |   -- Initialize `pack.toml` if none exists
474 |   when !(fileMissing globalPackToml) $
475 |     write globalPackToml (initToml "scheme")
476 |
477 |   -- Initialize collection `pack.toml` if none exists
478 |   when !(fileMissing collectionToml) $
479 |     write collectionToml (collectionTomlContent coll)
480 |
481 |   localTomls  <- findInAllParentDirs (packToml ==) curDir
482 |   localConfs  <- for localTomls $ readFromTOML UserConfig
483 |   collToml    <- readOptionalFromTOML UserConfig collectionToml
484 |   global      <- readOptionalFromTOML UserConfig globalPackToml
485 |
486 |   let ini     := foldl update (init coll) (global::collToml::localConfs)
487 |
488 |   conf' <- liftEither $ applyArgs c ini args
489 |   conf  <- adjConfig args.cmd conf'
490 |
491 |   let logRef := MkLogRef conf.logLevel
492 |
493 |   debug "Pack user dir is \{pd.user}"
494 |   debug "Pack state dir is \{pd.state}"
495 |   debug "Pack cache dir is \{pd.cache}"
496 |   debug "Pack bin dir is \{pd.bin}"
497 |   debug "Current directory is \{cur}"
498 |   case localTomls of
499 |     _::_ =>
500 |       logMany
501 |         Info
502 |         {inlineSingle=True}
503 |         "Found local config at"
504 |         (interpolate <$> localTomls)
505 |     []   => debug "No local config found"
506 |   info "Using package collection \{conf.collection}"
507 |   debug "Config loaded"
508 |   mkDir pd.user
509 |   mkDir pd.state
510 |   mkDir pd.cache
511 |   mkDir pd.bin
512 |   pure conf
513 |
514 | export
515 | getLineBufferingCmd : HasIO io => io LineBufferingCmd
516 | getLineBufferingCmd = findCmd variants
517 |
518 |   where
519 |     findCmd : List (String, CmdArgList) -> io LineBufferingCmd
520 |     findCmd [] = pure $ MkLineBufferingCmd []
521 |     findCmd ((cmd, args)::rest) = do
522 |       0 <- system $ escapeCmd
523 |              ["type", cmd, NoEscape ">", "/dev/null", NoEscape "2>", "/dev/null"]
524 |         | _ => findCmd rest
525 |       0 <- system $ escapeCmd
526 |              [ cmd, "-oL", "ls", NoEscape ">", "/dev/null", NoEscape "2>", "/dev/null"]
527 |         | _ => findCmd rest
528 |       pure $ MkLineBufferingCmd $ [cmd] ++ args
529 |
530 |     variants : List (String, CmdArgList)
531 |     variants =
532 |       [ ("stdbuf",  ["-oL"])
533 |       , ("gstdbuf", ["-oL"])
534 |       ]
535 |
536 | --------------------------------------------------------------------------------
537 | --          Environment
538 | --------------------------------------------------------------------------------
539 |
540 | addCore : PackageMap -> PackageMap
541 | addCore m = foldl (\m,c => insert (corePkgName c) (Core c) m) m corePkgs
542 |
543 | ||| Load the package collection as given in the (auto-implicit) user config.
544 | export covering
545 | loadDB :
546 |      {auto _ : HasIO io}
547 |   -> {auto _ : TmpDir}
548 |   -> {auto _ : PackDirs}
549 |   -> MetaConfig
550 |   -> EitherT PackErr io MetaDB
551 | loadDB mc = do
552 |   when !(missing dbDir) updateDB
553 |   debug "reading package collection"
554 |   raw <- readFromTOML MetaDB dbFile
555 |   case fileStem dbFile of
556 |     Just "HEAD" => pure $ map toLatest raw
557 |     _           => pure raw
558 |
559 | ||| Run a pack action in the directory of the cloned Idris repository.
560 | export
561 | withCoreGit :
562 |      {auto _ : HasIO io}
563 |   -> {auto e : Env}
564 |   -> (Path Abs -> EitherT PackErr io a)
565 |   -> EitherT PackErr io a
566 | withCoreGit = withGit compiler e.db.idrisURL e.db.idrisCommit False
567 |
568 | ||| Caches the `.ipkg` files of the core libraries to make them
569 | ||| quickly available when running queries.
570 | export
571 | cacheCoreIpkgFiles : HasIO io => Env => Path Abs -> EitherT PackErr io ()
572 | cacheCoreIpkgFiles dir = do
573 |   for_ corePkgs $ \c =>
574 |     copyFile (toAbsFile dir (coreIpkgPath c)) (coreCachePath c)
575 |   let api := coreCachePath IdrisApi
576 |   desc <- parseIpkgFile api api
577 |   write versionCachePath (maybe "0.0.0" show desc.desc.version)
578 |
579 |
580 | export
581 | notCached : HasIO io => (e : Env) => PkgName -> Package -> io Bool
582 | notCached n (Git u c i _ _ _) = fileMissing $ ipkgCachePath n c i
583 | notCached n (Local d i _ _) = pure False
584 | notCached n (Core c)        = fileMissing $ coreCachePath c
585 |
586 | export
587 | cachePkg :
588 |      {auto _ : HasIO io}
589 |   -> {auto e : Env}
590 |   -> PkgName
591 |   -> Package
592 |   -> EitherT PackErr io ()
593 | cachePkg n (Git u c i _ _ _) =
594 |   let cache  := ipkgCachePath n c i
595 |       tmpLoc := gitTmpDir n </> i
596 |    in withGit n u c False $ \dir => do
597 |         let pf := patchFile n i
598 |         when !(fileExists pf) (patch tmpLoc pf)
599 |         copyFile tmpLoc cache
600 | cachePkg n (Local d i _ _)    = pure ()
601 | cachePkg n (Core c)           =
602 |   let cache  := coreCachePath c
603 |       tmpLoc := gitTmpDir compiler </> coreIpkgPath c
604 |    in withCoreGit cacheCoreIpkgFiles
605 |
606 | export
607 | cachePkgs : HasIO io => (e : Env) => EitherT PackErr io PkgVersion
608 | cachePkgs =
609 |   let pkgs := toList e.all
610 |    in do
611 |      (S n,ml,ps) <- needCaching Lin 0 60 pkgs | (0,_,_) => readIdrisVersion
612 |      traverse_ (doCache (S n) ml) ps
613 |      readIdrisVersion
614 |
615 |   where
616 |     needCaching :
617 |          SnocList (Nat,PkgName,Package)
618 |       -> (count : Nat)
619 |       -> (maxLen : Nat)
620 |       -> List (PkgName,Package)
621 |       -> EitherT PackErr io (Nat,Nat,List (Nat,PkgName,Package))
622 |     needCaching sp n ml []               = pure (n, ml, sp <>> [])
623 |     needCaching sp n ml ((pn,pkg) :: ps) = do
624 |       True <- notCached pn pkg | False => needCaching sp n ml ps
625 |       let n'  := S n
626 |           ml' := max ml (length (interpolate pn) + 26)
627 |       needCaching (sp :< (n', pn, pkg)) n' ml' ps
628 |
629 |     cacheInfo : (tot, maxLength, ix : Nat) -> PkgName -> String
630 |     cacheInfo tot ml ix pn =
631 |       let line := padRight ml '.' "Caching package info for \{pn} "
632 |           stot := show tot
633 |           six  := padLeft (length stot) ' ' (show ix)
634 |        in "\{line} (\{six}/\{stot})"
635 |
636 |     doCache :
637 |           (tot : Nat)
638 |        -> (maxLenght : Nat)
639 |        -> (Nat,PkgName,Package)
640 |        -> EitherT PackErr io ()
641 |     doCache tot ml (n,pn,pkg) = do
642 |       cache (cacheInfo tot ml n pn)
643 |       cachePkg pn pkg
644 |
645 |     readIdrisVersion : EitherT PackErr io PkgVersion
646 |     readIdrisVersion = do
647 |       when !(fileMissing versionCachePath)
648 |         (cachePkg (corePkgName IdrisApi) $ Core IdrisApi)
649 |       s <- trim <$> read versionCachePath
650 |       let vers := MkPkgVersion $ map cast $ split ('.' ==) s
651 |       debug "compiler version is \{show vers}"
652 |       pure vers
653 |
654 | ||| Load the package collection as given in the (auto-implicit) user config
655 | ||| and convert the result to a pack environment.
656 | export covering
657 | env :
658 |      {auto _   : HasIO io}
659 |   -> {auto pd  : PackDirs}
660 |   -> {auto td  : TmpDir}
661 |   -> {auto ch  : LibCache}
662 |   -> {auto lbf : LineBufferingCmd}
663 |   -> (mc       : MetaConfig)
664 |   -> (fetch    : FetchMethod)
665 |   -> EitherT PackErr io Env
666 | env mc fetch = do
667 |   mdb <- loadDB mc
668 |   clk <- liftIO $ clockTime UTC
669 |   debug "clock time is \{show $ toNano clk}"
670 |   when (fetch == ClearCommits) (rmDir commitsDir)
671 |   db  <- traverseDB (resolveMeta $ fetch > MissingOnly) mdb
672 |   c   <- traverse (resolveMeta $ fetch > MissingOnly) db.idrisURL mc
673 |
674 |   let url    := fromMaybe db.idrisURL c.idrisURL
675 |       commit := fromMaybe db.idrisCommit c.idrisCommit
676 |       -- take the DB's idris commit into account
677 |       c'     := {allIdrisCommits $= (db.idrisCommit ::)} c
678 |       -- adjust the idrisCommit and URL to use according to user overrides
679 |       db'    := {idrisURL := url, idrisCommit := commit} db
680 |       all    := fromMaybe empty $ lookup All c'.custom
681 |       loc    := fromMaybe empty $ lookup c'.collection c'.custom
682 |
683 |   -- adjusting list of packages to customizations
684 |   p1   <- adjPkgMap (fetch > MissingOnly) (kvList all) (addCore db.packages)
685 |   p2   <- adjPkgMap (fetch > MissingOnly) (kvList loc) p1
686 |   let env    := MkEnv pd td c' ch db' p2 lbf clk
687 |
688 |   vers <- cachePkgs
689 |   pure $ {db $= setVersion vers} env
690 |
691 | ||| Update the `collection` field in file `PACK_DIR/user/pack.toml`
692 | ||| with the name of the package collection given in config `c`.
693 | export covering
694 | writeCollection :
695 |      {auto _ : HasIO io}
696 |   -> {auto _ : PackDirs}
697 |   -> {auto c : Config}
698 |   -> EitherT PackErr io ()
699 | writeCollection = do
700 |   str <- read globalPackToml
701 |   write collectionToml (collectionTomlContent c.collection)
702 |