diff --git a/Lake/Config/Dependency.lean b/Lake/Config/Dependency.lean index 589aa4fdfe..b6226ef964 100644 --- a/Lake/Config/Dependency.lean +++ b/Lake/Config/Dependency.lean @@ -17,7 +17,7 @@ In Lake, dependency sources currently come into flavors: -/ inductive Source where | path (dir : FilePath) -| git (url rev : String) +| git (url : String) (rev : Option String) deriving Inhabited, Repr /-- A `Dependency` of a package. -/ diff --git a/Lake/Config/Resolve.lean b/Lake/Config/Resolve.lean index 591aa9b12c..3aac6877e1 100644 --- a/Lake/Config/Resolve.lean +++ b/Lake/Config/Resolve.lean @@ -17,21 +17,26 @@ namespace Lake section open Git -/-- Update the Git package in `dir` if necessary. -/ +/-- Update the Git package in `dir` to `rev` if not already at it. -/ def updateGitPkg (name : String) -(dir : FilePath) (rev : String) : (LogT IO) PUnit := do - if (← headRevision dir) == rev then return - logInfo s!"{name}: updating {dir} to revision {rev}" - unless ← revisionExists rev dir do fetch dir - checkoutDetach rev dir +(dir : FilePath) (rev? : Option String) : LogT IO PUnit := do + if let some rev := rev? then + if (← headRevision dir) == rev then return + logInfo s!"{name}: updating {dir} to revision {rev}" + unless ← revisionExists rev dir do fetch dir + checkoutDetach rev dir + else + logInfo s!"{name}: updating {dir}" + pull dir /-- Clone the Git package as `dir`. -/ -def cloneGitPkg (name : String) -(dir : FilePath) (url rev : String) : (LogT IO) PUnit := do +def cloneGitPkg (name : String) (dir : FilePath) +(url : String) (rev? : Option String) : LogT IO PUnit := do logInfo s!"{name}: cloning {url} to {dir}" clone url dir - let hash ← parseOriginRevision rev dir - checkoutDetach hash dir + if let some rev := rev? then + let hash ← parseOriginRevision rev dir + checkoutDetach hash dir abbrev ResolveM := StateT (NameMap PackageEntry) <| LogT IO @@ -42,39 +47,37 @@ Attempts to reproduce the `PackageEntry` in the manifest (if one exists) unless `shouldUpdate` is true. Otherwise, produces the package based on `url` and `rev` and saves the result to the manifest. -/ -def materializeGitPkg (name : String) -(dir : FilePath) (url rev : String) (shouldUpdate := true) : ResolveM PUnit := do +def materializeGitPkg (name : String) (dir : FilePath) +(url : String) (rev? : Option String) (shouldUpdate := true) : ResolveM PUnit := do if let some entry := (← get).find? name then if (← dir.isDir) then if url = entry.url then if shouldUpdate then - let rev ← parseOriginRevision rev dir - updateGitPkg name dir rev + updateGitPkg name dir rev? + let rev ← headRevision dir modify (·.insert name {entry with rev}) else updateGitPkg name dir entry.rev else if shouldUpdate then logInfo s!"{name}: URL changed, deleting {dir} and cloning again" IO.FS.removeDirAll dir - cloneGitPkg name dir url rev - let rev ← parseOriginRevision rev dir + cloneGitPkg name dir url rev? + let rev ← headRevision dir modify (·.insert name {entry with url, rev}) else if shouldUpdate then - cloneGitPkg name dir url rev - let rev ← parseOriginRevision rev dir + cloneGitPkg name dir url rev? + let rev ← headRevision dir modify (·.insert name {entry with url, rev}) else cloneGitPkg name dir entry.url entry.rev else if (← dir.isDir) then - let rev ← parseOriginRevision rev dir + let rev ← headRevision dir modify (·.insert name {name, url, rev}) - if (← headRevision dir) == rev then return - updateGitPkg name dir rev else - cloneGitPkg name dir url rev - let rev ← parseOriginRevision rev dir + cloneGitPkg name dir url rev? + let rev ← headRevision dir modify (·.insert name {name, url, rev}) end @@ -87,10 +90,10 @@ def materializeDep (ws : Workspace) (pkg : Package) (dep : Dependency) (shouldUpdate := true) : ResolveM FilePath := match dep.src with | Source.path dir => return pkg.dir / dir - | Source.git url rev => do + | Source.git url rev? => do let name := dep.name.toString (escape := false) let depDir := ws.packagesDir / name - materializeGitPkg name depDir url rev shouldUpdate + materializeGitPkg name depDir url rev? shouldUpdate return depDir /-- diff --git a/Lake/DSL/Require.lean b/Lake/DSL/Require.lean index d0e1f4d49c..aedabd4925 100644 --- a/Lake/DSL/Require.lean +++ b/Lake/DSL/Require.lean @@ -14,7 +14,7 @@ syntax fromPath := term syntax fromGit := - &" git " term:max "@" term:max ("/" term)? + &" git " term:max ("@" term:max)? ("/" term)? syntax fromClause := fromGit <|> fromPath @@ -23,10 +23,16 @@ syntax depSpec := ident " from " fromClause (" with " term)? def evalDepSpec : Syntax → TermElabM Dependency -| `(depSpec| $name:ident from git $url @ $rev / $path $[with $args?:term]?) => do +| `(depSpec| $name:ident from git $url $[@ $rev?]? $[/ $path?]? $[with $args?:term]?) => do let url ← evalTerm String url - let rev ← evalTerm String rev - let path ← evalTerm System.FilePath path + let rev ← + match rev? with + | some rev => some <$> evalTerm String rev + | none => pure none + let path ← + match path? with + | some path => evalTerm System.FilePath path + | none => pure "." let args ← match args? with | some args => evalTerm (List String) args | none => pure [] @@ -48,7 +54,8 @@ require bar from git "url.git"@"rev"/"optional"/"path-to"/"dir-with-pkg" ``` Either form supports the optional `with` clause. -The `/` and the following term in the git form of `require` is optional. +The `@"rev"` and `/"path"/"dir"` parts of the git form of `require` +are optional. The elements of both the `from` and `with` clauses are proper terms so normal computation is supported within them (though parentheses made be diff --git a/Lake/Util/Git.lean b/Lake/Util/Git.lean index bb2ad8ac18..de481ebbf8 100644 --- a/Lake/Util/Git.lean +++ b/Lake/Util/Git.lean @@ -41,6 +41,9 @@ def quietInit (repo : Option FilePath := none) := def fetch (repo : Option FilePath := none) := execGit #["fetch"] repo +def pull (repo : Option FilePath := none) := + execGit #["pull"] repo + def checkoutBranch (branch : String) (repo : Option FilePath := none) := execGit #["checkout", "-B", branch] repo diff --git a/README.md b/README.md index a567874899..37a7728a5b 100644 --- a/README.md +++ b/README.md @@ -195,7 +195,7 @@ require foo from "path"/"to"/"local"/"package" with ["optional","args"] require bar from git "url.git"@"rev"/"optional"/"path-to"/"dir-with-pkg" ``` -The first form adds a local dependency and the second form adds a Git dependency. For a Git dependency, the revision can be a commit hash, branch, or tag. Also, the `/` and the following term of the `require` is optional. +The first form adds a local dependency and the second form adds a Git dependency. For a Git dependency, the revision can be a commit hash, branch, or tag. Also, the `@"rev"` and `/"path-to"/"term"` parts of the `require` are optional. Both forms also support an optional `with` clause to specify arguments to pass to the dependency's package configuration (i.e., same as `args` in a `lake build -- ` invocation). The elements of both the `from` and `with` clauses are proper terms so normal computation is supported within them (though parentheses made be required to disambiguate the syntax). diff --git a/examples/git/lakefile.lean b/examples/git/lakefile.lean index f5518efa82..a304bc3d2b 100644 --- a/examples/git/lakefile.lean +++ b/examples/git/lakefile.lean @@ -5,7 +5,7 @@ open System Lake DSL package git_hello require hello from - git "../.."@"master"/"examples"/"hello" + git "../.."/"examples"/"hello" @[defaultTarget] lean_exe git_hello {