|
4 | 4 | {-# LANGUAGE CPP #-} |
5 | 5 | {-# LANGUAGE DuplicateRecordFields #-} |
6 | 6 | {-# LANGUAGE TypeFamilies #-} |
| 7 | +{-# LANGUAGE PartialTypeSignatures #-} |
7 | 8 |
|
8 | 9 | -- | A Shake implementation of the compiler service, built |
9 | 10 | -- using the "Shaker" abstraction layer for in-memory use. |
@@ -93,7 +94,7 @@ import Data.Proxy |
93 | 94 | import qualified Data.Text as T |
94 | 95 | import qualified Data.Text.Encoding as T |
95 | 96 | import qualified Data.Text.Utf16.Rope.Mixed as Rope |
96 | | -import Data.Time (UTCTime (..)) |
| 97 | +import Data.Time (UTCTime (..), getCurrentTime, diffUTCTime) |
97 | 98 | import Data.Time.Clock.POSIX (posixSecondsToUTCTime) |
98 | 99 | import Data.Tuple.Extra |
99 | 100 | import Data.Typeable (cast) |
@@ -175,6 +176,12 @@ import System.Info.Extra (isWindows) |
175 | 176 |
|
176 | 177 | import qualified Data.IntMap as IM |
177 | 178 | import GHC.Fingerprint |
| 179 | +import Text.Pretty.Simple |
| 180 | +import qualified Data.Map.Strict as Map |
| 181 | +import System.FilePath (takeExtension, takeFileName, normalise, dropTrailingPathSeparator, dropExtension, splitDirectories) |
| 182 | +import Data.Char (isUpper) |
| 183 | +import System.Directory.Extra (listFilesRecursive, listFilesInside) |
| 184 | +import System.IO.Unsafe |
178 | 185 |
|
179 | 186 | data Log |
180 | 187 | = LogShake Shake.Log |
@@ -313,30 +320,21 @@ getParsedModuleDefinition packageState opt file ms = do |
313 | 320 | getLocatedImportsRule :: Recorder (WithPriority Log) -> Rules () |
314 | 321 | getLocatedImportsRule recorder = |
315 | 322 | define (cmapWithPrio LogShake recorder) $ \GetLocatedImports file -> do |
| 323 | + |
316 | 324 | ModSummaryResult{msrModSummary = ms} <- use_ GetModSummaryWithoutTimestamps file |
317 | | - (KnownTargets targets targetsMap) <- useNoFile_ GetKnownTargets |
| 325 | + -- TODO: should we reverse this concatenation, there are way less |
| 326 | + -- source import than normal import in theory, so it should be faster |
318 | 327 | let imports = [(False, imp) | imp <- ms_textual_imps ms] ++ [(True, imp) | imp <- ms_srcimps ms] |
319 | 328 | env_eq <- use_ GhcSession file |
320 | 329 | let env = hscEnv env_eq |
321 | 330 | let import_dirs = map (second homeUnitEnv_dflags) $ hugElts $ hsc_HUG env |
322 | 331 | let dflags = hsc_dflags env |
323 | 332 | opt <- getIdeOptions |
324 | | - let getTargetFor modName nfp |
325 | | - | Just (TargetFile nfp') <- HM.lookup (TargetFile nfp) targetsMap = do |
326 | | - -- reuse the existing NormalizedFilePath in order to maximize sharing |
327 | | - itExists <- getFileExists nfp' |
328 | | - return $ if itExists then Just nfp' else Nothing |
329 | | - | Just tt <- HM.lookup (TargetModule modName) targets = do |
330 | | - -- reuse the existing NormalizedFilePath in order to maximize sharing |
331 | | - let ttmap = HM.mapWithKey const (HashSet.toMap tt) |
332 | | - nfp' = HM.lookupDefault nfp nfp ttmap |
333 | | - itExists <- getFileExists nfp' |
334 | | - return $ if itExists then Just nfp' else Nothing |
335 | | - | otherwise = do |
336 | | - itExists <- getFileExists nfp |
337 | | - return $ if itExists then Just nfp else Nothing |
| 333 | + |
| 334 | + moduleMaps <- use_ GetModulesPaths file |
338 | 335 | (diags, imports') <- fmap unzip $ forM imports $ \(isSource, (mbPkgName, modName)) -> do |
339 | | - diagOrImp <- locateModule (hscSetFlags dflags env) import_dirs (optExtensions opt) getTargetFor modName mbPkgName isSource |
| 336 | + |
| 337 | + diagOrImp <- locateModule moduleMaps (hscSetFlags dflags env) import_dirs (optExtensions opt) modName mbPkgName isSource |
340 | 338 | case diagOrImp of |
341 | 339 | Left diags -> pure (diags, Just (modName, Nothing)) |
342 | 340 | Right (FileImport path) -> pure ([], Just (modName, Just path)) |
@@ -626,10 +624,51 @@ getModuleGraphRule recorder = defineEarlyCutOffNoFile (cmapWithPrio LogShake rec |
626 | 624 | fs <- toKnownFiles <$> useNoFile_ GetKnownTargets |
627 | 625 | dependencyInfoForFiles (HashSet.toList fs) |
628 | 626 |
|
| 627 | +{-# NOINLINE cacheVar #-} |
| 628 | +cacheVar = unsafePerformIO (newTVarIO mempty) |
| 629 | + |
| 630 | +getModulesPathsRule :: Recorder (WithPriority Log) -> Rules () |
| 631 | +getModulesPathsRule recorder = defineEarlyCutoff (cmapWithPrio LogShake recorder) $ Rule $ \GetModulesPaths file -> do |
| 632 | + env_eq <- use_ GhcSession file |
| 633 | + |
| 634 | + cache <- liftIO (readTVarIO cacheVar) |
| 635 | + case Map.lookup (envUnique env_eq) cache of |
| 636 | + Just res -> pure (mempty, ([], Just res)) |
| 637 | + Nothing -> do |
| 638 | + let env = hscEnv env_eq |
| 639 | + let import_dirs = map (second homeUnitEnv_dflags) $ hugElts $ hsc_HUG env |
| 640 | + opt <- getIdeOptions |
| 641 | + let exts = (optExtensions opt) |
| 642 | + let acceptedExtensions = concatMap (\x -> ['.':x, '.':x <> "-boot"]) exts |
| 643 | + |
| 644 | + (unzip -> (a, b)) <- flip mapM import_dirs $ \(u, dyn) -> do |
| 645 | + (unzip -> (a, b)) <- flip mapM (importPaths dyn) $ \dir' -> do |
| 646 | + let dir = dropTrailingPathSeparator dir' |
| 647 | + let predicate path = pure (path == dir || isUpper (head (takeFileName path))) |
| 648 | + let dir_number_directories = length (splitDirectories dir) |
| 649 | + let toModule file = mkModuleName (intercalate "." $ drop dir_number_directories (splitDirectories (dropExtension file))) |
| 650 | + |
| 651 | + -- TODO: we are taking/droping extension, this could be factorized to save a few cpu cycles ;) |
| 652 | + -- TODO: do acceptedextensions needs to be a set ? or a vector? |
| 653 | + modules <- fmap (\path -> (toModule path, toNormalizedFilePath' path)) . filter (\y -> takeExtension y `elem` acceptedExtensions) <$> liftIO (listFilesInside predicate dir) |
| 654 | + let isSourceModule (_, path) = "-boot" `isSuffixOf` fromNormalizedFilePath path |
| 655 | + let (sourceModules, notSourceModules) = partition isSourceModule modules |
| 656 | + pure $ (Map.fromList notSourceModules, Map.fromList sourceModules) |
| 657 | + pure (fmap (u,) $ mconcat a, fmap (u, ) $ mconcat b) |
| 658 | + |
| 659 | + let res = (mconcat a, mconcat b) |
| 660 | + liftIO $ atomically $ modifyTVar' cacheVar (Map.insert (envUnique env_eq) res) |
| 661 | + |
| 662 | + pure (mempty, ([], Just $ (mconcat a, mconcat b))) |
| 663 | + |
629 | 664 | dependencyInfoForFiles :: [NormalizedFilePath] -> Action (BS.ByteString, DependencyInformation) |
630 | 665 | dependencyInfoForFiles fs = do |
| 666 | + -- liftIO $ print ("fs length", length fs) |
631 | 667 | (rawDepInfo, bm) <- rawDependencyInformation fs |
| 668 | + -- liftIO $ print ("ok with raw deps") |
| 669 | + -- liftIO $ pPrint rawDepInfo |
632 | 670 | let (all_fs, _all_ids) = unzip $ HM.toList $ pathToIdMap $ rawPathIdMap rawDepInfo |
| 671 | + -- liftIO $ print ("all_fs length", length all_fs) |
633 | 672 | msrs <- uses GetModSummaryWithoutTimestamps all_fs |
634 | 673 | let mss = map (fmap msrModSummary) msrs |
635 | 674 | let deps = map (\i -> IM.lookup (getFilePathId i) (rawImports rawDepInfo)) _all_ids |
@@ -708,6 +747,7 @@ loadGhcSession recorder ghcSessionDepsConfig = do |
708 | 747 | IdeGhcSession{loadSessionFun} <- useNoFile_ GhcSessionIO |
709 | 748 | -- loading is always returning a absolute path now |
710 | 749 | (val,deps) <- liftIO $ loadSessionFun $ fromNormalizedFilePath file |
| 750 | + -- TODO: this is responsible for a LOT of allocations |
711 | 751 |
|
712 | 752 | -- add the deps to the Shake graph |
713 | 753 | let addDependency fp = do |
@@ -1229,6 +1269,7 @@ mainRule recorder RulesConfig{..} = do |
1229 | 1269 | getModIfaceRule recorder |
1230 | 1270 | getModSummaryRule templateHaskellWarning recorder |
1231 | 1271 | getModuleGraphRule recorder |
| 1272 | + getModulesPathsRule recorder |
1232 | 1273 | getFileHashRule recorder |
1233 | 1274 | knownFilesRule recorder |
1234 | 1275 | getClientSettingsRule recorder |
|
0 commit comments