Setup.hs 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. {-# LANGUAGE CPP #-}
  2. {-# LANGUAGE NondecreasingIndentation #-}
  3. import Data.Maybe
  4. import Distribution.Simple
  5. import Distribution.Simple.LocalBuildInfo
  6. import Distribution.Simple.Setup
  7. import Distribution.Simple.BuildPaths (exeExtension)
  8. import Distribution.PackageDescription
  9. #if MIN_VERSION_Cabal(2,3,0)
  10. import Distribution.System ( buildPlatform )
  11. #endif
  12. import System.FilePath
  13. import System.Directory (makeAbsolute, removeFile)
  14. import System.Environment (getEnvironment)
  15. import System.Process
  16. import System.Exit
  17. import System.IO.Error (isDoesNotExistError)
  18. import Control.Monad (when, forM_, unless)
  19. import Control.Exception (catch, throwIO)
  20. main :: IO ()
  21. main = defaultMainWithHooks userhooks
  22. userhooks :: UserHooks
  23. userhooks = simpleUserHooks
  24. { copyHook = copyHook'
  25. , instHook = instHook'
  26. }
  27. -- Install and copy hooks are default, but amended with .agdai files in data-files.
  28. instHook' :: PackageDescription -> LocalBuildInfo -> UserHooks -> InstallFlags -> IO ()
  29. instHook' pd lbi hooks flags = instHook simpleUserHooks pd' lbi hooks flags where
  30. pd' = pd { dataFiles = concatMap expandAgdaExt $ dataFiles pd }
  31. -- Andreas, 2020-04-25, issue #4569: defer 'generateInterface' until after
  32. -- the library has been copied to a destination where it can be found.
  33. -- @cabal build@ will likely no longer produce the .agdai files, but @cabal install@ does.
  34. copyHook' :: PackageDescription -> LocalBuildInfo -> UserHooks -> CopyFlags -> IO ()
  35. copyHook' pd lbi hooks flags = do
  36. -- Copy library and executable etc.
  37. copyHook simpleUserHooks pd lbi hooks flags
  38. unless (skipInterfaces lbi) $ do
  39. -- Generate .agdai files.
  40. generateInterfaces pd lbi
  41. -- Copy again, now including the .agdai files.
  42. copyHook simpleUserHooks pd' lbi hooks flags
  43. where
  44. pd' = pd
  45. { dataFiles = concatMap expandAgdaExt $ dataFiles pd
  46. -- Andreas, 2020-04-25, issue #4569:
  47. -- I tried clearing some fields to avoid copying again.
  48. -- However, cabal does not like me messing with the PackageDescription.
  49. -- Clearing @library@ or @executables@ leads to internal errors.
  50. -- Thus, we just copy things again. Not a terrible problem.
  51. -- , library = Nothing
  52. -- , executables = []
  53. -- , subLibraries = []
  54. -- , foreignLibs = []
  55. -- , testSuites = []
  56. -- , benchmarks = []
  57. -- , extraSrcFiles = []
  58. -- , extraTmpFiles = []
  59. -- , extraDocFiles = []
  60. }
  61. -- Used to add .agdai files to data-files
  62. expandAgdaExt :: FilePath -> [FilePath]
  63. expandAgdaExt fp | takeExtension fp == ".agda" = [ fp, toIFile fp ]
  64. | otherwise = [ fp ]
  65. toIFile :: FilePath -> FilePath
  66. toIFile file = replaceExtension file ".agdai"
  67. -- Andreas, 2019-10-21, issue #4151:
  68. -- skip the generation of interface files with program suffix "-quicker"
  69. skipInterfaces :: LocalBuildInfo -> Bool
  70. skipInterfaces lbi = fromPathTemplate (progSuffix lbi) == "-quicker"
  71. generateInterfaces :: PackageDescription -> LocalBuildInfo -> IO ()
  72. generateInterfaces pd lbi = do
  73. -- for debugging, these are examples how you can inspect the flags...
  74. -- print $ flagAssignment lbi
  75. -- print $ fromPathTemplate $ progSuffix lbi
  76. -- then...
  77. let bdir = buildDir lbi
  78. agda = bdir </> "agda" </> "agda" <.> agdaExeExtension
  79. ddir <- makeAbsolute $ "src" </> "data"
  80. -- assuming we want to type check all .agda files in data-files
  81. -- current directory root of the package.
  82. putStrLn "Generating Agda library interface files..."
  83. forM_ (dataFiles pd) $ \fp -> when (takeExtension fp == ".agda") $ do
  84. let fullpath = ddir </> fp
  85. let fullpathi = toIFile fullpath
  86. -- remove existing interface file
  87. let handleExists e | isDoesNotExistError e = return ()
  88. | otherwise = throwIO e
  89. removeFile fullpathi `catch` handleExists
  90. putStrLn $ "... " ++ fullpath
  91. ok <- rawSystem' ddir agda [ "--no-libraries", "--local-interfaces"
  92. , "--ignore-all-interfaces"
  93. , "-Werror"
  94. , fullpath, "-v0"
  95. ]
  96. case ok of
  97. ExitSuccess -> return ()
  98. ExitFailure _ -> die $ "Error: Failed to typecheck " ++ fullpath ++ "!"
  99. agdaExeExtension :: String
  100. #if MIN_VERSION_Cabal(2,3,0)
  101. agdaExeExtension = exeExtension buildPlatform
  102. #else
  103. agdaExeExtension = exeExtension
  104. #endif
  105. rawSystem' :: FilePath -> String -> [String] -> IO ExitCode
  106. rawSystem' agda_datadir cmd args = do
  107. -- modify environment with Agda_datadir, so agda-executable will look
  108. -- for data-files in the right place
  109. e <- getEnvironment
  110. let e' = ("Agda_datadir", agda_datadir) : e
  111. (_,_,_,p) <- createProcess_ "rawSystem" (proc cmd args) { delegate_ctlc = True, env = Just e' }
  112. waitForProcess p