/- Copyright (c) 2017 Microsoft Corporation. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Author: Gabriel Ebner -/ import leanpkg.manifest system.io data.hash_map leanpkg.proc variable [io.interface] namespace leanpkg def assignment := hash_map string (λ _, string) -- TODO(gabriel): hash function for strings def assignment.empty : assignment := mk_hash_map list.length @[reducible] def solver := state_t assignment io instance {α : Type} : has_coe (io α) (solver α) := ⟨state_t.lift⟩ def not_yet_assigned (d : string) : solver bool := do assg ← state_t.read, return $ ¬ assg.contains d def resolved_path (d : string) : solver string := do assg ← state_t.read, some path ← return (assg.find d) | io.fail "", return path -- TODO(gabriel): directory existence testing def dir_exists (d : string) : io bool := do ch ← io.proc.spawn { cmd := "test", args := ["-d", d] }, ev ← io.proc.wait ch, return $ ev = 0 -- TODO(gabriel): windows? def resolve_dir (abs_or_rel : string) (base : string) : string := if abs_or_rel.reverse.head = '/' then abs_or_rel -- absolute else base ++ "/" ++ abs_or_rel def materialize (relpath : string) (dep : dependency) : solver unit := match dep.src with | (source.path dir) := do let depdir := resolve_dir dir relpath, io.put_str_ln $ dep.name ++ ": using local path " ++ depdir, state_t.modify $ λ assg, assg.insert dep.name depdir | (source.git url rev) := do let depdir := "_target/deps/" ++ dep.name, already_there ← dir_exists depdir, let checkout_action := exec_cmd {cmd := "git", args := ["checkout", "--detach", rev], cwd := depdir}, (do guard already_there, io.put_str_ln $ dep.name ++ ": trying to update " ++ depdir ++ " to revision " ++ rev, checkout_action) <|> (do guard already_there, exec_cmd {cmd := "git", args := ["fetch"], cwd := depdir}, checkout_action) <|> (do io.put_str_ln $ dep.name ++ ": cloning " ++ url ++ " to " ++ depdir, exec_cmd {cmd := "rm", args := ["-rf", depdir]}, exec_cmd {cmd := "mkdir", args := ["-p", depdir]}, exec_cmd {cmd := "git", args := ["clone", url, depdir]}, exec_cmd {cmd := "git", args := ["checkout", "--detach", rev], cwd := depdir}), state_t.modify $ λ assg, assg.insert dep.name depdir end def solve_deps_core : ∀ (rel_path : string) (d : manifest) (max_depth : ℕ), solver unit | _ _ 0 := io.fail "maximum dependency resolution depth reached" | relpath d (max_depth + 1) := do deps ← monad.filter (not_yet_assigned ∘ dependency.name) d.dependencies, monad.for' deps (materialize relpath), monad.for' deps $ λ dep, do p ← resolved_path dep.name, d ← manifest.from_file $ p ++ "/" ++ "leanpkg.toml", when (d.name ≠ dep.name) $ io.fail $ d.name ++ " (in " ++ relpath ++ ") depends on " ++ d.name ++ ", but resolved dependency has name " ++ dep.name ++ " (in " ++ p ++ ")", solve_deps_core p d max_depth def solve_deps (d : manifest) : io assignment := do (_, assg) ← solve_deps_core "." d 1024 $ assignment.empty.insert d.name ".", return assg def construct_path_core (depname : string) (dirname : string) : io (list string) := list.map (λ relpath, dirname ++ "/" ++ relpath) <$> manifest.effective_path <$> (manifest.from_file $ dirname ++ "/" ++ leanpkg_toml_fn) def construct_path (assg : assignment) : io (list string) := do let assg := assg.fold [] (λ xs depname dirname, (depname, dirname) :: xs), list.join <$> (list.mfor assg $ λ ⟨depname, dirname⟩, construct_path_core depname dirname) end leanpkg