Browse Source

Officially rename this project to Phoebe

master
Peter J. Jones 1 year ago
commit
320db663be
9 changed files with 637 additions and 0 deletions
  1. 26
    0
      LICENSE
  2. 40
    0
      README.md
  3. 18
    0
      default.nix
  4. 38
    0
      nix/call-package.nix
  5. 109
    0
      nix/interactive.nix
  6. 47
    0
      nix/package.nix
  7. 19
    0
      nix/stack.nix
  8. 320
    0
      src/nix-hs.sh
  9. 20
    0
      templates/default.nix

+ 26
- 0
LICENSE View File

@@ -0,0 +1,26 @@
1
+Copyright (c) 2017 Peter J. Jones <pjones@devalot.com>
2
+All rights reserved.
3
+
4
+Redistribution and use in source and binary forms, with or without
5
+modification, are permitted provided that the following conditions are
6
+met:
7
+
8
+1. Redistributions of source code must retain the above copyright
9
+   notice, this list of conditions and the following disclaimer.
10
+
11
+2. Redistributions in binary form must reproduce the above copyright
12
+   notice, this list of conditions and the following disclaimer in the
13
+   documentation and/or other materials provided with the
14
+   distribution.
15
+
16
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

+ 40
- 0
README.md View File

@@ -0,0 +1,40 @@
1
+# Haskell + nixpkgs = nix-hs
2
+
3
+Are you a [Haskell][] programmer?  Do you use [nixpkgs][]?  Want to
4
+make using those two together really simple?  You're in luck.
5
+
6
+This project provides a set of Nix files and a tool called `nix-hs`
7
+that makes working with Haskell projects very simple.  For starters,
8
+Nix files are automatically generated and updated as needed.  Other
9
+features include:
10
+
11
+  * Works with both `cabal` and `stack`
12
+  * Build with profiling using a command line option
13
+  * Easily use any version of GHC in `nixpkgs`
14
+  * Interactive development and package generation
15
+
16
+## Installing nix-hs
17
+
18
+Coming soon...
19
+
20
+Hint: Install it as an [overlay] [].
21
+
22
+## Interactive Development
23
+
24
+Coming soon...
25
+
26
+Hint: `$ nix-hs -h`
27
+
28
+## Making a Private Package for nixpkgs
29
+
30
+Coming soon...
31
+
32
+## Other Things You Should Know
33
+
34
+  * In order to be idempotent, `nix-hs` runs `cabal` without a
35
+    configuration file (usually `~/.cabal/config`).  This also keeps
36
+    `cabal` from downloading packages from hackage.
37
+
38
+[haskell]: https://www.haskell.org/
39
+[nixpkgs]: https://nixos.org/nix/
40
+[overlay]: https://nixos.org/nixpkgs/manual/#chap-overlays

+ 18
- 0
default.nix View File

@@ -0,0 +1,18 @@
1
+################################################################################
2
+# This file is a nixpkgs overlay.
3
+
4
+################################################################################
5
+#
6
+# This file is part of the package nix-hs. It is subject to the license
7
+# terms in the LICENSE file found in the top-level directory of this
8
+# distribution and at:
9
+#
10
+#   git://git.devalot.com/nix-hs.git
11
+#
12
+# No part of this package, including this file, may be copied, modified,
13
+# propagated, or distributed except according to the terms contained in
14
+# the LICENSE file.
15
+
16
+self: super: {
17
+  nix-hs = with self; callPackage ./nix/package.nix { };
18
+}

+ 38
- 0
nix/call-package.nix View File

@@ -0,0 +1,38 @@
1
+################################################################################
2
+#
3
+# This file is part of the package nix-hs. It is subject to the license
4
+# terms in the LICENSE file found in the top-level directory of this
5
+# distribution and at:
6
+#
7
+#   git://git.devalot.com/nix-hs.git
8
+#
9
+# No part of this package, including this file, may be copied, modified,
10
+# propagated, or distributed except according to the terms contained in
11
+# the LICENSE file.
12
+
13
+################################################################################
14
+{ stdenv, pkgs, lib, ...}:
15
+
16
+################################################################################
17
+fn: # This is the function/file generated by cabal2nix.
18
+
19
+{ src                            # Where to get the package from.
20
+, haskell ? pkgs.haskellPackages # Pass this in from your Haskell overrides.
21
+, buildInputs ? []               # Extra packages needed during building.
22
+, postInstall ? ""               # Extra install steps
23
+, builderOptions ? { }           # Extra options to pass to the Haskell mkDerivation
24
+}:
25
+
26
+################################################################################
27
+let callPackage = haskell.callPackage fn {
28
+      mkDerivation = { buildTools ? []
29
+                     , ...
30
+                     }@args:
31
+      haskell.mkDerivation (args // builderOptions // {
32
+        buildTools  = buildInputs ++ buildTools;
33
+        postInstall = postInstall;
34
+        src         = src;
35
+      });
36
+    };
37
+
38
+in callPackage

+ 109
- 0
nix/interactive.nix View File

@@ -0,0 +1,109 @@
1
+################################################################################
2
+#
3
+# This file is part of the package nix-hs. It is subject to the license
4
+# terms in the LICENSE file found in the top-level directory of this
5
+# distribution and at:
6
+#
7
+#   git://git.devalot.com/nix-hs.git
8
+#
9
+# No part of this package, including this file, may be copied, modified,
10
+# propagated, or distributed except according to the terms contained in
11
+# the LICENSE file.
12
+#
13
+################################################################################
14
+{ pkgs         ? (import <nixpkgs> {}).pkgs
15
+, compiler     ? "default" # Which version of GHC to use, or "default".
16
+, profiling    ? false     # Enable profiling or not.
17
+, optimization ? true      # Enable optimization or not.
18
+, doHaddock    ? true      # Create documentation for dependencies.
19
+, file                     # The package file (default.nix) to load.
20
+}:
21
+
22
+with pkgs.lib;
23
+
24
+let
25
+
26
+  # Some handy bindings:
27
+  cabal = "${haskell.cabal-install}/bin/cabal";
28
+
29
+  cabalConfigureFlags = concatStringsSep " "
30
+    [ (optionalString optimization "--enable-optimization")
31
+      "--enable-tests" # Safe to always keep on.
32
+    ];
33
+
34
+  # These are the shell functions that `nix-hs' will call into.
35
+  shellFunctions = "\n" + ''
36
+    # NOTE: This is here because the generic Haskell builder doesn't
37
+    # end it's shellHook with a newline and so we get syntax errors
38
+    # without the blank line above.
39
+    alias cabal='${cabal} --config-file=/dev/null'
40
+
41
+    do_cabal_configure() {
42
+      set -e
43
+      cabal configure ${cabalConfigureFlags}
44
+    }
45
+
46
+    do_cabal_build() {
47
+      set -e
48
+      cabal build
49
+    }
50
+
51
+    do_cabal_test() {
52
+      set -e
53
+      cabal test
54
+    }
55
+
56
+    do_cabal_clean() {
57
+      set -e
58
+      cabal clean
59
+    }
60
+
61
+    do_cabal_repl() {
62
+      set -e
63
+      cabal repl "$@"
64
+    }
65
+  '';
66
+
67
+  # Select a compiler:
68
+  basePackages =
69
+    if compiler == "default"
70
+      then pkgs.haskellPackages
71
+      else pkgs.haskell.packages."ghc${compiler}";
72
+
73
+  # Overrides to control properties such as profiling.  This is a bit
74
+  # of a mess because Haskell overrides in Nix don't seem to compose
75
+  # well: https://github.com/NixOS/nixpkgs/issues/26561
76
+  haskell = basePackages.override (orig: {
77
+    overrides = composeExtensions (orig.overrides or (_: _: {}))
78
+      (self: super: {
79
+        mkDerivation = { shellHook ? ""
80
+                       , ...
81
+                       }@args:
82
+          super.mkDerivation (args // {
83
+            inherit doHaddock;
84
+            shellHook = shellHook + shellFunctions;
85
+            enableLibraryProfiling = profiling;
86
+            enableExecutableProfiling = profiling;
87
+          });
88
+    });
89
+  });
90
+
91
+  # haskell = basePackages.extend (self: super: {
92
+  #     mkDerivation = { shellHook ? ""
93
+  #                    , ...
94
+  #                    }@args:
95
+  #       super.mkDerivation (args // {
96
+  #         inherit doHaddock;
97
+  #         shellHook = shellHook + shellFunctions;
98
+  #         enableLibraryProfiling = profiling;
99
+  #         enableExecutableProfiling = profiling;
100
+  #       });
101
+  # });
102
+
103
+  # Override the Haskell package set with the one from above:
104
+  alteredPackages = pkgs // { haskellPackages = haskell; };
105
+
106
+  # Load the local file:
107
+  drv = import file { pkgs = alteredPackages; };
108
+
109
+in drv.env

+ 47
- 0
nix/package.nix View File

@@ -0,0 +1,47 @@
1
+################################################################################
2
+#
3
+# This file is part of the package nix-hs. It is subject to the license
4
+# terms in the LICENSE file found in the top-level directory of this
5
+# distribution and at:
6
+#
7
+#   git://git.devalot.com/nix-hs.git
8
+#
9
+# No part of this package, including this file, may be copied, modified,
10
+# propagated, or distributed except according to the terms contained in
11
+# the LICENSE file.
12
+
13
+################################################################################
14
+{ stdenvNoCC, pkgs, lib
15
+, bash, cabal2nix, haskellPackages
16
+}:
17
+
18
+with lib;
19
+
20
+let
21
+  drv = stdenvNoCC.mkDerivation rec {
22
+    name = "nix-hs-${version}";
23
+    version = "0.2.0";
24
+
25
+    phases = [ "installPhase" ];
26
+
27
+    installPhase = ''
28
+      mkdir -p $out/bin $out/templates $out/lib
29
+
30
+      export bash=${bash}
31
+      export cabal2nix=${cabal2nix}
32
+      export stack=${haskellPackages.stack}
33
+      export templates=$out/templates
34
+      export interactive=$out/lib/interactive.nix
35
+      export stacknix=$out/lib/stack.nix
36
+
37
+      substituteAll ${../src/nix-hs.sh} $out/bin/nix-hs
38
+      chmod 0555 $out/bin/nix-hs
39
+
40
+      install -m0444 ${../templates/default.nix} $out/templates/default.nix
41
+      install -m0444 ${../nix/interactive.nix} $out/lib/interactive.nix
42
+      install -m0400 ${../nix/stack.nix} $out/lib/stack.nix
43
+    '';
44
+  };
45
+in {
46
+  callPackage = pkgs.callPackage ./call-package.nix { };
47
+} // drv

+ 19
- 0
nix/stack.nix View File

@@ -0,0 +1,19 @@
1
+{ pkgs ? (import <nixpkgs> {}).pkgs
2
+, ghc  ? pkgs.ghc
3
+, file
4
+}:
5
+
6
+let
7
+  # Load the local package:
8
+  package = import file { pkgs = pkgs; };
9
+
10
+  # Pull build inputs from the local package: (not sure why, but there
11
+  # are some nulls in the buildInputs that we need to remove).
12
+  buildInputs = builtins.filter (p: p != null)
13
+    package.buildInputs;
14
+
15
+in pkgs.haskell.lib.buildStackProject {
16
+  name = package.name;
17
+  buildInputs = buildInputs;
18
+  inherit ghc;
19
+}

+ 320
- 0
src/nix-hs.sh View File

