Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Do a no-link build before building C sources #1079

Closed
wants to merge 2 commits into from

1 participant

@ezyang
Collaborator

No description provided.

ezyang added some commits
@ezyang ezyang Do a no-link build before building C sources, so stub.h headers can b…
…e created.

Signed-off-by: Edward Z. Yang <ezyang@mit.edu>
2f0e83a
@ezyang ezyang Support for C files as main.
Signed-off-by: Edward Z. Yang <ezyang@mit.edu>
0e9e006
@ezyang ezyang closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Oct 22, 2012
  1. @ezyang

    Do a no-link build before building C sources, so stub.h headers can b…

    ezyang authored
    …e created.
    
    Signed-off-by: Edward Z. Yang <ezyang@mit.edu>
Commits on Oct 23, 2012
  1. @ezyang

    Support for C files as main.

    ezyang authored
    Signed-off-by: Edward Z. Yang <ezyang@mit.edu>
This page is out of date. Refresh to see the latest.
View
2  Cabal/Cabal.cabal
@@ -72,6 +72,7 @@ Library
Distribution.Simple.BuildPaths,
Distribution.Simple.Bench,
Distribution.Simple.Command,
+ Distribution.Simple.CCompiler,
Distribution.Simple.Compiler,
Distribution.Simple.Configure,
Distribution.Simple.GHC,
@@ -160,6 +161,7 @@ test-suite package-tests
PackageTests.TestSuiteExeV10.Check,
PackageTests.BenchmarkStanza.Check,
PackageTests.TemplateHaskell.Check,
+ PackageTests.CMain.Check,
PackageTests.PackageTester
hs-source-dirs: tests
build-depends:
View
23 Cabal/Distribution/PackageDescription/Check.hs
@@ -80,6 +80,8 @@ import Distribution.System
( OS(..), Arch(..), buildPlatform )
import Distribution.License
( License(..), knownLicenses )
+import Distribution.Simple.CCompiler
+ ( filenameCDialect )
import Distribution.Simple.Utils
( cabalVersion, intercalate, parseFileGlob, FileGlob(..), lowercase )
@@ -253,10 +255,10 @@ checkExecutable exe =
"No 'Main-Is' field found for executable " ++ exeName exe
, check (not (null (modulePath exe))
- && takeExtension (modulePath exe) `notElem` [".hs", ".lhs"]) $
+ && (not $ pathIsKnownProgrammingLanguage $ modulePath exe)) $
PackageBuildImpossible $
- "The 'Main-Is' field must specify a '.hs' or '.lhs' file "
- ++ "(even if it is generated by a preprocessor)."
+ "The 'main-is' field must specify a file with an extension of a"
+ ++ "known programming language."
, check (not (null moduleDuplicates)) $
PackageBuildWarning $
@@ -291,8 +293,8 @@ checkTestSuite pkg test =
, check mainIsWrongExt $
PackageBuildImpossible $
- "The 'main-is' field must specify a '.hs' or '.lhs' file "
- ++ "(even if it is generated by a preprocessor)."
+ "The 'main-is' field must specify a file with an extension of a"
+ ++ "known programming language."
-- Test suites might be built as (internal) libraries named after
-- the test suite and thus their names must not clash with the
@@ -306,7 +308,7 @@ checkTestSuite pkg test =
moduleDuplicates = dups $ testModules test
mainIsWrongExt = case testInterface test of
- TestSuiteExeV10 _ f -> takeExtension f `notElem` [".hs", ".lhs"]
+ TestSuiteExeV10 _ f -> not $ pathIsKnownProgrammingLanguage f
_ -> False
libNameClash = testName test `elem` [ libName
@@ -1493,3 +1495,12 @@ commaSep = intercalate ", "
dups :: Ord a => [a] -> [a]
dups xs = [ x | (x:_:_) <- group (sort xs) ]
+
+pathIsKnownProgrammingLanguage :: FilePath -> Bool
+pathIsKnownProgrammingLanguage path =
+ let extension = takeExtension path
+ isHaskell = elem extension [".hs", ".lhs"]
+ isC = case filenameCDialect extension of
+ Nothing -> False
+ Just _ -> True
+ in isHaskell || isC
View
121 Cabal/Distribution/Simple/CCompiler.hs
@@ -0,0 +1,121 @@
+-----------------------------------------------------------------------------
+-- |
+-- Module : Distribution.Simple.CCompiler
+-- Copyright : 2011, Dan Knapp
+--
+-- Maintainer : cabal-devel@haskell.org
+-- Portability : portable
+--
+-- This simple package provides types and functions for interacting with
+-- C compilers. Currently it's just a type enumerating extant C-like
+-- languages, which we call dialects.
+
+{-
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+
+ * Neither the name of Isaac Jones nor the names of other
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -}
+
+module Distribution.Simple.CCompiler
+ (CDialect(..),
+ cSourceExtensions,
+ cDialectFilenameExtension,
+ filenameCDialect)
+ where
+
+import Data.Monoid
+ ( Monoid(..) )
+import System.FilePath
+ ( takeExtension )
+
+
+-- | Represents a dialect of C. The Monoid instance expresses backward
+-- compatibility, in the sense that 'mappend a b' is the least inclusive
+-- dialect which both 'a' and 'b' can be correctly interpreted as.
+data CDialect = C
+ | ObjectiveC
+ | CPlusPlus
+ | ObjectiveCPlusPlus
+ deriving (Show)
+
+
+instance Monoid CDialect where
+ mempty = C
+ mappend C anything = anything
+ mappend ObjectiveC CPlusPlus = ObjectiveCPlusPlus
+ mappend CPlusPlus ObjectiveC = ObjectiveCPlusPlus
+ mappend _ ObjectiveCPlusPlus = ObjectiveCPlusPlus
+ mappend ObjectiveC _ = ObjectiveC
+ mappend CPlusPlus _ = CPlusPlus
+ mappend ObjectiveCPlusPlus _ = ObjectiveCPlusPlus
+
+
+-- | A list of all file extensions which are recognized as possibly containing
+-- some dialect of C code. Note that this list is only for source files,
+-- not for header files.
+cSourceExtensions :: [String]
+cSourceExtensions = ["c", "i", "ii", "m", "mi", "mm", "M", "mii", "cc", "cp",
+ "cxx", "cpp", "CPP", "c++", "C"]
+
+
+-- | Takes a dialect of C and whether code is intended to be passed through
+-- the preprocessor, and returns a filename extension for containing that
+-- code.
+cDialectFilenameExtension :: CDialect -> Bool -> String
+cDialectFilenameExtension C True = "c"
+cDialectFilenameExtension C False = "i"
+cDialectFilenameExtension ObjectiveC True = "m"
+cDialectFilenameExtension ObjectiveC False = "mi"
+cDialectFilenameExtension CPlusPlus True = "cpp"
+cDialectFilenameExtension CPlusPlus False = "ii"
+cDialectFilenameExtension ObjectiveCPlusPlus True = "mm"
+cDialectFilenameExtension ObjectiveCPlusPlus False = "mii"
+
+
+-- | Infers from a filename's extension the dialect of C which it contains,
+-- and whether it is intended to be passed through the preprocessor.
+filenameCDialect :: String -> Maybe (CDialect, Bool)
+filenameCDialect filename = do
+ extension <- case takeExtension filename of
+ "" -> Nothing
+ extension -> Just $ drop 1 extension
+ case extension of
+ "c" -> return (C, True)
+ "i" -> return (C, False)
+ "ii" -> return (CPlusPlus, False)
+ "m" -> return (ObjectiveC, True)
+ "mi" -> return (ObjectiveC, False)
+ "mm" -> return (ObjectiveCPlusPlus, True)
+ "M" -> return (ObjectiveCPlusPlus, True)
+ "mii" -> return (ObjectiveCPlusPlus, False)
+ "cc" -> return (CPlusPlus, True)
+ "cp" -> return (CPlusPlus, True)
+ "cxx" -> return (CPlusPlus, True)
+ "cpp" -> return (CPlusPlus, True)
+ "CPP" -> return (CPlusPlus, True)
+ "c++" -> return (CPlusPlus, True)
+ "C" -> return (CPlusPlus, True)
+ _ -> Nothing
View
39 Cabal/Distribution/Simple/GHC.hs
@@ -824,22 +824,13 @@ buildExe verbosity _pkg_descr lbi
-- FIX: what about exeName.hi-boot?
-- build executables
- unless (null (cSources exeBi)) $ do
- info verbosity "Building C Sources."
- sequence_
- [ do let opts = (componentCcGhcOptions verbosity lbi exeBi clbi
- exeDir filename) `mappend` mempty {
- ghcOptDynamic = toFlag (withDynExe lbi),
- ghcOptProfilingMode = toFlag (withProfExe lbi)
- }
- odir = fromFlag (ghcOptObjDir opts)
- createDirectoryIfMissingVerbose verbosity True odir
- runGhcProg opts
- | filename <- cSources exeBi]
srcMainFile <- findFile (exeDir : hsSourceDirs exeBi) modPath
let cObjs = map (`replaceExtension` objExtension) (cSources exeBi)
+ let no_hs_main = if elem (takeExtension srcMainFile) [".hs", ".lhs"]
+ then []
+ else ["-no-hs-main"]
let vanillaOpts = (componentGhcOptions verbosity lbi exeBi clbi exeDir)
`mappend` mempty {
ghcOptMode = toFlag GhcModeMake,
@@ -848,7 +839,8 @@ buildExe verbosity _pkg_descr lbi
ghcOptLinkOptions = PD.ldOptions exeBi,
ghcOptLinkLibs = extraLibs exeBi,
ghcOptLinkLibPath = extraLibDirs exeBi,
- ghcOptLinkFrameworks = PD.frameworks exeBi
+ ghcOptLinkFrameworks = PD.frameworks exeBi,
+ ghcOptExtra = no_hs_main
}
exeOpts | withProfExe lbi = vanillaOpts `mappend` mempty {
@@ -870,10 +862,27 @@ buildExe verbosity _pkg_descr lbi
-- with profiling. This is because the code that TH needs to
-- run at compile time needs to be the vanilla ABI so it can
-- be loaded up and run by the compiler.
- when ((withProfExe lbi || withDynExe lbi) &&
- EnableExtension TemplateHaskell `elem` allExtensions exeBi) $
+ --
+ -- We also need to do this build if we have any C sources, because
+ -- it may generate stub files which the C sources would use.
+ when (((withProfExe lbi || withDynExe lbi) &&
+ EnableExtension TemplateHaskell `elem` allExtensions exeBi)
+ || not (null (cSources exeBi))) $
runGhcProg vanillaOpts { ghcOptNoLink = toFlag True }
+ unless (null (cSources exeBi)) $ do
+ info verbosity "Building C Sources."
+ sequence_
+ [ do let opts = (componentCcGhcOptions verbosity lbi exeBi clbi
+ exeDir filename) `mappend` mempty {
+ ghcOptDynamic = toFlag (withDynExe lbi),
+ ghcOptProfilingMode = toFlag (withProfExe lbi)
+ }
+ odir = fromFlag (ghcOptObjDir opts)
+ createDirectoryIfMissingVerbose verbosity True odir
+ runGhcProg opts
+ | filename <- cSources exeBi]
+
runGhcProg exeOpts { ghcOptOutputFile = toFlag (targetDir </> exeNameReal) }
View
6 Cabal/Distribution/Simple/PreProcess.hs
@@ -71,6 +71,8 @@ import Distribution.PackageDescription as PD
import qualified Distribution.InstalledPackageInfo as Installed
( InstalledPackageInfo_(..) )
import qualified Distribution.Simple.PackageIndex as PackageIndex
+import Distribution.Simple.CCompiler
+ ( cSourceExtensions )
import Distribution.Simple.Compiler
( CompilerFlavor(..), Compiler(..), compilerFlavor, compilerVersion )
import Distribution.Simple.LocalBuildInfo
@@ -219,9 +221,11 @@ preprocessComponent pd comp lbi isSrcDist verbosity handlers = case comp of
BenchmarkUnsupported tt -> die $ "No support for preprocessing benchmark "
++ "type " ++ display tt
where
- builtinSuffixes
+ builtinHaskellSuffixes
| NHC == compilerFlavor (compiler lbi) = ["hs", "lhs", "gc"]
| otherwise = ["hs", "lhs"]
+ builtinCSuffixes = cSourceExtensions
+ builtinSuffixes = builtinHaskellSuffixes ++ builtinCSuffixes
localHandlers bi = [(ext, h bi lbi) | (ext, h) <- handlers]
pre dirs dir lhndlrs fp =
preprocessFile dirs dir isSrcDist fp verbosity builtinSuffixes lhndlrs
View
4 Cabal/tests/PackageTests.hs
@@ -25,6 +25,7 @@ import PackageTests.BuildDeps.InternalLibrary1.Check
import PackageTests.BuildDeps.InternalLibrary2.Check
import PackageTests.BuildDeps.InternalLibrary3.Check
import PackageTests.BuildDeps.InternalLibrary4.Check
+import PackageTests.CMain.Check
import PackageTests.TestOptions.Check
import PackageTests.TestStanza.Check
import PackageTests.TestSuiteExeV10.Check
@@ -71,7 +72,8 @@ tests cabalVersion = [
hunit "PackageTests/BuildDeps/InternalLibrary1/" PackageTests.BuildDeps.InternalLibrary1.Check.suite,
hunit "PackageTests/BuildDeps/InternalLibrary2/" PackageTests.BuildDeps.InternalLibrary2.Check.suite,
hunit "PackageTests/BuildDeps/InternalLibrary3/" PackageTests.BuildDeps.InternalLibrary3.Check.suite,
- hunit "PackageTests/BuildDeps/InternalLibrary4/" PackageTests.BuildDeps.InternalLibrary4.Check.suite
+ hunit "PackageTests/BuildDeps/InternalLibrary4/" PackageTests.BuildDeps.InternalLibrary4.Check.suite,
+ hunit "PackageTests/CMain" PackageTests.CMain.Check.checkBuild
]
else [])
View
19 Cabal/tests/PackageTests/CMain/Check.hs
@@ -0,0 +1,19 @@
+module PackageTests.CMain.Check
+ ( checkBuild
+ ) where
+
+import Distribution.Simple.Hpc
+import Distribution.Version
+import Test.HUnit
+import System.Directory
+import System.FilePath
+import PackageTests.PackageTester
+
+dir :: FilePath
+dir = "PackageTests" </> "CMain"
+
+checkBuild :: Test
+checkBuild = TestCase $ do
+ let spec = PackageSpec dir []
+ buildResult <- cabal_build spec
+ assertBuildSucceeded buildResult
View
3  Cabal/tests/PackageTests/CMain/Setup.hs
@@ -0,0 +1,3 @@
+import Distribution.Simple
+main = defaultMain
+
View
6 Cabal/tests/PackageTests/CMain/foo.c
@@ -0,0 +1,6 @@
+#include <stdio.h>
+
+int main(int argc, char **argv) {
+ printf("Hello world!");
+ return 0;
+}
View
8 Cabal/tests/PackageTests/CMain/my.cabal
@@ -0,0 +1,8 @@
+name: my
+version: 0.1
+license: BSD3
+cabal-version: >= 1.9.2
+build-type: Simple
+
+executable foo
+ main-is: foo.c
View
2  Cabal/tests/PackageTests/PackageTester.hs
@@ -90,7 +90,7 @@ doCabalBuild spec = do
configResult <- doCabalConfigure spec
if successful configResult
then do
- res <- cabal spec ["build"]
+ res <- cabal spec ["build", "-v"]
return $ recordRun res BuildSuccess configResult
else
return configResult
Something went wrong with that request. Please try again.