Skip to content

Commit

Permalink
Add :deps/prep-lib support for git dependencies
Browse files Browse the repository at this point in the history
Source dependencies may require a "prep" step e.g. to compile Java
source[1]. This patch adds support for this but only for git
dependencies since that's the only dependency type which implements
this at the moment anyway[2]. This is achieved by extracing the
necessary info from `deps.edn` to the lockfile. A second pass over the
constructed Clojure home derived from the lockfile then picks out all
the git libs which need prepping, runs the respective command for them
and finally constructs a new home with the prepped libs.

1: See https://clojure.org/guides/deps_and_cli#prep_libs
2: Technically, :local/root dependencies also implement it but
   `clojure-nix-locker` doesn't have to handle these.
  • Loading branch information
DerGuteMoritz committed Sep 13, 2022
1 parent a5e03fb commit 7d3a7a8
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 44 deletions.
73 changes: 52 additions & 21 deletions createHome.nix
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ let
};
};

handleGit = path: { url, rev, sha256, common_dir }: {
handleGit = path: { url, rev, sha256, common_dir, ... }: {
name = path;
path = pkgs.fetchgit {
inherit url rev sha256;
Expand All @@ -32,8 +32,10 @@ let
# Corresponds to the ~/.m2/repository directory
mavenRepoCache = pkgs.linkFarm "maven-repo-cache" (lib.mapAttrsToList fetchMaven contents.maven);

unpreppedGitWorkTrees = lib.mapAttrsToList handleGit contents.git;

# This corresponds to the ~/.gitlibs/libs directory, containing git worktrees
gitWorktreeCache = pkgs.linkFarm "git-worktree-cache" (lib.mapAttrsToList handleGit contents.git);
gitWorktreeCache = gitWorkTrees: pkgs.linkFarm "git-worktree-cache" gitWorkTrees;

# This corresponds to the ~/.gitlibs/_repos directory, containing git directories for the above worktrees
gitFakeRepoCache = pkgs.runCommandNoCC "git-fake-repo-cache" {}
Expand All @@ -56,24 +58,53 @@ let
echo '{}' > $out/tools/tools.edn
'';

# Creates the final home directory, combining all parts together
result = pkgs.linkFarm "clojure-home" [
{
name = ".m2/repository";
path = mavenRepoCache;
}
{
name = ".gitlibs/libs";
path = gitWorktreeCache;
}
{
name = ".gitlibs/_repos";
path = gitFakeRepoCache;
}
# Creates a home directory for Clojure, combining all parts together
clojureHome = gitWorkTrees:
pkgs.linkFarm "clojure-home" [
{
name = ".m2/repository";
path = mavenRepoCache;
}
{
name = ".gitlibs/libs";
path = gitWorktreeCache gitWorkTrees;
}
{
name = ".gitlibs/_repos";
path = gitFakeRepoCache;
}
{
name = ".clojure";
path = configDir;
}
];

unpreppedHome = clojureHome unpreppedGitWorkTrees;

utils = import ./utils.nix { inherit pkgs; };

prepLib = { path, name }: spec:
if spec ? prep then
let prep = spec.prep; in
pkgs.runCommand "${name}-prepped"
{ nativeBuildInputs = [ (utils.wrapClojure unpreppedHome pkgs.clojure) ]; }
''
cp -r ${path} $out
chmod -R +w $out
cd $out
clojure -X:${prep.alias} ${prep.fn}
''
else
path;

prepGitWorkTree = { name, ... }@wt:
{
name = ".clojure";
path = configDir;
}
];
inherit name;
path = prepLib wt (lib.getAttr name contents.git);
};

preppedGitWorkTrees = builtins.map prepGitWorkTree unpreppedGitWorkTrees;

preppedHome = clojureHome preppedGitWorkTrees;

in result
in preppedHome
28 changes: 6 additions & 22 deletions default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ let
# We don't care about lines being too long
flakeIgnore = [ "E501" ];
} ./locker.py;

utils = import ./utils.nix { inherit pkgs; };
in {
inherit standaloneLocker;

Expand All @@ -28,6 +30,7 @@ in {
commandLocker = command: pkgs.writeShellApplication {
name = "clojure-nix-locker";
runtimeInputs = [
pkgs.babashka
pkgs.coreutils
pkgs.git
pkgs.gnutar
Expand Down Expand Up @@ -70,26 +73,7 @@ in {
homeDirectory = import ./createHome.nix {
inherit pkgs src lockfile mavenRepos;
};
shellEnv = pkgs.writeTextFile {
name = "clojure-nix-locker.shell-env";
text = ''
export HOME="${homeDirectory}"
export JAVA_TOOL_OPTIONS="-Duser.home=${homeDirectory}"
'';
meta = {
description = ''
Can be sourced in shell scripts to export environment
variables so that `clojure` uses the locked dependencies.
'';
};
};
wrapClojure = clojure:
(pkgs.runCommandNoCC "locked-clojure" { buildInputs = [ pkgs.makeWrapper ]; } ''
mkdir -p $out/bin
makeWrapper ${clojure}/bin/clojure $out/bin/clojure \
--run "source ${shellEnv}"
makeWrapper ${clojure}/bin/clj $out/bin/clj \
--run "source ${shellEnv}"
'');
};
shellEnv = utils.shellEnv homeDirectory;
wrapClojure = utils.wrapClojure homeDirectory;
};
}
13 changes: 12 additions & 1 deletion locker.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,20 +29,31 @@

gitlibsDir = args.home.joinpath('.gitlibs').resolve()

extractPrepLibInfo = '''
(some-> *input*
:deps/prep-lib
(select-keys [:alias :fn])
cheshire.core/generate-string
println)
'''

if gitlibsDir.exists():
for namespace_path in gitlibsDir.joinpath('libs').iterdir():
for name_path in namespace_path.iterdir():
for rev_path in name_path.iterdir():
path = rev_path.relative_to(gitlibsDir, "libs").as_posix()
repo = Repo(rev_path)
prefetch = subprocess.run(["nix-prefetch-git", rev_path], stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, check=True)

with open(rev_path / "deps.edn") as deps_edn:
prep = subprocess.run(["bb", "-I", "--stream", extractPrepLibInfo], stdin=deps_edn, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, check=True)
result['git'][path] = {
# This is the path to the corresponding bare repository in ~/.gitlibs/_repos
"common_dir": Path(repo.common_dir).resolve().relative_to(gitlibsDir, "_repos").as_posix(),
"url": repo.remotes.origin.url,
"rev": repo.head.commit.hexsha,
"sha256": json.loads(prefetch.stdout)['sha256'],
}
if preps := prep.stdout:
result['git'][path]['prep'] = json.loads(preps)

print(json.dumps(result, indent=2, sort_keys=True))
25 changes: 25 additions & 0 deletions utils.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{ pkgs }:

rec {
shellEnv = homeDirectory: pkgs.writeTextFile {
name = "clojure-nix-locker.shell-env";
text = ''
export HOME="${homeDirectory}"
export JAVA_TOOL_OPTIONS="-Duser.home=${homeDirectory}"
'';
meta = {
description = ''
Can be sourced in shell scripts to export environment
variables so that `clojure` uses the locked dependencies.
'';
};
};
wrapClojure = homeDirectory: clojure:
(pkgs.runCommandNoCC "locked-clojure" { buildInputs = [ pkgs.makeWrapper ]; } ''
mkdir -p $out/bin
makeWrapper ${clojure}/bin/clojure $out/bin/clojure \
--run "source ${shellEnv homeDirectory}"
makeWrapper ${clojure}/bin/clj $out/bin/clj \
--run "source ${shellEnv homeDirectory}"
'');
}

0 comments on commit 7d3a7a8

Please sign in to comment.