Skip to content

Commit 05730a9

Browse files
committed
Add Stack.Iface: helpers aimed at #105
1 parent ac67160 commit 05730a9

File tree

2 files changed

+219
-0
lines changed

2 files changed

+219
-0
lines changed

src/Stack/Iface.hs

Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
{-# LANGUAGE TemplateHaskell #-}
2+
{-# LANGUAGE ConstraintKinds #-}
3+
{-# LANGUAGE OverloadedStrings #-}
4+
5+
-- TODO(DanBurton): remove the following once the module is done.
6+
{-# OPTIONS_GHC -fno-warn-name-shadowing -fno-warn-unused-imports #-}
7+
8+
module Stack.Iface where
9+
10+
import Data.Map (Map)
11+
import Data.ByteString(ByteString)
12+
import Distribution.ModuleName (ModuleName)
13+
14+
import Control.Monad.IO.Class
15+
import Control.Monad.Reader
16+
import Control.Monad.Catch
17+
import Control.Monad.Logger
18+
import Path
19+
import Path.IO (fileExists)
20+
import qualified Data.ByteString.Char8 as S8
21+
import qualified Data.Map as Map
22+
import qualified Data.Text as Text
23+
import qualified Data.Text.Encoding as Text
24+
import Data.Maybe
25+
import Data.Monoid
26+
import Data.Foldable (foldMap)
27+
import Distribution.PackageDescription
28+
import qualified Distribution.ModuleName as ModuleName
29+
import System.Process (readProcess)
30+
import System.FilePath (dropExtension, addExtension)
31+
32+
import Stack.Build.Source
33+
import Stack.Build.Types
34+
import Stack.Constants
35+
import Stack.Package
36+
import Stack.Types
37+
38+
--type M m env = (MonadLogger m, MonadIO m, MonadCatch m, MonadReader env m, HasEnvConfig env)
39+
40+
data TargetModules = TargetModules
41+
{ targetIsExecutable :: Bool
42+
-- ^ Implies "Main" as a target module if True.
43+
-- benchmark and test targets are also executable.
44+
, targetExposedModules :: [ModuleName]
45+
, targetOtherModules :: [ModuleName]
46+
}
47+
deriving (Show)
48+
49+
type ShowIface = Path Abs File -> IO ByteString
50+
51+
-- All of the compiled modules for a given target
52+
-- can be found in a single directory tree.
53+
detectFiles :: ShowIface -> Path Abs Dir -- place to find .hi files
54+
-> TargetModules -> IO [FilePath]
55+
detectFiles showIface hiDir targetModules = do
56+
let targetFilesRel :: [FilePath] -- (Relative) FilePath representation of modules.
57+
targetFilesRel
58+
= if targetIsExecutable targetModules
59+
then ["Main"]
60+
else []
61+
<> map ModuleName.toFilePath (targetExposedModules targetModules)
62+
<> map ModuleName.toFilePath (targetOtherModules targetModules)
63+
let targetHiFilesAbs :: [Path Abs File]
64+
targetHiFilesAbs = concatMap toHi targetFilesRel
65+
where
66+
toHi :: FilePath -> [Path Abs File]
67+
toHi fp = case pathHiExtMay of
68+
Just pathHiExt -> [hiDir </> pathHiExt]
69+
Nothing -> [] -- warn?
70+
where
71+
pathHiExtMay
72+
= parseRelFile
73+
$ addExtension fp "hi"
74+
75+
depFiles <- fmap concat $ forM targetHiFilesAbs $ \file -> do
76+
exists <- fileExists file
77+
if exists
78+
then do
79+
iface <- showIface file
80+
return $ findDepFiles iface
81+
else return [] -- warn?
82+
83+
return depFiles
84+
85+
86+
findDepFiles :: ByteString -> [FilePath]
87+
findDepFiles bs = depFiles
88+
where
89+
text = Text.decodeUtf8 bs
90+
ts = Text.lines text
91+
depFiles = map Text.unpack $ mapMaybe f ts
92+
f = Text.stripPrefix "addDependentFile \""
93+
>=> Text.stripSuffix "\""
94+
95+
-- Map from Target to TargetModules
96+
targetModules :: PackageDescription -> Map Target TargetModules
97+
targetModules pDesc
98+
= foldMap libraryTargetModules (library pDesc)
99+
<> foldMap executableTargetModules (executables pDesc)
100+
<> foldMap testSuiteTargetModules (testSuites pDesc)
101+
<> foldMap benchmarkTargetModules (benchmarks pDesc)
102+
103+
libraryTargetModules :: Library -> Map Target TargetModules
104+
libraryTargetModules lib = Map.singleton TargetLibrary $
105+
TargetModules
106+
{ targetIsExecutable = False
107+
, targetExposedModules = exposedModules lib
108+
, targetOtherModules = otherModules (libBuildInfo lib)
109+
}
110+
111+
executableTargetModules :: Executable -> Map Target TargetModules
112+
executableTargetModules exe = Map.singleton (TargetExecutable (exeName exe)) $
113+
TargetModules
114+
{ targetIsExecutable = True
115+
, targetExposedModules = []
116+
, targetOtherModules = otherModules (buildInfo exe)
117+
}
118+
119+
testSuiteTargetModules :: TestSuite -> Map Target TargetModules
120+
testSuiteTargetModules test = Map.singleton (TargetExecutable (testName test)) $
121+
TargetModules
122+
{ targetIsExecutable = True
123+
, targetExposedModules = []
124+
, targetOtherModules = otherModules (testBuildInfo test)
125+
}
126+
127+
benchmarkTargetModules :: Benchmark -> Map Target TargetModules
128+
benchmarkTargetModules bench = Map.singleton (TargetExecutable (benchmarkName bench)) $
129+
TargetModules
130+
{ targetIsExecutable = True
131+
, targetExposedModules = []
132+
, targetOtherModules = otherModules (benchmarkBuildInfo bench)
133+
}
134+
135+
data CompilationContext = CompilationContext
136+
{ ccPackageName :: String
137+
, ccPackageVersion :: Version
138+
, ccProjectRoot :: Path Abs Dir
139+
, ccGhcVersion :: Version
140+
, ccArch :: String
141+
, ccSnapshot :: String
142+
, ccCabalLibVersion :: Version
143+
}
144+
145+
targetHiDir :: MonadThrow m => CompilationContext -> Target -> m (Path Abs Dir)
146+
targetHiDir cc TargetLibrary = do
147+
let showGhcVer = versionString (ccGhcVersion cc)
148+
let showArch = ccArch cc
149+
let showPackageAndVersion = ccPackageName cc <> "-" <> versionString (ccPackageVersion cc)
150+
151+
arch <- parseRelDir (ccArch cc)
152+
snapshot <- parseRelDir (ccSnapshot cc)
153+
ghcVer <- parseRelDir showGhcVer
154+
archGhc <- parseRelDir (showArch <> "-ghc-" <> showGhcVer)
155+
packageAndVersion <- parseRelDir showPackageAndVersion
156+
157+
return $ ccProjectRoot cc </> $(mkRelDir ".stack-work/install")
158+
</> arch </> snapshot </> ghcVer
159+
</> $(mkRelDir "lib") </> archGhc </> packageAndVersion
160+
targetHiDir cc (TargetExecutable exeName) = do
161+
let showCabalVersion = versionString (ccCabalLibVersion cc)
162+
163+
arch <- parseRelDir (ccArch cc)
164+
cabalWithVer <- parseRelDir ("Cabal-" <> showCabalVersion)
165+
exe <- parseRelDir exeName
166+
exeTmp <- parseRelDir (exeName <> "-tmp")
167+
168+
return $ ccProjectRoot cc </> $(mkRelDir ".stack-work/dist")
169+
</> arch </> cabalWithVer
170+
</> $(mkRelDir "build") </> exe </> exeTmp
171+
172+
data Target
173+
= TargetLibrary
174+
| TargetExecutable String
175+
deriving (Eq, Ord, Show)
176+
177+
sampleRun :: IO ()
178+
sampleRun = do
179+
let showIface arg = do
180+
str <- readProcess "ghc" ["--show-iface", toFilePath arg] ""
181+
return $ S8.pack str
182+
--let hiDir =
183+
-- -- $(mkAbsDir "/home/dan/dep-file-test/.stack-work/install/x86_64-linux/lts-2.13/7.8.4/lib/x86_64-linux-ghc-7.8.4/dep-file-test-0.1.0.0")
184+
-- $(mkAbsDir "/home/dan/dep-file-test/.stack-work/dist/x86_64-linux/Cabal-1.18.1.5/build/dep-file-test/dep-file-test-tmp")
185+
let ctx = CompilationContext
186+
{ ccPackageName = "dep-file-test"
187+
, ccPackageVersion = $(mkVersion "0.1.0.0")
188+
, ccProjectRoot = $(mkAbsDir "/home/dan/dep-file-test")
189+
, ccGhcVersion = $(mkVersion "7.8.4")
190+
, ccArch = "x86_64-linux"
191+
, ccSnapshot = "lts-2.13"
192+
, ccCabalLibVersion = $(mkVersion "1.18.1.5")
193+
}
194+
195+
hiDir <- targetHiDir ctx (TargetExecutable "dep-file-test")
196+
let targetModules = TargetModules
197+
{ targetIsExecutable = True
198+
, targetExposedModules = []
199+
, targetOtherModules = []
200+
}
201+
files <- detectFiles showIface hiDir targetModules
202+
mapM_ print files
203+
204+
--iface :: M m env => m ()
205+
--iface = do
206+
-- let print' :: (Show a, MonadIO m) => a -> m ()
207+
-- print' = liftIO . print
208+
-- localInstallRoot <- installationRootLocal
209+
-- print' localInstallRoot
210+
211+
-- dist <- distRelativeDir
212+
-- print' dist
213+
214+
-- (lps, _, _) <- loadLocals defaultBuildOpts Map.empty
215+
-- forM_ lps $ \lp -> do
216+
-- print' $ packageName $ lpPackage lp
217+
218+
-- return ()

stack.cabal

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ library
4646
Stack.FileWatch
4747
Stack.GhcPkg
4848
Stack.Init
49+
Stack.Iface
4950
Stack.New
5051
Stack.Options
5152
Stack.Package

0 commit comments

Comments
 (0)