From 9ee3342e3e7d5b989c21e4889302aeefee9ff3e7 Mon Sep 17 00:00:00 2001 From: Matthew Bauer Date: Wed, 11 Sep 2019 17:43:27 -0400 Subject: [PATCH 01/16] Format long lines --- src/System/Which.hs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/System/Which.hs b/src/System/Which.hs index 5440f3f..92caf9a 100644 --- a/src/System/Which.hs +++ b/src/System/Which.hs @@ -7,13 +7,17 @@ import Language.Haskell.TH (Exp, Q, reportError, runIO) import Data.Monoid ((<>)) import Data.List (isPrefixOf) --- | Determine which executable would run if the given path were executed, or return Nothing if a suitable executable cannot be found +-- | Determine which executable would run if the given path were +-- executed, or return Nothing if a suitable executable cannot be +-- found which :: FilePath -> IO (Maybe FilePath) which f = fmap (fmap (T.unpack . Sh.toTextIgnore)) $ Sh.shelly $ Sh.which $ Sh.fromText $ T.pack f -- | Run `which` at compile time, and substitute the full path to the executable. -- --- This is useful in NixOS to ensure that the resulting executable contains the dependency in its closure and that it refers to the same version at run time as at compile time +-- This is useful in NixOS to ensure that the resulting executable +-- contains the dependency in its closure and that it refers to the +-- same version at run time as at compile time staticWhich :: FilePath -> Q Exp staticWhich f = do mf' <- runIO $ which f @@ -21,7 +25,9 @@ staticWhich f = do Nothing -> compileError $ "Could not find executable for " <> show f Just f' | "/nix/store/" `isPrefixOf` f' -> [| f' |] - | otherwise -> compileError $ "Path to executable " <> show f <> " was found in " <> show f' <> " which is not in /nix/store. Be sure to add the relevant package to 'backendTools' in default.nix." + | otherwise -> compileError $ + "Path to executable " <> show f <> " was found in " <> show f' + <> " which is not in /nix/store. Be sure to add the relevant package to 'backendTools' in default.nix." where compileError msg' = do From c9c16e2e557588e48bafbc48bbe44338d2b5af21 Mon Sep 17 00:00:00 2001 From: Matthew Bauer Date: Wed, 11 Sep 2019 17:43:58 -0400 Subject: [PATCH 02/16] Specify exports in System.Which --- src/System/Which.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/System/Which.hs b/src/System/Which.hs index 92caf9a..8b9e116 100644 --- a/src/System/Which.hs +++ b/src/System/Which.hs @@ -1,5 +1,5 @@ {-# LANGUAGE OverloadedStrings, TemplateHaskell #-} -module System.Which where +module System.Which (which, staticWhich) where import qualified Shelly as Sh import qualified Data.Text as T From bb693e132c609bea95d6160c229348ae5c4d2990 Mon Sep 17 00:00:00 2001 From: Matthew Bauer Date: Wed, 11 Sep 2019 17:44:42 -0400 Subject: [PATCH 03/16] Use HOST_PATH where available HOST_PATH is set in Nixpkgs during cross-compilation to detect binaries available at runtime. It makes sure you get the right architecture in your cross-compiled binaries. When HOST_PATH is not set, fall back to PATH. --- src/System/Which.hs | 36 ++++++++++++++++++++++++++++++++---- which.cabal | 6 ++++-- 2 files changed, 36 insertions(+), 6 deletions(-) diff --git a/src/System/Which.hs b/src/System/Which.hs index 8b9e116..f58f5dd 100644 --- a/src/System/Which.hs +++ b/src/System/Which.hs @@ -1,17 +1,45 @@ {-# LANGUAGE OverloadedStrings, TemplateHaskell #-} module System.Which (which, staticWhich) where -import qualified Shelly as Sh +import Control.Applicative +import Control.Monad (foldM) +import Control.Monad.Trans.Maybe +import Data.Bool (bool) +import Data.List (isPrefixOf) import qualified Data.Text as T -import Language.Haskell.TH (Exp, Q, reportError, runIO) import Data.Monoid ((<>)) -import Data.List (isPrefixOf) +import Language.Haskell.TH (Exp, Q, reportError, runIO) +import System.Directory (executable, getPermissions) +import System.Environment (lookupEnv) +import System.FilePath (searchPathSeparator) +import System.FilePath.Posix (()) +import System.IO.Error (catchIOError) + +-- | Is this path executable? +isExecutable :: FilePath -> IO Bool +isExecutable f = catchIOError (fmap executable $ getPermissions f) (const $ pure False) -- | Determine which executable would run if the given path were -- executed, or return Nothing if a suitable executable cannot be -- found which :: FilePath -> IO (Maybe FilePath) -which f = fmap (fmap (T.unpack . Sh.toTextIgnore)) $ Sh.shelly $ Sh.which $ Sh.fromText $ T.pack f +which f = do + path <- (runMaybeT $ MaybeT (lookupEnv "HOST_PATH") <|> MaybeT (lookupEnv "PATH")) + case path of + Just path -> do + fp <- lookupSearchPath f path + case fp of + Just fp' -> fmap (bool Nothing (Just fp')) $ isExecutable fp' + Nothing -> pure Nothing + Nothing -> pure Nothing + +-- | Lookup a path inside of a given search path. +lookupSearchPath :: FilePath -> String -> IO (Maybe FilePath) +lookupSearchPath f path = findPath $ T.split (== searchPathSeparator) $ T.pack path + where findPath [] = pure Nothing + findPath (x:xs) = + let fp = T.unpack x f + in bool (findPath xs) (pure $ Just fp) =<< isExecutable fp -- | Run `which` at compile time, and substitute the full path to the executable. -- diff --git a/which.cabal b/which.cabal index 0058c0e..ad50d41 100644 --- a/which.cabal +++ b/which.cabal @@ -18,9 +18,11 @@ library exposed-modules: System.Which build-depends: base >= 4.9.0 && < 4.13, - shelly >= 1.8.0 && < 1.10, + directory >= 1.0 && < 1.4, + filepath >= 1.0 && < 1.5, text >= 1.2.3 && < 1.3, - template-haskell >= 2.11.0 && < 2.15 + template-haskell >= 2.11.0 && < 2.15, + transformers >= 0.3.0.0 && < 0.6.0.0 hs-source-dirs: src default-language: Haskell2010 From b392f92b57cf4b46aadee4ac8f6fabdcda6d1188 Mon Sep 17 00:00:00 2001 From: Matthew Bauer Date: Wed, 11 Sep 2019 17:49:07 -0400 Subject: [PATCH 04/16] Add gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c33954f --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +dist-newstyle/ From 2c77380bed401e568423e31eca4f92355fb8fedc Mon Sep 17 00:00:00 2001 From: Matthew Bauer Date: Wed, 11 Sep 2019 18:26:31 -0400 Subject: [PATCH 05/16] Use findExecutablesInDirectories from directory This does what we want --- src/System/Which.hs | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/src/System/Which.hs b/src/System/Which.hs index f58f5dd..2b94bff 100644 --- a/src/System/Which.hs +++ b/src/System/Which.hs @@ -6,10 +6,11 @@ import Control.Monad (foldM) import Control.Monad.Trans.Maybe import Data.Bool (bool) import Data.List (isPrefixOf) -import qualified Data.Text as T +import Data.Maybe (listToMaybe) import Data.Monoid ((<>)) +import qualified Data.Text as T import Language.Haskell.TH (Exp, Q, reportError, runIO) -import System.Directory (executable, getPermissions) +import System.Directory (executable, getPermissions, findExecutablesInDirectories) import System.Environment (lookupEnv) import System.FilePath (searchPathSeparator) import System.FilePath.Posix (()) @@ -24,23 +25,14 @@ isExecutable f = catchIOError (fmap executable $ getPermissions f) (const $ pure -- found which :: FilePath -> IO (Maybe FilePath) which f = do - path <- (runMaybeT $ MaybeT (lookupEnv "HOST_PATH") <|> MaybeT (lookupEnv "PATH")) + path <- runMaybeT $ MaybeT (lookupEnv "HOST_PATH") <|> MaybeT (lookupEnv "PATH") case path of Just path -> do - fp <- lookupSearchPath f path - case fp of - Just fp' -> fmap (bool Nothing (Just fp')) $ isExecutable fp' - Nothing -> pure Nothing + fmap listToMaybe $ + findExecutablesInDirectories (fmap T.unpack $ + T.split (== searchPathSeparator) $ T.pack path) f Nothing -> pure Nothing --- | Lookup a path inside of a given search path. -lookupSearchPath :: FilePath -> String -> IO (Maybe FilePath) -lookupSearchPath f path = findPath $ T.split (== searchPathSeparator) $ T.pack path - where findPath [] = pure Nothing - findPath (x:xs) = - let fp = T.unpack x f - in bool (findPath xs) (pure $ Just fp) =<< isExecutable fp - -- | Run `which` at compile time, and substitute the full path to the executable. -- -- This is useful in NixOS to ensure that the resulting executable From dd815ee30d63c5272ad6f9f1c1070caa6769f233 Mon Sep 17 00:00:00 2001 From: Matthew Bauer Date: Wed, 11 Sep 2019 18:35:09 -0400 Subject: [PATCH 06/16] Add test suite --- test/Spec.hs | 87 ++++++++++++++++++++++++++++++++++++++++++++++++++++ which.cabal | 11 +++++++ 2 files changed, 98 insertions(+) create mode 100644 test/Spec.hs diff --git a/test/Spec.hs b/test/Spec.hs new file mode 100644 index 0000000..d33786d --- /dev/null +++ b/test/Spec.hs @@ -0,0 +1,87 @@ +import Control.Exception +import Control.Monad +import Data.List +import System.Directory +import System.Environment +import System.Exit +import System.FilePath +import System.Posix.Temp +import System.Which + +shouldEqual :: Eq a => Show a => a -> a -> IO () +shouldEqual a b = when (a /= b) $ do + putStrLn $ show a <> " does not equal " <> show b + exitFailure + +setup :: IO (FilePath, FilePath, FilePath) +setup = do + bin <- mkdtemp "bin" + bin2 <- mkdtemp "bin2" + bin3 <- mkdtemp "bin3" + + let makeExecutable f = setPermissions f . setOwnerExecutable True =<< getPermissions f + + appendFile (bin "hello") "hello" + makeExecutable $ bin "hello" + appendFile (bin2 "hello") "hello2" + makeExecutable $ bin2 "hello" + appendFile (bin2 "hello2") "hello2" + makeExecutable $ bin2 "hello2" + appendFile (bin3 "hello") "hello" + makeExecutable $ bin3 "hello" + appendFile (bin3 "hello2") "hello2" + -- makeExecutable $ bin3 "hello2" + appendFile (bin3 "hello3") "hello3" + makeExecutable $ bin3 "hello3" + + pure (bin, bin2, bin3) + +cleanup :: (FilePath, FilePath, FilePath) -> IO () +cleanup (bin, bin2, bin3) = do + removeDirectoryRecursive bin + removeDirectoryRecursive bin2 + removeDirectoryRecursive bin3 + +main :: IO () +main = do + bracket setup cleanup $ \(bin, bin2, bin3) -> do + unsetEnv "PATH" + unsetEnv "HOST_PATH" + + fp <- which "hello" + + shouldEqual fp Nothing + + setEnv "PATH" $ concat $ intersperse ":" [bin, bin2, bin3] + setEnv "HOST_PATH" $ concat $ intersperse ":" [bin, bin2] + + fp1 <- which "hello" + fp2 <- which "hello2" + fp3 <- which "hello3" + + shouldEqual fp1 (Just $ bin "hello") + shouldEqual fp2 (Just $ bin2 "hello2") + shouldEqual fp3 Nothing + + unsetEnv "PATH" + + fp4 <- which "hello" + fp5 <- which "hello2" + fp6 <- which "hello3" + + shouldEqual fp4 (Just $ bin "hello") + shouldEqual fp5 (Just $ bin2 "hello2") + shouldEqual fp6 Nothing + + unsetEnv "HOST_PATH" + setEnv "PATH" $ concat $ intersperse ":" [bin3, bin2, bin] + + fp7 <- which "hello" + fp8 <- which "hello2" + fp9 <- which "hello3" + + shouldEqual fp7 (Just $ bin3 "hello") + shouldEqual fp8 (Just $ bin2 "hello2") + shouldEqual fp9 (Just $ bin3 "hello3") + + pure () diff --git a/which.cabal b/which.cabal index ad50d41..4197529 100644 --- a/which.cabal +++ b/which.cabal @@ -27,6 +27,17 @@ library hs-source-dirs: src default-language: Haskell2010 +test-suite spec + type: exitcode-stdio-1.0 + main-is: Spec.hs + hs-source-dirs: test + build-depends: + base, + directory, + filepath, + unix, + which + source-repository head type: git location: https://github.com/obsidiansystems/which From 19e1c4727a1e614f0ba71ba261389df6ff911325 Mon Sep 17 00:00:00 2001 From: Matthew Bauer Date: Wed, 11 Sep 2019 18:38:52 -0400 Subject: [PATCH 07/16] Bump version to 0.2.0.0 --- which.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/which.cabal b/which.cabal index 4197529..648bb62 100644 --- a/which.cabal +++ b/which.cabal @@ -1,5 +1,5 @@ name: which -version: 0.1.0.0 +version: 0.2.0.0 license: BSD3 license-file: LICENSE author: Obsidian Systems LLC From 0ebf7c07d9fac06403e80960bec4e33f6b535fad Mon Sep 17 00:00:00 2001 From: Matthew Bauer Date: Wed, 11 Sep 2019 18:41:41 -0400 Subject: [PATCH 08/16] Cleanup --- src/System/Which.hs | 17 ++++------------- which.cabal | 5 ++++- 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/src/System/Which.hs b/src/System/Which.hs index 2b94bff..5dd5d92 100644 --- a/src/System/Which.hs +++ b/src/System/Which.hs @@ -2,23 +2,15 @@ module System.Which (which, staticWhich) where import Control.Applicative -import Control.Monad (foldM) import Control.Monad.Trans.Maybe -import Data.Bool (bool) import Data.List (isPrefixOf) import Data.Maybe (listToMaybe) import Data.Monoid ((<>)) import qualified Data.Text as T import Language.Haskell.TH (Exp, Q, reportError, runIO) -import System.Directory (executable, getPermissions, findExecutablesInDirectories) +import System.Directory (findExecutablesInDirectories) import System.Environment (lookupEnv) import System.FilePath (searchPathSeparator) -import System.FilePath.Posix (()) -import System.IO.Error (catchIOError) - --- | Is this path executable? -isExecutable :: FilePath -> IO Bool -isExecutable f = catchIOError (fmap executable $ getPermissions f) (const $ pure False) -- | Determine which executable would run if the given path were -- executed, or return Nothing if a suitable executable cannot be @@ -27,10 +19,9 @@ which :: FilePath -> IO (Maybe FilePath) which f = do path <- runMaybeT $ MaybeT (lookupEnv "HOST_PATH") <|> MaybeT (lookupEnv "PATH") case path of - Just path -> do - fmap listToMaybe $ - findExecutablesInDirectories (fmap T.unpack $ - T.split (== searchPathSeparator) $ T.pack path) f + Just path' -> fmap listToMaybe $ + findExecutablesInDirectories (fmap T.unpack $ + T.split (== searchPathSeparator) $ T.pack path') f Nothing -> pure Nothing -- | Run `which` at compile time, and substitute the full path to the executable. diff --git a/which.cabal b/which.cabal index 648bb62..54f5079 100644 --- a/which.cabal +++ b/which.cabal @@ -25,18 +25,21 @@ library transformers >= 0.3.0.0 && < 0.6.0.0 hs-source-dirs: src + ghc-options: -Wall default-language: Haskell2010 test-suite spec type: exitcode-stdio-1.0 main-is: Spec.hs - hs-source-dirs: test build-depends: base, directory, filepath, unix, which + hs-source-dirs: test + ghc-options: -Wall + default-language: Haskell2010 source-repository head type: git From 16cdd63a66f35b2f0a66d79175000fae82899d93 Mon Sep 17 00:00:00 2001 From: Matthew Bauer Date: Wed, 11 Sep 2019 18:47:23 -0400 Subject: [PATCH 09/16] Tighten directory bounds findExecutablesInDirectories was only added in 1.2.4.0. --- which.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/which.cabal b/which.cabal index 54f5079..372e09e 100644 --- a/which.cabal +++ b/which.cabal @@ -18,7 +18,7 @@ library exposed-modules: System.Which build-depends: base >= 4.9.0 && < 4.13, - directory >= 1.0 && < 1.4, + directory >= 1.2.4.0 && < 1.4, filepath >= 1.0 && < 1.5, text >= 1.2.3 && < 1.3, template-haskell >= 2.11.0 && < 2.15, From 88a23fb97b4f2728794abc64513c1767ff5be554 Mon Sep 17 00:00:00 2001 From: Matthew Bauer Date: Wed, 11 Sep 2019 18:52:10 -0400 Subject: [PATCH 10/16] Cleanup test suite --- test/Spec.hs | 68 ++++++++++++++++++++++++++-------------------------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/test/Spec.hs b/test/Spec.hs index d33786d..56f5026 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -1,5 +1,4 @@ import Control.Exception -import Control.Monad import Data.List import System.Directory import System.Environment @@ -9,9 +8,13 @@ import System.Posix.Temp import System.Which shouldEqual :: Eq a => Show a => a -> a -> IO () -shouldEqual a b = when (a /= b) $ do +shouldEqual a b | a /= b = do putStrLn $ show a <> " does not equal " <> show b exitFailure +shouldEqual a b = putStrLn $ show a <> " == " <> show b + +makeExecutable :: FilePath -> IO () +makeExecutable f = setPermissions f . setOwnerExecutable True =<< getPermissions f setup :: IO (FilePath, FilePath, FilePath) setup = do @@ -19,8 +22,6 @@ setup = do bin2 <- mkdtemp "bin2" bin3 <- mkdtemp "bin3" - let makeExecutable f = setPermissions f . setOwnerExecutable True =<< getPermissions f - appendFile (bin "hello") "hello" makeExecutable $ bin "hello" appendFile (bin2 "hello") "hello2" @@ -43,45 +44,44 @@ cleanup (bin, bin2, bin3) = do removeDirectoryRecursive bin3 main :: IO () -main = do - bracket setup cleanup $ \(bin, bin2, bin3) -> do - unsetEnv "PATH" - unsetEnv "HOST_PATH" +main = bracket setup cleanup $ \(bin, bin2, bin3) -> do + unsetEnv "PATH" + unsetEnv "HOST_PATH" - fp <- which "hello" + fp <- which "hello" - shouldEqual fp Nothing + shouldEqual fp Nothing - setEnv "PATH" $ concat $ intersperse ":" [bin, bin2, bin3] - setEnv "HOST_PATH" $ concat $ intersperse ":" [bin, bin2] + setEnv "PATH" $ concat $ intersperse ":" [bin, bin2, bin3] + setEnv "HOST_PATH" $ concat $ intersperse ":" [bin, bin2] - fp1 <- which "hello" - fp2 <- which "hello2" - fp3 <- which "hello3" + fp1 <- which "hello" + fp2 <- which "hello2" + fp3 <- which "hello3" - shouldEqual fp1 (Just $ bin "hello") - shouldEqual fp2 (Just $ bin2 "hello2") - shouldEqual fp3 Nothing + shouldEqual fp1 (Just $ bin "hello") + shouldEqual fp2 (Just $ bin2 "hello2") + shouldEqual fp3 Nothing - unsetEnv "PATH" + unsetEnv "PATH" - fp4 <- which "hello" - fp5 <- which "hello2" - fp6 <- which "hello3" + fp4 <- which "hello" + fp5 <- which "hello2" + fp6 <- which "hello3" - shouldEqual fp4 (Just $ bin "hello") - shouldEqual fp5 (Just $ bin2 "hello2") - shouldEqual fp6 Nothing + shouldEqual fp4 (Just $ bin "hello") + shouldEqual fp5 (Just $ bin2 "hello2") + shouldEqual fp6 Nothing - unsetEnv "HOST_PATH" - setEnv "PATH" $ concat $ intersperse ":" [bin3, bin2, bin] + unsetEnv "HOST_PATH" + setEnv "PATH" $ concat $ intersperse ":" [bin3, bin2, bin] - fp7 <- which "hello" - fp8 <- which "hello2" - fp9 <- which "hello3" + fp7 <- which "hello" + fp8 <- which "hello2" + fp9 <- which "hello3" - shouldEqual fp7 (Just $ bin3 "hello") - shouldEqual fp8 (Just $ bin2 "hello2") - shouldEqual fp9 (Just $ bin3 "hello3") + shouldEqual fp7 (Just $ bin3 "hello") + shouldEqual fp8 (Just $ bin2 "hello2") + shouldEqual fp9 (Just $ bin3 "hello3") - pure () + pure () From a437030e52e4c9421e741637d8d1bf9fd1528c2f Mon Sep 17 00:00:00 2001 From: Matthew Bauer Date: Thu, 12 Sep 2019 10:31:16 -0400 Subject: [PATCH 11/16] Fix missing mappend in pre-8.4 ghc --- test/Spec.hs | 1 + 1 file changed, 1 insertion(+) diff --git a/test/Spec.hs b/test/Spec.hs index 56f5026..d71ce7a 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -1,5 +1,6 @@ import Control.Exception import Data.List +import Data.Monoid ((<>)) import System.Directory import System.Environment import System.Exit From f3e516bc49a60fa98a54bafbcdec41e5f1de8b13 Mon Sep 17 00:00:00 2001 From: Matthew Bauer Date: Thu, 12 Sep 2019 10:34:39 -0400 Subject: [PATCH 12/16] Update template haskell error --- src/System/Which.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/System/Which.hs b/src/System/Which.hs index 5dd5d92..569ac25 100644 --- a/src/System/Which.hs +++ b/src/System/Which.hs @@ -38,7 +38,7 @@ staticWhich f = do | "/nix/store/" `isPrefixOf` f' -> [| f' |] | otherwise -> compileError $ "Path to executable " <> show f <> " was found in " <> show f' - <> " which is not in /nix/store. Be sure to add the relevant package to 'backendTools' in default.nix." + <> " which is not in /nix/store. Be sure to add the relevant package to default.nix." where compileError msg' = do From e22ae840b08acc734615dce0ed93d364fb278886 Mon Sep 17 00:00:00 2001 From: Matthew Bauer Date: Thu, 12 Sep 2019 10:58:31 -0400 Subject: [PATCH 13/16] Add comment on bin3/hello2 --- test/Spec.hs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/Spec.hs b/test/Spec.hs index d71ce7a..6021c0f 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -32,7 +32,11 @@ setup = do appendFile (bin3 "hello") "hello" makeExecutable $ bin3 "hello" appendFile (bin3 "hello2") "hello2" + + -- Don’t make this one executable to make sure we don’t resolve + -- non-executable exes below. -- makeExecutable $ bin3 "hello2" + appendFile (bin3 "hello3") "hello3" makeExecutable $ bin3 "hello3" From 571d5774f490b8c946ad3f2243868e44ae06d8be Mon Sep 17 00:00:00 2001 From: Matthew Bauer Date: Thu, 12 Sep 2019 10:34:39 -0400 Subject: [PATCH 14/16] Update template haskell error --- src/System/Which.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/System/Which.hs b/src/System/Which.hs index 8b9e116..f176366 100644 --- a/src/System/Which.hs +++ b/src/System/Which.hs @@ -27,7 +27,7 @@ staticWhich f = do | "/nix/store/" `isPrefixOf` f' -> [| f' |] | otherwise -> compileError $ "Path to executable " <> show f <> " was found in " <> show f' - <> " which is not in /nix/store. Be sure to add the relevant package to 'backendTools' in default.nix." + <> " which is not in /nix/store. Be sure to add the relevant package to default.nix." where compileError msg' = do From 04da6f309b0fbe256bb8235c7bf030676d1fd822 Mon Sep 17 00:00:00 2001 From: Matthew Bauer Date: Thu, 12 Sep 2019 13:56:10 -0400 Subject: [PATCH 15/16] =?UTF-8?q?Don=E2=80=99t=20require=20which=20results?= =?UTF-8?q?=20to=20be=20in=20/nix/store?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #2 --- src/System/Which.hs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/System/Which.hs b/src/System/Which.hs index f176366..bc26c26 100644 --- a/src/System/Which.hs +++ b/src/System/Which.hs @@ -23,11 +23,7 @@ staticWhich f = do mf' <- runIO $ which f case mf' of Nothing -> compileError $ "Could not find executable for " <> show f - Just f' - | "/nix/store/" `isPrefixOf` f' -> [| f' |] - | otherwise -> compileError $ - "Path to executable " <> show f <> " was found in " <> show f' - <> " which is not in /nix/store. Be sure to add the relevant package to default.nix." + Just f' -> [| f' |] where compileError msg' = do From 68cb31f7ec8f4e708b3074368c1432c4b7772dbd Mon Sep 17 00:00:00 2001 From: Matthew Bauer Date: Wed, 20 Nov 2019 16:30:53 -0500 Subject: [PATCH 16/16] Add fallback when Nix path does not exist MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We do not always know at build-time where we will get an executable. For instance, on Nix-less systems, the Nix store will be unavailable and we need to get it some other way. We could hardcode /usr/local/bin/ to PATH so that GHC will find it, but this introduces unnecessary impurities and requires changing hashes between builds. As a workaround, just use classic “which” functionality. --- src/System/Which.hs | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/System/Which.hs b/src/System/Which.hs index 494aefc..9feaf99 100644 --- a/src/System/Which.hs +++ b/src/System/Which.hs @@ -3,12 +3,11 @@ module System.Which (which, staticWhich) where import Control.Applicative import Control.Monad.Trans.Maybe -import Data.List (isPrefixOf) import Data.Maybe (listToMaybe) import Data.Monoid ((<>)) import qualified Data.Text as T import Language.Haskell.TH (Exp, Q, reportError, runIO) -import System.Directory (findExecutablesInDirectories) +import System.Directory (findExecutablesInDirectories, doesFileExist) import System.Environment (lookupEnv) import System.FilePath (searchPathSeparator) @@ -34,7 +33,18 @@ staticWhich f = do mf' <- runIO $ which f case mf' of Nothing -> compileError $ "Could not find executable for " <> show f - Just f' -> [| f' |] + Just f' -> [| do + -- Check if the file actually exists at runtime + exists <- doesFileExist f' + + -- If it does, run it, otherwise fallback to classic which. + if exists then pure f' + else do + result <- which f + case result of + Just v -> pure v + Nothing -> error $ "Could not find executable for " <> show f + |] where compileError msg' = do