Skip to content

Instantly share code, notes, and snippets.

@taktoa
Last active January 8, 2018 12:57
Show Gist options
  • Select an option

  • Save taktoa/894ff2ba18bdde7142177f2c3ddcbaea to your computer and use it in GitHub Desktop.

Select an option

Save taktoa/894ff2ba18bdde7142177f2c3ddcbaea to your computer and use it in GitHub Desktop.
Build the profunctors library incrementally using Nix.
with builtins;
rec {
pkgs = import <nixpkgs> {};
testGHC = pkgs.haskellPackages.ghcWithPackages (p: with p; [
base base-orphans bifunctors comonad contravariant distributive
tagged transformers
]);
testInputs = [ testGHC pkgs.findutils pkgs.parallel ];
extractSrc = drv: { patches ? [] }: pkgs.stdenv.mkDerivation {
name = "${drv.name}-src";
src = drv.src;
phases = [ "unpackPhase" "patchPhase" "installPhase" ];
inherit patches;
installPhase = ''
mkdir -p $out/
cp -Rv ./* $out/
'';
};
testCBs = inputs: {
build = self: { command, dependencies }: (
pkgs.stdenv.mkDerivation {
name = "intermediate-build";
src = pkgs.buildEnv {
name = "intermediate-build-env";
paths = dependencies;
};
inherit command;
buildInputs = inputs;
phases = ["unpackPhase" "buildPhase" "installPhase"];
unpackPhase = ''
cp -Lsrv "$src" .
cd "$(basename "$src")"
chmod -Rv u+w .
'';
buildPhase = ''
eval "$command"
'';
installPhase = ''
mkdir -p "$out"
function helper () {
FILE="$1"
if test -e "$FILE"; then
echo "helper: processing $FILE"
if test -d "$FILE"; then
echo "helper: $FILE is a directory, so we should make it"
mkdir -pv "$out/$FILE"
elif test -h "$FILE"; then
echo "helper: $FILE is symlink, so we should symlink the original"
cp -sv "$(readlink -f "$FILE")" "$out/$(dirname "$FILE")"
else
echo "helper: $FILE is a regular file, so we should copy it"
cp -v "$FILE" "$out/$(dirname "$FILE")"
fi
else
echo "helper: error: $FILE does not exist"
fi
}
export -f helper
find . | parallel "helper {}"
'';
});
phony = self: dependencies: pkgs.buildEnv {
name = "intermediate-phony";
paths = dependencies;
};
leaf = self: path: (
with rec {
file = readFile (toPath "${self.src}/${path}");
drv = toFile "interned-leaf" file;
};
pkgs.runCommand "intermediate-leaf" {} ''
mkdir -p "$out/$(dirname ${path})"
cp -v "${drv}" "$out/$(dirname ${path})/$(basename ${path})"
'');
};
exampleSrc = extractSrc pkgs.haskellPackages.profunctors {
patches = [];
#patches = [ ./example.patch ];
};
dynamize = data: { build, phony, leaf }: self: (
with rec {
convertBuild = node: build self { inherit (node) command dependencies; };
convertPhony = node: phony self node.dependencies;
convertLeaf = node: leaf self node.path;
convertNode = node: (
if node.type == "build" then convertBuild node
else if node.type == "phony" then convertPhony node
else if node.type == "leaf" then convertLeaf node
else abort ("invalid node type in build graph: " + node.type));
};
{
inherit (data) src defaults;
graph = pkgs.lib.attrsets.mapAttrs (key: val: convertNode val) data.graph;
});
resolver = cb: { build, phony, leaf }: (
cb {
build = self: { command, dependencies }: (build self {
inherit command;
dependencies = map (x: self.graph.${x}) dependencies;
});
phony = self: dependencies: (
phony self (map (x: self.graph.${x}) dependencies));
inherit leaf;
});
exampleNinjaData = {
src = toPath "${exampleSrc}/src";
defaults = ["all"];
graph = {
"all" = {
type = "phony";
dependencies = [
"Data/Profunctor.o"
"Data/Profunctor/Adjunction.o"
"Data/Profunctor/Cayley.o"
"Data/Profunctor/Choice.o"
"Data/Profunctor/Closed.o"
"Data/Profunctor/Composition.o"
"Data/Profunctor/Mapping.o"
"Data/Profunctor/Monad.o"
"Data/Profunctor/Ran.o"
"Data/Profunctor/Rep.o"
"Data/Profunctor/Sieve.o"
"Data/Profunctor/Strong.o"
"Data/Profunctor/Traversing.o"
"Data/Profunctor/Types.o"
"Data/Profunctor/Unsafe.o"
];
};
"Data/Profunctor.hi" = { type = "phony"; dependencies = [ "Data/Profunctor.o" ]; };
"Data/Profunctor/Adjunction.hi" = { type = "phony"; dependencies = [ "Data/Profunctor/Adjunction.o" ]; };
"Data/Profunctor/Cayley.hi" = { type = "phony"; dependencies = [ "Data/Profunctor/Cayley.o" ]; };
"Data/Profunctor/Choice.hi" = { type = "phony"; dependencies = [ "Data/Profunctor/Choice.o" ]; };
"Data/Profunctor/Closed.hi" = { type = "phony"; dependencies = [ "Data/Profunctor/Closed.o" ]; };
"Data/Profunctor/Composition.hi" = { type = "phony"; dependencies = [ "Data/Profunctor/Composition.o" ]; };
"Data/Profunctor/Mapping.hi" = { type = "phony"; dependencies = [ "Data/Profunctor/Mapping.o" ]; };
"Data/Profunctor/Monad.hi" = { type = "phony"; dependencies = [ "Data/Profunctor/Monad.o" ]; };
"Data/Profunctor/Ran.hi" = { type = "phony"; dependencies = [ "Data/Profunctor/Ran.o" ]; };
"Data/Profunctor/Rep.hi" = { type = "phony"; dependencies = [ "Data/Profunctor/Rep.o" ]; };
"Data/Profunctor/Sieve.hi" = { type = "phony"; dependencies = [ "Data/Profunctor/Sieve.o" ]; };
"Data/Profunctor/Strong.hi" = { type = "phony"; dependencies = [ "Data/Profunctor/Strong.o" ]; };
"Data/Profunctor/Traversing.hi" = { type = "phony"; dependencies = [ "Data/Profunctor/Traversing.o" ]; };
"Data/Profunctor/Types.hi" = { type = "phony"; dependencies = [ "Data/Profunctor/Types.o" ]; };
"Data/Profunctor/Unsafe.hi" = { type = "phony"; dependencies = [ "Data/Profunctor/Unsafe.o" ]; };
"Data/Profunctor.hs" = { type = "leaf"; path = "Data/Profunctor.hs"; };
"Data/Profunctor/Adjunction.hs" = { type = "leaf"; path = "Data/Profunctor/Adjunction.hs"; };
"Data/Profunctor/Cayley.hs" = { type = "leaf"; path = "Data/Profunctor/Cayley.hs"; };
"Data/Profunctor/Choice.hs" = { type = "leaf"; path = "Data/Profunctor/Choice.hs"; };
"Data/Profunctor/Closed.hs" = { type = "leaf"; path = "Data/Profunctor/Closed.hs"; };
"Data/Profunctor/Composition.hs" = { type = "leaf"; path = "Data/Profunctor/Composition.hs"; };
"Data/Profunctor/Mapping.hs" = { type = "leaf"; path = "Data/Profunctor/Mapping.hs"; };
"Data/Profunctor/Monad.hs" = { type = "leaf"; path = "Data/Profunctor/Monad.hs"; };
"Data/Profunctor/Ran.hs" = { type = "leaf"; path = "Data/Profunctor/Ran.hs"; };
"Data/Profunctor/Rep.hs" = { type = "leaf"; path = "Data/Profunctor/Rep.hs"; };
"Data/Profunctor/Sieve.hs" = { type = "leaf"; path = "Data/Profunctor/Sieve.hs"; };
"Data/Profunctor/Strong.hs" = { type = "leaf"; path = "Data/Profunctor/Strong.hs"; };
"Data/Profunctor/Traversing.hs" = { type = "leaf"; path = "Data/Profunctor/Traversing.hs"; };
"Data/Profunctor/Types.hs" = { type = "leaf"; path = "Data/Profunctor/Types.hs"; };
"Data/Profunctor/Unsafe.hs" = { type = "leaf"; path = "Data/Profunctor/Unsafe.hs"; };
"Data/Profunctor/Traversing.o" = {
type = "build";
command = "/bin/sh -c \"ghc -c Data/Profunctor/Traversing.hs\"";
dependencies = [
"Data/Profunctor/Traversing.hs"
"Data/Profunctor/Monad.hi"
"Data/Profunctor/Unsafe.hi"
"Data/Profunctor/Strong.hi"
"Data/Profunctor/Types.hi"
"Data/Profunctor/Choice.hi"
];
};
"Data/Profunctor/Mapping.o" = {
type = "build";
command = "/bin/sh -c \"ghc -c Data/Profunctor/Mapping.hs\"";
dependencies = [
"Data/Profunctor/Mapping.hs"
"Data/Profunctor/Monad.hi"
"Data/Profunctor/Unsafe.hi"
"Data/Profunctor/Closed.hi"
"Data/Profunctor/Strong.hi"
"Data/Profunctor/Types.hi"
"Data/Profunctor/Traversing.hi"
"Data/Profunctor/Choice.hi"
];
};
"Data/Profunctor/Strong.o" = {
type = "build";
command = "/bin/sh -c \"ghc -c Data/Profunctor/Strong.hs\"";
dependencies = [
"Data/Profunctor/Strong.hs"
"Data/Profunctor/Monad.hi"
"Data/Profunctor/Unsafe.hi"
"Data/Profunctor/Types.hi"
"Data/Profunctor/Adjunction.hi"
];
};
"Data/Profunctor/Adjunction.o" = {
type = "build";
command = "/bin/sh -c \"ghc -c Data/Profunctor/Adjunction.hs\"";
dependencies = [
"Data/Profunctor/Adjunction.hs"
"Data/Profunctor/Monad.hi"
"Data/Profunctor/Types.hi"
];
};
"Data/Profunctor/Types.o" = {
type = "build";
command = "/bin/sh -c \"ghc -c Data/Profunctor/Types.hs\"";
dependencies = [
"Data/Profunctor/Types.hs"
"Data/Profunctor/Unsafe.hi"
];
};
"Data/Profunctor/Monad.o" = {
type = "build";
command = "/bin/sh -c \"ghc -c Data/Profunctor/Monad.hs\"";
dependencies = [
"Data/Profunctor/Monad.hs"
"Data/Profunctor/Types.hi"
];
};
"Data/Profunctor/Choice.o" = {
type = "build";
command = "/bin/sh -c \"ghc -c Data/Profunctor/Choice.hs\"";
dependencies = [
"Data/Profunctor/Choice.hs"
"Data/Profunctor/Monad.hi"
"Data/Profunctor/Unsafe.hi"
"Data/Profunctor/Strong.hi"
"Data/Profunctor/Types.hi"
"Data/Profunctor/Adjunction.hi"
];
};
"Data/Profunctor/Unsafe.o" = {
type = "build";
command = "/bin/sh -c \"ghc -c Data/Profunctor/Unsafe.hs\"";
dependencies = [
"Data/Profunctor/Unsafe.hs"
];
};
"Data/Profunctor/Closed.o" = {
type = "build";
command = "/bin/sh -c \"ghc -c Data/Profunctor/Closed.hs\"";
dependencies = [
"Data/Profunctor/Closed.hs"
"Data/Profunctor/Monad.hi"
"Data/Profunctor/Unsafe.hi"
"Data/Profunctor/Strong.hi"
"Data/Profunctor/Types.hi"
"Data/Profunctor/Adjunction.hi"
];
};
"Data/Profunctor/Cayley.o" = {
type = "build";
command = "/bin/sh -c \"ghc -c Data/Profunctor/Cayley.hs\"";
dependencies = [
"Data/Profunctor/Cayley.hs"
"Data/Profunctor/Unsafe.hi"
"Data/Profunctor/Monad.hi"
"Data/Profunctor.hi"
];
};
"Data/Profunctor/Composition.o" = {
type = "build";
command = "/bin/sh -c \"ghc -c Data/Profunctor/Composition.hs\"";
dependencies = [
"Data/Profunctor/Composition.hs"
"Data/Profunctor/Unsafe.hi"
"Data/Profunctor/Sieve.hi"
"Data/Profunctor/Rep.hi"
"Data/Profunctor/Monad.hi"
"Data/Profunctor/Adjunction.hi"
"Data/Profunctor.hi"
];
};
"Data/Profunctor/Sieve.o" = {
type = "build";
command = "/bin/sh -c \"ghc -c Data/Profunctor/Sieve.hs\"";
dependencies = [
"Data/Profunctor/Sieve.hs"
"Data/Profunctor.hi"
];
};
"Data/Profunctor/Rep.o" = {
type = "build";
command = "/bin/sh -c \"ghc -c Data/Profunctor/Rep.hs\"";
dependencies = [
"Data/Profunctor/Rep.hs"
"Data/Profunctor/Sieve.hi"
"Data/Profunctor.hi"
];
};
"Data/Profunctor/Ran.o" = {
type = "build";
command = "/bin/sh -c \"ghc -c Data/Profunctor/Ran.hs\"";
dependencies = [
"Data/Profunctor/Ran.hs"
"Data/Profunctor/Unsafe.hi"
"Data/Profunctor/Monad.hi"
"Data/Profunctor/Composition.hi"
"Data/Profunctor.hi"
];
};
"Data/Profunctor.o" = {
type = "build";
command = "/bin/sh -c \"ghc -c Data/Profunctor.hs\"";
dependencies = [
"Data/Profunctor.hs"
"Data/Profunctor/Closed.hi"
"Data/Profunctor/Strong.hi"
"Data/Profunctor/Types.hi"
"Data/Profunctor/Choice.hi"
"Data/Profunctor/Mapping.hi"
];
};
};
};
exampleNinja = dynamize exampleNinjaData;
combineDefault = ({ defaults, graph, ... }:
pkgs.buildEnv {
name = "default";
paths = (map (x: graph.${x}) defaults);
});
buildNinja = ninja: cbs: combineDefault (pkgs.lib.fix (resolver ninja cbs));
test = buildNinja exampleNinja (testCBs testInputs);
}
@vaibhavsagar
Copy link

This is cool! It also looks like you've reimplemented Make in Nix 😃

@taktoa
Copy link
Author

taktoa commented Jun 3, 2017

@vaibhavsagar Yes, the intention here is to eventually create a ninja2nix tool that will convert any Ninja file to a Nix derivation. Then I will convert the output of ghc -M to Ninja file, which can be done with kati.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment