|
1 |
| -use anyhow::{Context, Result}; |
2 |
| -use clap::Parser; |
3 |
| -use nix_script_directives::Directives; |
4 |
| -use std::path::PathBuf; |
5 |
| -use std::process::{Command, ExitStatus}; |
6 |
| - |
7 |
| -/// `nix-script-haskell` is a wrapper around `nix-script` with options for |
8 |
| -/// scripts written in Haskell. |
9 |
| -/// |
10 |
| -/// I pay attention to all the same #! directives as nix-script, so you can |
11 |
| -/// still use `#!runtimeInputs` and friends to get external dependencies. (There |
12 |
| -/// is no need to specify `#!build` or `#!buildInputs` with regards to GHC or |
13 |
| -/// packages, though; I take care of that.) |
14 |
| -/// |
15 |
| -/// In addition, I pay attention to some additional directives specific to |
16 |
| -/// Haskell programs: |
17 |
| -/// |
18 |
| -/// `#!haskellPackages` should contain a list of packages the compiling GHC |
19 |
| -/// instance will know about. The available set of packages depends on your |
20 |
| -/// Nix installation; look in `haskellPackages` on `search.nixos.org` to get a |
21 |
| -/// full list. |
22 |
| -/// |
23 |
| -/// `#!ghcFlags` should be a string of command-line options to pass to `ghc` |
24 |
| -/// when compiling. |
25 |
| -#[derive(Debug, Parser)] |
26 |
| -#[clap(version, trailing_var_arg = true)] |
27 |
| -struct Opts { |
28 |
| - /// Launch a ghcid session watching the script |
29 |
| - #[clap(long, conflicts_with("shell"))] |
30 |
| - ghcid: bool, |
31 |
| - |
32 |
| - /// Enter a shell with all script dependencies |
33 |
| - #[clap(long, conflicts_with("ghcid"))] |
34 |
| - shell: bool, |
35 |
| - |
36 |
| - /// In shell mode, run this command instead of a shell. |
37 |
| - #[clap(long, requires("shell"))] |
38 |
| - run: Option<String>, |
39 |
| - |
40 |
| - /// In shell mode, run a "pure" shell (that is, one that isolates the |
41 |
| - /// shell a little more from what you have in your environment.) |
42 |
| - #[clap(long, requires("shell"))] |
43 |
| - pure: bool, |
44 |
| - |
45 |
| - #[clap(long, default_value("nix-script"), hide(true))] |
46 |
| - nix_script_bin: PathBuf, |
47 |
| - |
48 |
| - /// The script and arguments to pass to nix-script |
49 |
| - #[arg(num_args = 1.., required = true)] |
50 |
| - script_and_arguments: Vec<String>, |
51 |
| -} |
52 |
| - |
53 |
| -impl Opts { |
54 |
| - fn run(&self) -> Result<ExitStatus> { |
55 |
| - let (script, args) = self |
56 |
| - .get_script_and_arguments() |
57 |
| - .context("could not get script and arguments")?; |
58 |
| - |
59 |
| - let directives = Directives::from_file("#!", &script) |
60 |
| - .context("could not parse directives from script")?; |
61 |
| - |
62 |
| - let mut nix_script = Command::new(&self.nix_script_bin); |
63 |
| - |
64 |
| - let build_command = format!( |
65 |
| - "mv $SRC $SRC.hs; ghc {} -o $OUT $SRC.hs", |
66 |
| - directives |
67 |
| - .all |
68 |
| - .get("ghcFlags") |
69 |
| - .map(|ps| ps.join(" ")) |
70 |
| - .unwrap_or_default() |
71 |
| - ); |
72 |
| - log::debug!("build command is `{}`", build_command); |
73 |
| - nix_script.arg("--build-command").arg(build_command); |
| 1 | +mod opts; |
74 | 2 |
|
75 |
| - let compiler = format!( |
76 |
| - "haskellPackages.ghcWithPackages (ps: with ps; [ {} ])", |
77 |
| - directives |
78 |
| - .all |
79 |
| - .get("haskellPackages") |
80 |
| - .map(|ps| ps.join(" ")) |
81 |
| - .unwrap_or_default() |
82 |
| - ); |
83 |
| - log::debug!("compiler is `{}`", &compiler); |
84 |
| - nix_script.arg("--build-input").arg(compiler); |
85 |
| - |
86 |
| - if self.shell { |
87 |
| - log::debug!("entering shell mode"); |
88 |
| - nix_script.arg("--shell"); |
89 |
| - } else if self.ghcid { |
90 |
| - log::debug!("entering ghcid mode"); |
91 |
| - nix_script |
92 |
| - .arg("--shell") |
93 |
| - .arg("--runtime-input") |
94 |
| - .arg("ghcid") |
95 |
| - .arg("--run") |
96 |
| - .arg(format!("ghcid {}", script.display())); |
97 |
| - } |
98 |
| - |
99 |
| - nix_script.arg(script); |
100 |
| - nix_script.args(args); |
101 |
| - |
102 |
| - let mut child = nix_script.spawn().with_context(|| { |
103 |
| - format!( |
104 |
| - "could not call {}. Is it on the PATH?", |
105 |
| - self.nix_script_bin.display() |
106 |
| - ) |
107 |
| - })?; |
108 |
| - |
109 |
| - child.wait().context("could not run the script") |
110 |
| - } |
111 |
| - |
112 |
| - fn get_script_and_arguments(&self) -> Result<(PathBuf, Vec<String>)> { |
113 |
| - log::trace!("parsing script and args"); |
114 |
| - |
115 |
| - let script = PathBuf::from( |
116 |
| - self.script_and_arguments |
117 |
| - .first() |
118 |
| - .context("no script to run; this is a bug; please report")?, |
119 |
| - ); |
120 |
| - |
121 |
| - Ok((script, self.script_and_arguments[1..].to_vec())) |
122 |
| - } |
123 |
| -} |
| 3 | +use clap::Parser; |
| 4 | +use opts::Opts; |
124 | 5 |
|
125 | 6 | fn main() {
|
126 | 7 | env_logger::Builder::from_env("NIX_SCRIPT_LOG").init();
|
|
0 commit comments