|
1 |
| -import Distribution.Simple ( defaultMainArgs ) |
| 1 | +import Distribution.Simple ( defaultMainWithHooksArgs, simpleUserHooks ) |
2 | 2 | import System.Environment ( getArgs )
|
3 | 3 |
|
| 4 | + |
| 5 | +-- Source copied from: https://hackage.haskell.org/package/cabal-pkg-config-version-hook-0.1.0.1/docs/src/Distribution.PkgConfigVersionHook.html#addHook |
| 6 | +-- TODO: Import this via `setup-depends` instead once it's on Stackage. |
| 7 | + |
| 8 | +import Control.Lens ((%~), (^.)) |
| 9 | +import Control.Monad (when) |
| 10 | +import qualified Data.Char as C |
| 11 | +import Data.Foldable (toList) |
| 12 | +import Data.Function ((&)) |
| 13 | +import qualified Data.List as L |
| 14 | +import Distribution.Simple (UserHooks (confHook)) |
| 15 | +import Distribution.Simple.Setup (ConfigFlags, configConfigurationsFlags) |
| 16 | +import Distribution.Types.BuildInfo.Lens (ccOptions, cppOptions, cxxOptions) |
| 17 | +import Distribution.Types.Flag (flagName, mkFlagAssignment, mkFlagName, unFlagName) |
| 18 | +import Distribution.Types.GenericPackageDescription.Lens |
| 19 | + ( GenericPackageDescription, |
| 20 | + condBenchmarks, |
| 21 | + condExecutables, |
| 22 | + condForeignLibs, |
| 23 | + condLibrary, |
| 24 | + condSubLibraries, |
| 25 | + condTestSuites, |
| 26 | + genPackageFlags, |
| 27 | + ) |
| 28 | +import System.IO (hPutStrLn, stderr) |
| 29 | +import System.Process (readProcess) |
| 30 | +import qualified Text.ParserCombinators.ReadP as P |
| 31 | +import Prelude hiding (log) |
| 32 | + |
| 33 | +-- | Hook into Cabal to provide pkg-config metadata. Can be applied multiple |
| 34 | +-- times to support multiple packages. |
| 35 | +addHook :: Settings -> UserHooks -> UserHooks |
| 36 | +addHook settings hooks = hooks {confHook = composeConfHook settings (confHook hooks)} |
| 37 | + |
| 38 | +-- | How the metadata for a pkg-config package should be made available to the |
| 39 | +-- cabal file. |
| 40 | +data Settings = Settings |
| 41 | + { -- | Name of the package; used for querying pkg-config. |
| 42 | + pkgConfigName :: String, |
| 43 | + -- | Name to use in the Haskell CPP and C/C++ preprocessor macros. |
| 44 | + -- |
| 45 | + -- For example, `pkgConfigName = "FOO"` will set the macros |
| 46 | + -- |
| 47 | + -- * @FOO_MAJOR@ |
| 48 | + -- |
| 49 | + -- * @FOO_MINOR@ |
| 50 | + -- |
| 51 | + -- * @FOO_PATCH@ |
| 52 | + -- |
| 53 | + -- * @FOO_IS_AT_LEAST(major, minor, patch)@ |
| 54 | + macroName :: String, |
| 55 | + -- | Name to use when setting flag values in the cabal file. |
| 56 | + -- |
| 57 | + -- Flags named with this prefix, followed by a dash, followed by a major version number, an underscore and a minor version number will be set when the detected package is at least that version. |
| 58 | + flagPrefixName :: String |
| 59 | + } |
| 60 | + |
| 61 | +-- | Derive a default 'Settings' value from just a pkg-config package name. |
| 62 | +mkSettings :: String -> Settings |
| 63 | +mkSettings name = |
| 64 | + Settings |
| 65 | + { pkgConfigName = name, |
| 66 | + macroName = map (\c -> case c of '-' -> '_'; x -> x) name, |
| 67 | + flagPrefixName = name |
| 68 | + } |
| 69 | + |
| 70 | +-- | Extend the value of 'confHook'. It's what powers 'addHook'. |
| 71 | +composeConfHook :: |
| 72 | + Settings -> |
| 73 | + ((GenericPackageDescription, a) -> ConfigFlags -> IO b) -> |
| 74 | + (GenericPackageDescription, a) -> |
| 75 | + Distribution.Simple.Setup.ConfigFlags -> |
| 76 | + IO b |
| 77 | +composeConfHook settings origHook = \(genericPackageDescription, hookedBuildInfo) confFlags -> do |
| 78 | + (actualMajor, actualMinor, actualPatch) <- getPkgConfigPackageVersion (pkgConfigName settings) |
| 79 | + |
| 80 | + let defines = |
| 81 | + [ "-D" <> macroName settings <> "_MAJOR=" <> show actualMajor, |
| 82 | + "-D" <> macroName settings <> "_MINOR=" <> show actualMinor, |
| 83 | + "-D" <> macroName settings <> "_PATCH=" <> show actualPatch, |
| 84 | + "-D" <> macroName settings <> "_IS_AT_LEAST(a,b,c)=(" <> show actualMajor <> ">a||(" <> show actualMajor <> "==a&&(" <> show actualMinor <> ">b||(" <> show actualMinor <> "==b&&" <> show actualPatch <> ">=c))))" |
| 85 | + ] |
| 86 | + extraFlags = |
| 87 | + [ (mkFlagName (flagPrefixName settings ++ "-" ++ show major ++ "_" ++ show minor), (actualMajor, actualMinor) >= (major, minor)) |
| 88 | + | declaredFlag <- genericPackageDescription ^. genPackageFlags, |
| 89 | + let rawName = unFlagName $ flagName declaredFlag, |
| 90 | + rawVersion <- L.stripPrefix (flagPrefixName settings ++ "-") rawName & toList, |
| 91 | + [major, minor] <- unambiguously parseFlagVersion rawVersion & toList |
| 92 | + ] |
| 93 | + setDefines comp x = |
| 94 | + x |
| 95 | + & comp . cppOptions %~ (<> defines) |
| 96 | + & comp . ccOptions %~ (<> defines) |
| 97 | + & comp . cxxOptions %~ (<> defines) |
| 98 | + genericPackageDescription' = |
| 99 | + genericPackageDescription |
| 100 | + & setDefines (condLibrary . traverse . traverse) |
| 101 | + & setDefines (condSubLibraries . traverse . traverse . traverse) |
| 102 | + & setDefines (condForeignLibs . traverse . traverse . traverse) |
| 103 | + & setDefines (condExecutables . traverse . traverse . traverse) |
| 104 | + & setDefines (condTestSuites . traverse . traverse . traverse) |
| 105 | + & setDefines (condBenchmarks . traverse . traverse . traverse) |
| 106 | + |
| 107 | + configConfigurationsFlags' = configConfigurationsFlags confFlags `mappend` mkFlagAssignment extraFlags |
| 108 | + confFlags' = |
| 109 | + confFlags |
| 110 | + { configConfigurationsFlags = configConfigurationsFlags' |
| 111 | + } |
| 112 | + origHook (genericPackageDescription', hookedBuildInfo) confFlags' |
| 113 | + |
| 114 | +parseVersion :: P.ReadP [Int] |
| 115 | +parseVersion = do |
| 116 | + map read <$> do |
| 117 | + P.many1 (P.satisfy C.isDigit) `P.sepBy` P.char '.' |
| 118 | + |
| 119 | +parseFlagVersion :: P.ReadP [Int] |
| 120 | +parseFlagVersion = |
| 121 | + map read <$> do |
| 122 | + P.many1 (P.satisfy C.isDigit) `P.sepBy` P.char '_' |
| 123 | + |
| 124 | +unambiguously :: P.ReadP a -> String -> Maybe a |
| 125 | +unambiguously p s = |
| 126 | + case filter (\(_a, x) -> x == "") $ P.readP_to_S p s of |
| 127 | + [(v, _)] -> Just v |
| 128 | + _ -> Nothing |
| 129 | + |
| 130 | +getPkgConfigPackageVersion :: String -> IO (Int, Int, Int) |
| 131 | +getPkgConfigPackageVersion pkgName = do |
| 132 | + s <- readProcess "pkg-config" ["--modversion", pkgName] "" |
| 133 | + case L.sortOn (\(_, remainder) -> length remainder) $ P.readP_to_S parseVersion s of |
| 134 | + [] -> error ("Could not parse version " ++ show s ++ " returned by pkg-config for package " ++ pkgName) |
| 135 | + (v, r) : _ -> do |
| 136 | + when (L.dropWhile C.isSpace r /= "") $ do |
| 137 | + log ("ignoring trailing text " ++ show r ++ " in version " ++ show s ++ " of pkg-config package " ++ pkgName) |
| 138 | + let v' = v ++ L.repeat 0 |
| 139 | + pure (v' L.!! 0, v' L.!! 1, v' L.!! 2) |
| 140 | + |
| 141 | +-- Should probably use a Cabal function? |
| 142 | +log :: String -> IO () |
| 143 | +log = hPutStrLn stderr |
| 144 | + |
| 145 | +-- End of source copied from: https://hackage.haskell.org/package/cabal-pkg-config-version-hook |
| 146 | + |
| 147 | +hooks = |
| 148 | + simpleUserHooks & |
| 149 | + addHook |
| 150 | + (mkSettings "opencv4") |
| 151 | + { macroName = "SETUP_HS_OPENCV4_VERSION", |
| 152 | + flagPrefixName = "setup-hs-opencv4-version" |
| 153 | + } |
| 154 | + |
| 155 | + |
4 | 156 | main = do
|
5 | 157 | args <- getArgs
|
6 | 158 | let args' | "configure" `elem` args = args ++ ["--with-gcc","c++"]
|
7 | 159 | | otherwise = args
|
8 |
| - defaultMainArgs args' |
| 160 | + defaultMainWithHooksArgs hooks args' |
0 commit comments