mirror of
https://github.com/haskell/ghcide.git
synced 2024-12-17 15:11:41 +03:00
Support CPP in the IDE (#1131)
* Hide ghc-boot by default * Add support for CPP * Delete redundant comment
This commit is contained in:
parent
3d66a7aed6
commit
cbbe589e0c
183
src/Development/IDE/Functions/CPP.hs
Normal file
183
src/Development/IDE/Functions/CPP.hs
Normal file
@ -0,0 +1,183 @@
|
||||
-- Copyright (c) 2019 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
|
||||
-- SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
-- Copied from https://github.com/ghc/ghc/blob/master/compiler/main/DriverPipeline.hs on 14 May 2019
|
||||
-- Requested to be exposed at https://gitlab.haskell.org/ghc/ghc/merge_requests/944
|
||||
{- HLINT ignore -} -- since copied from upstream
|
||||
|
||||
{-# LANGUAGE CPP, NamedFieldPuns, NondecreasingIndentation, BangPatterns, MultiWayIf #-}
|
||||
{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
--
|
||||
-- GHC Driver
|
||||
--
|
||||
-- (c) The University of Glasgow 2005
|
||||
--
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
module Development.IDE.Functions.CPP(doCpp) where
|
||||
|
||||
import Packages
|
||||
import SysTools
|
||||
import Module
|
||||
import DynFlags
|
||||
import Panic
|
||||
import FileCleanup
|
||||
|
||||
import System.Directory
|
||||
import System.FilePath
|
||||
import Control.Monad
|
||||
import Data.List ( intercalate )
|
||||
import Data.Maybe
|
||||
import Data.Version
|
||||
|
||||
|
||||
|
||||
doCpp :: DynFlags -> Bool -> FilePath -> FilePath -> IO ()
|
||||
doCpp dflags raw input_fn output_fn = do
|
||||
let hscpp_opts = picPOpts dflags
|
||||
let cmdline_include_paths = includePaths dflags
|
||||
|
||||
pkg_include_dirs <- getPackageIncludePath dflags []
|
||||
let include_paths_global = foldr (\ x xs -> ("-I" ++ x) : xs) []
|
||||
(includePathsGlobal cmdline_include_paths ++ pkg_include_dirs)
|
||||
let include_paths_quote = foldr (\ x xs -> ("-iquote" ++ x) : xs) []
|
||||
(includePathsQuote cmdline_include_paths)
|
||||
let include_paths = include_paths_quote ++ include_paths_global
|
||||
|
||||
let verbFlags = getVerbFlags dflags
|
||||
|
||||
let cpp_prog args | raw = SysTools.runCpp dflags args
|
||||
| otherwise = SysTools.runCc Nothing dflags (SysTools.Option "-E" : args)
|
||||
|
||||
let target_defs = [] {-
|
||||
[ "-D" ++ HOST_OS ++ "_BUILD_OS",
|
||||
"-D" ++ HOST_ARCH ++ "_BUILD_ARCH",
|
||||
"-D" ++ TARGET_OS ++ "_HOST_OS",
|
||||
"-D" ++ TARGET_ARCH ++ "_HOST_ARCH" ] -}
|
||||
-- remember, in code we *compile*, the HOST is the same our TARGET,
|
||||
-- and BUILD is the same as our HOST.
|
||||
|
||||
let sse_defs =
|
||||
[ "-D__SSE__" | isSseEnabled dflags ] ++
|
||||
[ "-D__SSE2__" | isSse2Enabled dflags ] ++
|
||||
[ "-D__SSE4_2__" | isSse4_2Enabled dflags ]
|
||||
|
||||
let avx_defs =
|
||||
[ "-D__AVX__" | isAvxEnabled dflags ] ++
|
||||
[ "-D__AVX2__" | isAvx2Enabled dflags ] ++
|
||||
[ "-D__AVX512CD__" | isAvx512cdEnabled dflags ] ++
|
||||
[ "-D__AVX512ER__" | isAvx512erEnabled dflags ] ++
|
||||
[ "-D__AVX512F__" | isAvx512fEnabled dflags ] ++
|
||||
[ "-D__AVX512PF__" | isAvx512pfEnabled dflags ]
|
||||
|
||||
backend_defs <- getBackendDefs dflags
|
||||
|
||||
let th_defs = [ "-D__GLASGOW_HASKELL_TH__" ]
|
||||
-- Default CPP defines in Haskell source
|
||||
ghcVersionH <- getGhcVersionPathName dflags
|
||||
let hsSourceCppOpts = [ "-include", ghcVersionH ]
|
||||
|
||||
-- MIN_VERSION macros
|
||||
let uids = explicitPackages (pkgState dflags)
|
||||
pkgs = catMaybes (map (lookupPackage dflags) uids)
|
||||
mb_macro_include <-
|
||||
if not (null pkgs) && gopt Opt_VersionMacros dflags
|
||||
then do macro_stub <- newTempName dflags TFL_CurrentModule "h"
|
||||
writeFile macro_stub (generatePackageVersionMacros pkgs)
|
||||
-- Include version macros for every *exposed* package.
|
||||
-- Without -hide-all-packages and with a package database
|
||||
-- size of 1000 packages, it takes cpp an estimated 2
|
||||
-- milliseconds to process this file. See #10970
|
||||
-- comment 8.
|
||||
return [SysTools.FileOption "-include" macro_stub]
|
||||
else return []
|
||||
|
||||
cpp_prog ( map SysTools.Option verbFlags
|
||||
++ map SysTools.Option include_paths
|
||||
++ map SysTools.Option hsSourceCppOpts
|
||||
++ map SysTools.Option target_defs
|
||||
++ map SysTools.Option backend_defs
|
||||
++ map SysTools.Option th_defs
|
||||
++ map SysTools.Option hscpp_opts
|
||||
++ map SysTools.Option sse_defs
|
||||
++ map SysTools.Option avx_defs
|
||||
++ mb_macro_include
|
||||
-- Set the language mode to assembler-with-cpp when preprocessing. This
|
||||
-- alleviates some of the C99 macro rules relating to whitespace and the hash
|
||||
-- operator, which we tend to abuse. Clang in particular is not very happy
|
||||
-- about this.
|
||||
++ [ SysTools.Option "-x"
|
||||
, SysTools.Option "assembler-with-cpp"
|
||||
, SysTools.Option input_fn
|
||||
-- We hackily use Option instead of FileOption here, so that the file
|
||||
-- name is not back-slashed on Windows. cpp is capable of
|
||||
-- dealing with / in filenames, so it works fine. Furthermore
|
||||
-- if we put in backslashes, cpp outputs #line directives
|
||||
-- with *double* backslashes. And that in turn means that
|
||||
-- our error messages get double backslashes in them.
|
||||
-- In due course we should arrange that the lexer deals
|
||||
-- with these \\ escapes properly.
|
||||
, SysTools.Option "-o"
|
||||
, SysTools.FileOption "" output_fn
|
||||
])
|
||||
|
||||
getBackendDefs :: DynFlags -> IO [String]
|
||||
getBackendDefs dflags | hscTarget dflags == HscLlvm = do
|
||||
llvmVer <- figureLlvmVersion dflags
|
||||
return $ case llvmVer of
|
||||
Just n -> [ "-D__GLASGOW_HASKELL_LLVM__=" ++ format n ]
|
||||
_ -> []
|
||||
where
|
||||
format (major, minor)
|
||||
| minor >= 100 = error "getBackendDefs: Unsupported minor version"
|
||||
| otherwise = show $ (100 * major + minor :: Int) -- Contract is Int
|
||||
|
||||
getBackendDefs _ =
|
||||
return []
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- Macros (cribbed from Cabal)
|
||||
|
||||
generatePackageVersionMacros :: [PackageConfig] -> String
|
||||
generatePackageVersionMacros pkgs = concat
|
||||
-- Do not add any C-style comments. See #3389.
|
||||
[ generateMacros "" pkgname version
|
||||
| pkg <- pkgs
|
||||
, let version = packageVersion pkg
|
||||
pkgname = map fixchar (packageNameString pkg)
|
||||
]
|
||||
|
||||
fixchar :: Char -> Char
|
||||
fixchar '-' = '_'
|
||||
fixchar c = c
|
||||
|
||||
generateMacros :: String -> String -> Version -> String
|
||||
generateMacros prefix name version =
|
||||
concat
|
||||
["#define ", prefix, "VERSION_",name," ",show (showVersion version),"\n"
|
||||
,"#define MIN_", prefix, "VERSION_",name,"(major1,major2,minor) (\\\n"
|
||||
," (major1) < ",major1," || \\\n"
|
||||
," (major1) == ",major1," && (major2) < ",major2," || \\\n"
|
||||
," (major1) == ",major1," && (major2) == ",major2," && (minor) <= ",minor,")"
|
||||
,"\n\n"
|
||||
]
|
||||
where
|
||||
(major1:major2:minor:_) = map show (versionBranch version ++ repeat 0)
|
||||
|
||||
|
||||
-- | Find out path to @ghcversion.h@ file
|
||||
getGhcVersionPathName :: DynFlags -> IO FilePath
|
||||
getGhcVersionPathName dflags = do
|
||||
candidates <- case ghcVersionFile dflags of
|
||||
Just path -> return [path]
|
||||
Nothing -> (map (</> "ghcversion.h")) <$>
|
||||
(getPackageIncludePath dflags [toInstalledUnitId rtsUnitId])
|
||||
|
||||
found <- filterM doesFileExist candidates
|
||||
case found of
|
||||
[] -> throwGhcExceptionIO (InstallationError
|
||||
("ghcversion.h missing; tried: "
|
||||
++ intercalate ", " candidates))
|
||||
(x:_) -> return x
|
@ -22,6 +22,7 @@ module Development.IDE.Functions.Compile
|
||||
) where
|
||||
|
||||
import Development.IDE.Functions.Warnings
|
||||
import Development.IDE.Functions.CPP
|
||||
import Development.IDE.Types.Diagnostics
|
||||
import qualified Development.IDE.Functions.FindImports as FindImports
|
||||
import Development.IDE.Functions.GHCError
|
||||
@ -48,6 +49,7 @@ import StringBuffer as SB
|
||||
import TidyPgm
|
||||
import InstEnv
|
||||
import FamInstEnv
|
||||
import qualified GHC.LanguageExtensions as LangExt
|
||||
|
||||
import Control.DeepSeq
|
||||
import Control.Exception as E
|
||||
@ -63,6 +65,8 @@ import Development.IDE.Types.SpanInfo
|
||||
import GHC.Generics (Generic)
|
||||
import System.FilePath
|
||||
import System.Directory
|
||||
import System.IO.Extra
|
||||
|
||||
|
||||
-- | 'CoreModule' together with some additional information required for the
|
||||
-- conversion to DAML-LF.
|
||||
@ -445,6 +449,21 @@ parseFileContents
|
||||
parseFileContents preprocessor filename (time, contents) = do
|
||||
let loc = mkRealSrcLoc (mkFastString filename) 1 1
|
||||
dflags <- parsePragmasIntoDynFlags filename contents
|
||||
|
||||
(contents, dflags) <-
|
||||
if not $ xopt LangExt.Cpp dflags then
|
||||
return (contents, dflags)
|
||||
else do
|
||||
contents <- liftIO $ withTempDir $ \dir -> do
|
||||
let inp = dir </> takeFileName filename
|
||||
let out = dir </> takeFileName filename <.> "out"
|
||||
let f x = if SB.atEnd x then Nothing else Just $ SB.nextChar x
|
||||
liftIO $ writeFileUTF8 inp (unfoldr f contents)
|
||||
doCpp dflags True inp out
|
||||
liftIO $ SB.hGetStringBuffer out
|
||||
dflags <- parsePragmasIntoDynFlags filename contents
|
||||
return (contents, dflags)
|
||||
|
||||
case unP Parser.parseModule (mkPState dflags contents loc) of
|
||||
#ifdef USE_GHC
|
||||
PFailed _ logMsg msgErr ->
|
||||
|
Loading…
Reference in New Issue
Block a user