| /- | |
| 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 |