@@ -0,0 +1,320 @@
1
+#! @bash@/bin/bash
2
+
3
+################################################################################
4
+#
5
+# This file is part of the package nix-hs. It is subject to the license
6
+# terms in the LICENSE file found in the top-level directory of this
7
+# distribution and at:
8
+#
9
+#   git://git.devalot.com/nix-hs.git
10
+#
11
+# No part of this package, including this file, may be copied, modified,
12
+# propagated, or distributed except according to the terms contained in
13
+# the LICENSE file.
14
+
15
+################################################################################
16
+set -e
17
+set -u
18
+
19
+################################################################################
20
+export HASKELL_PROJECT_NAME
21
+export HASKELL_PROJECT_DIR
22
+
23
+################################################################################
24
+option_compiler=default
25
+option_profiling=false
26
+option_debug=0
27
+option_tool=""
28
+option_nixshell_args=()
29
+
30
+################################################################################
31
+usage () {
32
+cat <<EOF
33
+Usage: nix-hs [options] (build|test|clean|repl|shell)
34
+
35
+  -c VER  Use GHC version VER
36
+  -d      Enable debugging info for nix-hs
37
+  -h      This message
38
+  -I PATH Add PATH to NIX_PATH
39
+  -p      Enable profiling [default: off]
40
+  -n PATH Shortcut for '-I nixpkgs=PATH'
41
+  -t TYPE Force using build type TYPE (cabal|stack|make)
42
+EOF
43
+}
44
+
45
+################################################################################
46
+die() {
47
+  echo "ERROR:" "$@" > /dev/stderr
48
+  exit 1
49
+}
50
+
51
+################################################################################
52
+get_project_name() {
53
+  local name
54
+  local matches
55
+
56
+  name=$(ls ./?*.cabal 2> /dev/null)
57
+  matches=$(echo "$name" | wc -l)
58
+
59
+  if [ "$matches" != 1 ]; then
60
+    return 1
61
+  fi
62
+
63
+  basename "$name" .cabal
64
+}
65
+
66
+################################################################################
67
+# Search for a project Cabal file, changing to the directory that
68
+# contains it or dies.
69
+find_project() {
70
+  local dir
71
+  local name
72
+
73
+  dir=$(pwd)
74
+
75
+  while [ "$dir" != / ]; do
76
+    if name=$(get_project_name); then
77
+      break
78
+    fi
79
+
80
+    dir=$(dirname "$dir")
81
+    cd "$dir"
82
+  done
83
+
84
+  if [ "$dir" = / ]; then
85
+    die "cannot find the .cabal file for this project"
86
+  else
87
+    HASKELL_PROJECT_NAME="$name"
88
+    HASKELL_PROJECT_DIR="$dir"
89
+  fi
90
+}
91
+
92
+################################################################################
93
+# Check to see if the project root is *not* the same directory as the
94
+# project directory.  This is a common directory layout with stack
95
+# where one stack.yaml file is used to point to several projects.
96
+find_stack_root() {
97
+  # See if the parent directory has a stack file:
98
+  export STACK_YAML=${STACK_YAML:-stack.yaml}
99
+
100
+  if [ -r "$STACK_YAML" ]; then
101
+    return 0
102
+  elif [ ! -r "$STACK_YAML" ] && [ -r ../"$STACK_YAML" ]; then
103
+    export STACK_YAML="../$STACK_YAML"
104
+    return 0
105
+  fi
106
+
107
+  return 1
108
+}
109
+
110
+################################################################################
111
+nix_shell() {
112
+  local extra_options=()
113
+
114
+  if [ "$option_debug" -eq 1 ]; then
115
+    extra_options+=("--show-trace")
116
+  fi
117
+
118
+  # FIXME: support all interactive.nix options.
119
+
120
+  nix-shell --pure "$@" \
121
+            --argstr file "$(pwd)/default.nix" \
122
+            --argstr compiler "$option_compiler" \
123
+            --arg profiling "$option_profiling" \
124
+            "${option_nixshell_args[@]}" "${extra_options[@]}" \
125
+            @interactive@
126
+}
127
+
128
+################################################################################
129
+# Create/update the nix file from the cabal file.
130
+prepare_nix_files() {
131
+  local cabal_file=${HASKELL_PROJECT_NAME}.cabal
132
+  local nix_file=${HASKELL_PROJECT_NAME}.nix
133
+
134
+  if [ ! -r "$nix_file" ] || [ "$cabal_file" -nt "$nix_file" ]; then
135
+    @cabal2nix@/bin/cabal2nix . > "$nix_file"
136
+  fi
137
+
138
+  if [ ! -r "default.nix" ]; then
139
+    sed -e "s/@PROJECT@/${HASKELL_PROJECT_NAME}/g" \
140
+        < @templates@/default.nix > default.nix
141
+  fi
142
+}
143
+
144
+################################################################################
145
+# If needed, run `cabal configure'.
146
+cabal_configure() {
147
+  local cabal_file=${HASKELL_PROJECT_NAME}.cabal
148
+  local datestamp=dist/.configure-run-date
149
+
150
+  if [ ! -r "$datestamp" ] || [ "$cabal_file" -nt "$datestamp" ]; then
151
+    nix_shell --command "do_cabal_configure"
152
+    date > dist/.configure-run-date
153
+  fi
154
+}
155
+
156
+################################################################################
157
+run_cabal() {
158
+  prepare_nix_files
159
+  cabal_configure
160
+
161
+  case "${1:-build}" in
162
+    repl)
163
+      nix_shell --command "do_cabal_repl lib:$HASKELL_PROJECT_NAME"
164
+      ;;
165
+
166
+    shell)
167
+      nix_shell
168
+      ;;
169
+
170
+    *)
171
+      nix_shell --command "do_cabal_$1"
172
+      ;;
173
+  esac
174
+}
175
+
176
+################################################################################
177
+# TOOL: stack
178
+run_stack() {
179
+  prepare_nix_files
180
+  local stack_flags=()
181
+
182
+  if [ "$option_profiling" = true ]; then
183
+    if [ "${1:-build}" = build ]; then
184
+      stack_flags+=("--library-profiling")
185
+      stack_flags+=("--executable-profiling")
186
+    fi
187
+  fi
188
+
189
+  case "${1:-build}" in
190
+    shell)
191
+      nix_shell
192
+      ;;
193
+
194
+    *)
195
+      @stack@/bin/stack \
196
+        --nix --nix-shell-file=@stacknix@ \
197
+        --nix-shell-options "--argstr file $(pwd)/default.nix" \
198
+        "$@" "${stack_flags[@]}"
199
+      ;;
200
+  esac
201
+}
202
+
203
+################################################################################
204
+# TOOL: make
205
+run_make() {
206
+  case "${1:-build}" in
207
+    build)
208
+      make
209
+      ;;
210
+
211
+    *)
212
+      make "$@"
213
+      ;;
214
+  esac
215
+}
216
+
217
+################################################################################
218
+# Process the command line:
219
+while getopts "c:dhI:pn:t:" o; do
220
+  case "${o}" in
221
+    c) option_compiler=$(echo "$OPTARG" | tr -d '.')
222
+       ;;
223
+
224
+    d) option_debug=1
225
+       set -x
226
+       ;;
227
+
228
+    h) usage
229
+       exit
230
+       ;;
231
+
232
+    I) option_nixshell_args+=("-I" "$OPTARG")
233
+       ;;
234
+
235
+    p) option_profiling=true
236
+       ;;
237
+
238
+    n) option_nixshell_args+=("-I" "nixpkgs=$OPTARG")
239
+       ;;
240
+
241
+    t) option_tool=$OPTARG
242
+       ;;
243
+
244
+    *) exit 1
245
+       ;;
246
+  esac
247
+done
248
+
249
+shift $((OPTIND-1))
250
+
251
+################################################################################
252
+find_project
253
+
254
+################################################################################
255
+# For tools that treat this script like `cabal':
256
+if [ "${1:-build}" = cabal ] && [ "$#" -eq 2 ]; then shift; fi
257
+
258
+################################################################################
259
+# Figure out which tool we should be using:
260
+if [ -n "$option_tool" ]; then
261
+  tool=$option_tool
262
+else
263
+  if find_stack_root; then
264
+    tool=stack
265
+  elif [ -r Makefile ] || [ -r GNUmakefile ]; then
266
+    tool=make
267
+  else
268
+    tool=cabal
269
+  fi
270
+fi
271
+
272
+case "$tool" in
273
+  cabal)
274
+    : # No settings needed
275
+    ;;
276
+
277
+  stack)
278
+    : # No settings needed
279
+    ;;
280
+
281
+  make)
282
+    : # No settings needed
283
+    ;;
284
+
285
+  *)
286
+    die "unknown build tool: $tool"
287
+    ;;
288
+esac
289
+
290
+################################################################################
291
+# Main dispatch code:
292
+command="${1:-build}"
293
+if [ $# -gt 0 ]; then shift; fi
294
+if [ "${1:-}" = "--" ]; then shift; fi
295
+
296
+case "$command" in
297
+  new-build|build)
298
+    "run_${tool}" build "$@"
299
+    ;;
300
+
301
+  test)
302
+    "run_${tool}" test "$@"
303
+    ;;
304
+
305
+  clean)
306
+    "run_${tool}" clean "$@"
307
+    ;;
308
+
309
+  new-repl|repl)
310
+    "run_${tool}" repl "$@"
311
+    ;;
312
+
313
+  shell)
314
+    "run_${tool}" shell "$@"
315
+    ;;
316
+
317
+  *)
318
+    die "unknown command: $1"
319
+    ;;
320
+esac

+ 20
- 0
templates/default.nix View File

@@ -0,0 +1,20 @@
1
+{ pkgs ? (import <nixpkgs> {}).pkgs }:
2
+
3
+let
4
+  # List any extra packages you want available while your package is
5
+  # building or while in a nix shell:
6
+  extraPackages = with pkgs; [ ];
7
+
8
+  # Helpful if you want to override any Haskell packages:
9
+  haskell = pkgs.haskellPackages;
10
+in
11
+
12
+# Load the local nix file and use the overrides from above:
13
+haskell.callPackage ./@PROJECT@.nix {
14
+  mkDerivation = { buildTools ? []
15
+                 , ...
16
+                 }@args:
17
+    haskell.mkDerivation (args // {
18
+      buildTools = buildTools ++ extraPackages;
19
+    });
20
+}

Loading…
Cancel
Save