blob: 6afc734be1697883d4c0a92d4541f4b679ea781a [file]
/-
Copyright (c) 2017 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Gabriel Ebner, Sebastian Ullrich
-/
import Leanpkg.Manifest
import Leanpkg.Proc
import Leanpkg.Git
namespace Leanpkg
def Assignment := List (String × String)
namespace Assignment
def empty : Assignment := []
def contains (a : Assignment) (s : String) : Bool :=
(a.lookup s).isSome
def insert (a : Assignment) (k v : String) : Assignment :=
if a.contains k then a else (k, v) :: a
def fold {α} (i : α) (f : α → String → String → α) : Assignment → α :=
List.foldl (fun a ⟨k, v⟩ => f a k v) i
end Assignment
abbrev Solver := StateT Assignment IO
def notYetAssigned (d : String) : Solver Bool := do
¬ (← get).contains d
def resolvedPath (d : String) : Solver String := do
let some path ← pure ((← get).lookup d) | unreachable!
path
-- TODO(gabriel): windows?
def resolveDir (absOrRel : String) (base : String) : String :=
if absOrRel.front = '/' then
absOrRel -- absolute
else
base ++ "/" ++ absOrRel
def materialize (relpath : String) (dep : Dependency) : Solver Unit :=
match dep.src with
| Source.path dir => do
let depdir := resolveDir dir relpath
IO.eprintln s!"{dep.name}: using local path {depdir}"
modify (·.insert dep.name depdir)
| Source.git url rev branch => do
let depdir := "build/deps/" ++ dep.name
let alreadyThere ← IO.isDir depdir
if alreadyThere then
IO.eprint s!"{dep.name}: trying to update {depdir} to revision {rev}"
IO.eprintln (match branch with | none => "" | some branch => "@" ++ branch)
let hash ← gitParseOriginRevision depdir rev
let revEx ← gitRevisionExists depdir hash
unless revEx do
execCmd {cmd := "git", args := #["fetch"], cwd := depdir}
else
IO.eprintln s!"{dep.name}: cloning {url} to {depdir}"
execCmd {cmd := "git", args := #["clone", url, depdir]}
let hash ← gitParseOriginRevision depdir rev
execCmd {cmd := "git", args := #["checkout", "--detach", hash], cwd := depdir}
modify (·.insert dep.name depdir)
def solveDepsCore (relPath : String) (d : Manifest) : (maxDepth : Nat) → Solver Unit
| 0 => throw <| IO.userError "maximum dependency resolution depth reached"
| maxDepth + 1 => do
let deps ← d.dependencies.filterM (notYetAssigned ·.name)
deps.forM (materialize relPath)
for dep in deps do
let p ← resolvedPath dep.name
let d' ← Manifest.fromFile $ p ++ "/" ++ "leanpkg.toml"
unless d'.name = dep.name do
throw <| IO.userError <| d.name ++ " (in " ++ relPath ++ ") depends on " ++ d'.name ++
", but resolved dependency has name " ++ dep.name ++ " (in " ++ p ++ ")"
solveDepsCore p d' maxDepth
def solveDeps (d : Manifest) : IO Assignment := do
let (_, assg) ← (solveDepsCore "." d 1024).run <| Assignment.empty.insert d.name "."
assg
def constructPathCore (depname : String) (dirname : String) : IO String := do
let path ← Manifest.effectivePath (← Manifest.fromFile $ dirname ++ "/" ++ leanpkgTomlFn)
return dirname ++ "/" ++ path
def constructPath (assg : Assignment) : IO (List String) := do
assg.reverse.mapM fun ⟨depname, dirname⟩ => constructPathCore depname dirname
end Leanpkg