···11+Copyright (c) 2025, WeetHet
22+33+44+Redistribution and use in source and binary forms, with or without
55+modification, are permitted provided that the following conditions are met:
66+77+ * Redistributions of source code must retain the above copyright
88+ notice, this list of conditions and the following disclaimer.
99+1010+ * Redistributions in binary form must reproduce the above
1111+ copyright notice, this list of conditions and the following
1212+ disclaimer in the documentation and/or other materials provided
1313+ with the distribution.
1414+1515+ * Neither the name of the copyright holder nor the names of its
1616+ contributors may be used to endorse or promote products derived
1717+ from this software without specific prior written permission.
1818+1919+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
2020+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
2121+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
2222+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
2323+HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
2424+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
2525+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2626+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2727+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2828+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
2929+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+221
app/Main.hs
···11+module Main (main) where
22+33+import Options.Applicative
44+import Options.Applicative qualified as OA
55+import System.Environment (withArgs)
66+import System.IO (hPutStrLn)
77+import System.Posix (executeFile)
88+import System.Process (readProcess)
99+1010+data BuildOptions = BuildOptions
1111+ { buildFileish :: Maybe Text,
1212+ buildPackage :: Maybe Text,
1313+ buildArgs :: [(Text, Text)],
1414+ buildArgStrs :: [(Text, Text)],
1515+ buildAttrs :: [Text],
1616+ buildExprs :: [Text],
1717+ buildDryRun :: Bool,
1818+ buildIncludePaths :: [String],
1919+ buildOptions :: [(Text, Text)],
2020+ buildRepair :: Bool,
2121+ buildNoBuildOutput :: Bool,
2222+ buildMaxJobs :: Maybe Text,
2323+ buildCores :: Maybe Int,
2424+ buildMaxSilentTime :: Maybe Int,
2525+ buildTimeout :: Maybe Int,
2626+ buildKeepGoing :: Bool,
2727+ buildKeepFailed :: Bool,
2828+ buildFallback :: Bool,
2929+ buildReadonlyMode :: Bool,
3030+ buildVerbose :: Int,
3131+ buildQuiet :: Int,
3232+ buildLogFormat :: Maybe String,
3333+ buildCheck :: Bool
3434+ }
3535+ deriving (Show)
3636+3737+buildOptionsParser :: Parser BuildOptions
3838+buildOptionsParser =
3939+ BuildOptions
4040+ <$> optional (strArgument (metavar "fileish"))
4141+ <*> optional
4242+ ( strOption
4343+ ( long "package"
4444+ <> short 'p'
4545+ <> metavar "package"
4646+ <> help "Build a package by name (conflicts with fileish argument)"
4747+ )
4848+ )
4949+ <*> many
5050+ ( (,)
5151+ <$> strOption (long "arg" <> metavar "name" <> help "Pass a value for argument 'name'")
5252+ <*> strOption (metavar "value" <> help "Value for argument")
5353+ )
5454+ <*> many
5555+ ( (,)
5656+ <$> strOption (long "argstr" <> metavar "name" <> help "Pass a string value for argument 'name'")
5757+ <*> strOption (metavar "value" <> help "String value for argument")
5858+ )
5959+ <*> many
6060+ ( strOption
6161+ ( long "attr"
6262+ <> short 'A'
6363+ <> metavar "attrPath"
6464+ <> help "Select attribute to build"
6565+ )
6666+ )
6767+ <*> many
6868+ ( strOption
6969+ ( long "expr"
7070+ <> short 'E'
7171+ <> metavar "expr"
7272+ <> help "Interpret argument as a Nix expression"
7373+ )
7474+ )
7575+ <*> switch (long "dry-run" <> help "Show what would be built, without building")
7676+ <*> many
7777+ ( strOption
7878+ ( long "include"
7979+ <> short 'I'
8080+ <> metavar "[name=]path"
8181+ <> help "Add a path to the Nix expression search path"
8282+ )
8383+ )
8484+ <*> many
8585+ ( (,)
8686+ <$> strOption (long "option" <> metavar "name" <> help "Set Lix configuration option")
8787+ <*> strOption (metavar "value" <> help "Value for option")
8888+ )
8989+ <*> switch (long "repair" <> help "Fix corrupted or missing store paths by redownloading or rebuilding them")
9090+ <*> switch (long "no-build-output" <> short 'Q' <> help "Suppress output from builders to stderr")
9191+ <*> optional
9292+ ( strOption
9393+ ( long "max-jobs"
9494+ <> short 'j'
9595+ <> metavar "number"
9696+ <> help "Maximum number of build jobs (or 'auto')"
9797+ )
9898+ )
9999+ <*> optional
100100+ ( option
101101+ auto
102102+ ( long "cores"
103103+ <> metavar "number"
104104+ <> help "Number of CPU cores to use for building"
105105+ )
106106+ )
107107+ <*> optional
108108+ ( option
109109+ auto
110110+ ( long "max-silent-time"
111111+ <> metavar "seconds"
112112+ <> help "Maximum number of seconds a builder can go without producing output"
113113+ )
114114+ )
115115+ <*> optional
116116+ ( option
117117+ auto
118118+ ( long "timeout"
119119+ <> metavar "seconds"
120120+ <> help "Maximum number of seconds a builder can run"
121121+ )
122122+ )
123123+ <*> switch (long "keep-going" <> short 'k' <> help "Keep going in case of failed builds")
124124+ <*> switch (long "keep-failed" <> short 'K' <> help "Keep failed build directories")
125125+ <*> switch (long "fallback" <> help "Fall back on building if substitutes fail")
126126+ <*> switch (long "readonly-mode" <> help "Do not open the Lix database")
127127+ <*> (length <$> many (flag' () (long "verbose" <> short 'v' <> help "Increase verbosity")))
128128+ <*> (length <$> many (flag' () (long "quiet" <> help "Decrease verbosity")))
129129+ <*> optional
130130+ ( strOption
131131+ ( long "log-format"
132132+ <> metavar "format"
133133+ <> help "Change the output log format (raw, internal-json, bar, bar-with-logs, multiline, multiline-with-logs)"
134134+ )
135135+ )
136136+ <*> switch (long "check" <> help "Run the build in check mode (pass --check to nix-build)")
137137+138138+verifyNoConflicts :: BuildOptions -> IO BuildOptions
139139+verifyNoConflicts opts =
140140+ case (buildFileish opts, buildPackage opts) of
141141+ (Just _, Just _) -> do
142142+ hPutStrLn stderr "Error: --package/-p and fileish argument cannot be used together."
143143+ exitFailure
144144+ (Nothing, Nothing) -> do
145145+ hPutStrLn stderr "Error: You must specify either a fileish argument or --package/-p."
146146+ exitFailure
147147+ _ -> pure opts
148148+149149+nixBuildArgs :: BuildOptions -> [String]
150150+nixBuildArgs BuildOptions {..} =
151151+ concat
152152+ [ maybe [] (\f -> [toString f]) buildFileish,
153153+ maybe [] (\p -> ["-A", toString p]) buildPackage,
154154+ concatMap (\(n, v) -> ["--arg", toString n, toString v]) buildArgs,
155155+ concatMap (\(n, v) -> ["--argstr", toString n, toString v]) buildArgStrs,
156156+ concatMap (\a -> ["-A", toString a]) buildAttrs,
157157+ concatMap (\e -> ["-E", toString e]) buildExprs,
158158+ ["--dry-run" | buildDryRun],
159159+ concatMap (\i -> ["-I", i]) buildIncludePaths,
160160+ concatMap (\(n, v) -> ["--option", toString n, toString v]) buildOptions,
161161+ ["--repair" | buildRepair],
162162+ ["--no-build-output" | buildNoBuildOutput],
163163+ maybe [] (\j -> ["-j", toString j]) buildMaxJobs,
164164+ maybe [] (\c -> ["--cores", show c]) buildCores,
165165+ maybe [] (\s -> ["--max-silent-time", show s]) buildMaxSilentTime,
166166+ maybe [] (\t -> ["--timeout", show t]) buildTimeout,
167167+ ["--keep-going" | buildKeepGoing],
168168+ ["--keep-failed" | buildKeepFailed],
169169+ ["--fallback" | buildFallback],
170170+ ["--readonly-mode" | buildReadonlyMode],
171171+ replicate buildVerbose "--verbose",
172172+ replicate buildQuiet "--quiet",
173173+ maybe [] (\fmt -> ["--log-format", fmt]) buildLogFormat,
174174+ ["--check" | buildCheck]
175175+ ]
176176+177177+main :: IO ()
178178+main = do
179179+ args <- getArgs
180180+ let (ours, rest) = break (== "--") args
181181+ let passthrough = maybe [] tail (nonEmpty rest)
182182+183183+ withArgs ours $ do
184184+ opts_ <-
185185+ execParser $
186186+ info
187187+ (buildOptionsParser <**> helper)
188188+ (fullDesc <> progDesc "nix-run - run a Nix application" <> OA.header "nnix-run - run a Nix ")
189189+ opts <- verifyNoConflicts opts_
190190+191191+ let file = case buildPackage opts of
192192+ Just _ -> "<nixpkgs>"
193193+ Nothing -> fromMaybe "./." (buildFileish opts)
194194+195195+ let attr = case (buildAttrs opts, buildPackage opts) of
196196+ (a : _, _) -> Just a
197197+ ([], Just p) -> Just p
198198+ ([], Nothing) -> Nothing
199199+200200+ let attrSub = maybe "" ("." <>) attr
201201+ let expr =
202202+ unlines
203203+ [ "let",
204204+ " pkgs = import <nixpkgs> {};",
205205+ " drv = import (" <> file <> ");",
206206+ " drvResolved = if builtins.isFunction drv then drv {} else drv;",
207207+ "in pkgs.lib.getExe drvResolved" <> attrSub
208208+ ]
209209+ binPath <-
210210+ readProcess
211211+ "nix-instantiate"
212212+ ["--eval", "--raw", "-E", toString expr]
213213+ ""
214214+ let attrArgs = maybe [] (("-A" :) . one) attr
215215+ _ <-
216216+ readProcess
217217+ "nix-build"
218218+ ((++ nixBuildArgs opts) . map toString $ ["--no-out-link", file] ++ attrArgs)
219219+ ""
220220+221221+ executeFile binPath False passthrough Nothing