shake-language-c 0.5.0 → 0.6.2
raw patch · 41 files changed
+2220/−1974 lines, 41 filesdep +data-default-classdep +directorydep +doctestdep ~base
Dependencies added: data-default-class, directory, doctest, hspec, shake-language-c
Dependency ranges changed: base
Files
- CHANGELOG.md +36/−0
- Changelog.md +0/−0
- Development/Shake/Language/C.hs +0/−70
- Development/Shake/Language/C/BuildFlags.hs +0/−195
- Development/Shake/Language/C/Config.hs +0/−64
- Development/Shake/Language/C/Host.hs +0/−86
- Development/Shake/Language/C/Label.hs +0/−46
- Development/Shake/Language/C/Language.hs +0/−47
- Development/Shake/Language/C/PkgConfig.hs +0/−132
- Development/Shake/Language/C/Rules.hs +0/−154
- Development/Shake/Language/C/Target.hs +0/−104
- Development/Shake/Language/C/Target/Android.hs +0/−202
- Development/Shake/Language/C/Target/Linux.hs +0/−80
- Development/Shake/Language/C/Target/NaCl.hs +0/−193
- Development/Shake/Language/C/Target/OSX.hs +0/−190
- Development/Shake/Language/C/Target/Windows.hs +0/−82
- Development/Shake/Language/C/ToolChain.hs +0/−271
- Development/Shake/Language/C/Util.hs +0/−51
- README.md +3/−1
- shake-language-c.cabal +24/−6
- src/Development/Shake/Language/C.hs +49/−0
- src/Development/Shake/Language/C/BuildFlags.hs +218/−0
- src/Development/Shake/Language/C/Config.hs +65/−0
- src/Development/Shake/Language/C/Host.hs +86/−0
- src/Development/Shake/Language/C/Host/Linux.hs +39/−0
- src/Development/Shake/Language/C/Host/OSX.hs +33/−0
- src/Development/Shake/Language/C/Host/Windows.hs +38/−0
- src/Development/Shake/Language/C/Label.hs +46/−0
- src/Development/Shake/Language/C/Language.hs +47/−0
- src/Development/Shake/Language/C/PkgConfig.hs +125/−0
- src/Development/Shake/Language/C/Rules.hs +154/−0
- src/Development/Shake/Language/C/Target.hs +114/−0
- src/Development/Shake/Language/C/Target/Android.hs +203/−0
- src/Development/Shake/Language/C/Target/Linux.hs +66/−0
- src/Development/Shake/Language/C/Target/NaCl.hs +173/−0
- src/Development/Shake/Language/C/Target/OSX.hs +185/−0
- src/Development/Shake/Language/C/Target/Windows.hs +67/−0
- src/Development/Shake/Language/C/ToolChain.hs +271/−0
- src/Development/Shake/Language/C/Util.hs +65/−0
- tests/doctests.hs +23/−0
- tests/spectests.hs +90/−0
+ CHANGELOG.md view
@@ -0,0 +1,36 @@+# Changelog for shake-language-c++## [v0.6.2][]++Bug fix release.++## [v0.6.1][]++Bug fix release.++## [v0.6.0][]++### Added++* Add `Data.Default.Class.Default` instances for some data types; add dependency on package `data-default-class`.++### Changed++* Don't export the entire module `Development.Shake.Language.C.ToolChain` from `Development.Shake.Language.C`; expose `Development.Shake.Language.C.ToolChain` for toolchain writers.+* Export `Development.Shake.Language.C.Language.Language` from `Development.Shake.Language.C.BuildFlags` instead of `Development.Shake.Language.C`.+* Export `Development.Shake.Language.C.Rules` from `Development.Shake.Language.C`; hide `Development.Shake.Language.C.Rules` in Cabal file.+* **Android**: Add `libcxxabi` include directory instead of `gabi++` to include path when compiling with `libcxx`. Fixes `error: no member named '__cxa_demangle' in namespace '__cxxabiv1'`.++### Removed++* Remove `libppapi`, `libppapi_cpp`, `libnacl_io`, `libppapi_simple` from `Development.Shake.Language.C.Target.NaCl`.+* Remove `Development.Shake.Language.C.Target.archString`.++## [0.5.0][]++First released version.++[v0.6.2]: https://github.com/samplecount/shake-language-c/tree/v0.6.2+[v0.6.1]: https://github.com/samplecount/shake-language-c/tree/v0.6.1+[v0.6.0]: https://github.com/samplecount/shake-language-c/tree/v0.6.0+[v0.5.0]: https://github.com/samplecount/shake-language-c/tree/v0.5.0
− Changelog.md
− Development/Shake/Language/C.hs
@@ -1,70 +0,0 @@--- Copyright 2012-2014 Samplecount S.L.------ Licensed under the Apache License, Version 2.0 (the "License");--- you may not use this file except in compliance with the License.--- You may obtain a copy of the License at------ http://www.apache.org/licenses/LICENSE-2.0------ Unless required by applicable law or agreed to in writing, software--- distributed under the License is distributed on an "AS IS" BASIS,--- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.--- See the License for the specific language governing permissions and--- limitations under the License.--{-|-Description: Build @C@ language projects for various target platforms--}--module Development.Shake.Language.C (- -- * High-level build rules- module Development.Shake.Language.C.Rules- , module Development.Shake.Language.C.BuildFlags- -- * Build targets- -- $targets- , module Development.Shake.Language.C.Target- -- * Toolchains- , module Development.Shake.Language.C.ToolChain- -- * Source Languages- , module Development.Shake.Language.C.Language-) where--import Development.Shake.Language.C.BuildFlags-import Development.Shake.Language.C.Language-import Development.Shake.Language.C.Rules-import Development.Shake.Language.C.Target-import Development.Shake.Language.C.ToolChain (- Linkage(..)- , LinkResult(..)- , ToolChain- , ToolChainVariant(..)- , toolDirectory- , toolPrefix- , variant- , compilerCommand- , compiler- , archiverCommand- , archiver- , linkerCommand- , linker- , defaultBuildFlags- , defaultCompiler- , Archiver- , defaultArchiver- , Linker- , defaultLinker- , applyEnv- , toEnv- )--{- $targets--This library's focus is on cross compilation. Here's a list of modules that-provide support for targeting specific platforms:-- * "Development.Shake.Language.C.Target.Android"- * "Development.Shake.Language.C.Target.Linux"- * "Development.Shake.Language.C.Target.NaCl"- * "Development.Shake.Language.C.Target.OSX"- * "Development.Shake.Language.C.Target.Windows"--}
− Development/Shake/Language/C/BuildFlags.hs
@@ -1,195 +0,0 @@--- Copyright 2012-2014 Samplecount S.L.------ Licensed under the Apache License, Version 2.0 (the "License");--- you may not use this file except in compliance with the License.--- You may obtain a copy of the License at------ http://www.apache.org/licenses/LICENSE-2.0------ Unless required by applicable law or agreed to in writing, software--- distributed under the License is distributed on an "AS IS" BASIS,--- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.--- See the License for the specific language governing permissions and--- limitations under the License.--{-# LANGUAGE TemplateHaskell #-}--{-|-Description: Build flags record for building @C@ language projects--The `BuildFlags` record is an abstraction for various toolchain flags for-building executables and libraries from source files in a @C@-based language.-It's intended to be toolchain-independent, but currently there's certainly a-bias towards binutils\/gcc/clang toolchains.--}--module Development.Shake.Language.C.BuildFlags (- BuildFlags- -- Poor man's documentation for TH generated functions.- , systemIncludes -- | System include directories, referenced by @#include \<...\>@ in code and usually passed to the compiler with the @-I@ flag.- , userIncludes -- | User include directories, referenced by @#include "..."@ in code and usually passed to the compiler with the @-iquote@ flag.- , defines -- | Preprocessor defines, a list of pairs of names with or without a value.- , preprocessorFlags -- | Other preprocessor flags.- , compilerFlags -- | Compiler flags, either generic ones or for a specific source 'Language'.- , libraryPath -- | Linker search path for libraries.- , libraries -- | List of libraries to link against. Note that you should use the library name without the @lib@ prefix and without extension.- , linkerFlags -- | Flags passed to the linker.- , localLibraries -- | Locally built static libraries to be linked against. See also the corresponding section in the <https://github.com/samplecount/shake-language-c/blob/master/docs/Manual.md#locally-built-libraries manual>.- , archiverFlags -- | Flags passed to the object archiver.- , defineFlags- , compilerFlagsFor- , fromConfig- , (>>>=)- , append- , prepend-) where--import Control.Arrow-import Control.Monad-import Data.Char (isSpace)-import Data.Monoid-import Data.List-import Data.List.Split-import Data.Maybe-import Development.Shake.Language.C.Language (Language(..))-import Development.Shake.Language.C.Label-import Development.Shake.Language.C.Util--{-| Record type for abstracting various toolchain command line flags.--`BuildFlags` is an instance `Monoid`, you can create a default record with-`mempty` and append flags with `mappend`.--Record accessors are 'Data.Label.Mono.Lens' es from the-<https://hackage.haskell.org/package/fclabels fclabels> package, which-makes accessing and modifying record fields a bit more convenient.-@fclabels@ was chosen over <https://hackage.haskell.org/package/lens lens>-because it has far fewer dependencies, which is convenient when installing-the Shake build system in a per-project cabal sandbox. We might switch to-@lens@ when it gets included in the Haskell platform.--There are two convenience functions for working with `BuildFlags` record fields-containing lists of flags, `append` and `prepend`. Since most combinators in-this library expect a function @BuildFlags -> BuildFlags@, the following is a-common idiom:--@-buildFlags . append `systemIncludes` ["path"]-@--Note that when modifying the same record field, order of function composition-matters and you might want to use the arrow combinator '>>>' for appending in-source statement order:-->>> get systemIncludes \- $ buildFlags >>> append systemIncludes ["path1"] >>> append systemIncludes ["path2"] \- $ mempty-["path1", "path2"]--See "Development.Shake.Language.C.Rules" for how to use 'BuildFlags' in build-product rules.--}-data BuildFlags = BuildFlags {- _systemIncludes :: [FilePath]- , _userIncludes :: [FilePath]- , _defines :: [(String, Maybe String)]- , _preprocessorFlags :: [String]- , _compilerFlags :: [(Maybe Language, [String])]- , _libraryPath :: [FilePath]- , _libraries :: [String]- , _linkerFlags :: [String]- -- This is needed for linking against local libraries built by shake (the linker `needs' its inputs).- , _localLibraries :: [FilePath]- , _archiverFlags :: [String]- } deriving (Eq, Show)--mkLabel ''BuildFlags--defaultBuildFlags :: BuildFlags-defaultBuildFlags =- BuildFlags {- _systemIncludes = []- , _userIncludes = []- , _defines = []- , _preprocessorFlags = []- , _compilerFlags = []- , _libraryPath = []- , _libraries = []- , _linkerFlags = []- , _localLibraries = []- , _archiverFlags = []- }--instance Monoid BuildFlags where- mempty = defaultBuildFlags- a `mappend` b =- append systemIncludes (get systemIncludes a)- . append userIncludes (get userIncludes a)- . append defines (get defines a)- . append preprocessorFlags (get preprocessorFlags a)- . append compilerFlags (get compilerFlags a)- . append libraryPath (get libraryPath a)- . append libraries (get libraries a)- . append linkerFlags (get linkerFlags a)- . append localLibraries (get localLibraries a)- . append archiverFlags (get archiverFlags a)- $ b---- | Construct preprocessor flags from the 'defines' field of 'BuildFlags'.-defineFlags :: BuildFlags -> [String]-defineFlags = concatMapFlag "-D"- . map (\(a, b) -> maybe a (\b' -> a++"="++b') b)- . get defines---- | Return a list of compiler flags for a specific source language.-compilerFlagsFor :: Maybe Language -> BuildFlags -> [String]-compilerFlagsFor lang = concat- . maybe (map snd . filter (isNothing.fst))- (mapMaybe . f) lang- . get compilerFlags- where f _ (Nothing, x) = Just x- f l (Just l', x) | l == l' = Just x- | otherwise = Nothing---- | Construct a 'BuildFlags' modifier function from a config file.------ See also "Development.Shake.Language.C.Config".-fromConfig :: (Functor m, Monad m) => (String -> m (Maybe String)) -> m (BuildFlags -> BuildFlags)-fromConfig getConfig = do- let parseConfig parser = fmap (maybe [] parser) . getConfig . ("BuildFlags."++)-- config_systemIncludes <- parseConfig paths "systemIncludes"- config_userIncludes <- parseConfig paths "userIncludes"- config_defines <- parseConfig defines' "defines"- config_preprocessorFlags <- parseConfig flags "preprocessorFlags"- config_compilerFlags <- parseConfig ((:[]) . ((,)Nothing) . flags) "compilerFlags"- config_compilerFlags_c <- parseConfig ((:[]) . ((,)(Just C)) . flags) "compilerFlags.c"- config_compilerFlags_cxx <- parseConfig ((:[]) . ((,)(Just Cpp)) . flags) "compilerFlags.cxx"- config_libraryPath <- parseConfig paths "libraryPath"- config_libraries <- parseConfig flags "libraries"- config_linkerFlags <- parseConfig flags "linkerFlags"- config_localLibraries <- parseConfig paths "localLibraries"- config_archiverFlags <- parseConfig flags "archiverFlags"-- return $ append systemIncludes config_systemIncludes- . append userIncludes config_userIncludes- . append defines config_defines- . append preprocessorFlags config_preprocessorFlags- . append compilerFlags (config_compilerFlags ++ config_compilerFlags_c ++ config_compilerFlags_cxx)- . append libraryPath config_libraryPath- . append libraries config_libraries- . append linkerFlags config_linkerFlags- . append localLibraries config_localLibraries- . append archiverFlags config_archiverFlags- where- flags = words' . dropWhile isSpace- paths = words' . dropWhile isSpace- define [] = error "Empty preprocessor definition"- define [k] = (k, Nothing)- define [k,v] = (k, Just v)- define (k:vs) = (k, Just (intercalate "=" vs))- defines' = map (define . splitOn "=") . flags---- | Utility function for composing functions in a monad.-(>>>=) :: Monad m => m (a -> b) -> m (b -> c) -> m (a -> c)-(>>>=) = liftM2 (>>>)
− Development/Shake/Language/C/Config.hs
@@ -1,64 +0,0 @@-{-# LANGUAGE DeriveDataTypeable, GeneralizedNewtypeDeriving #-}--{-|-Description: Read values from configuration files--This module provides utilities for reading values from configuration files,-similar to the functions provided by "Development.Shake.Config".--}-module Development.Shake.Language.C.Config(- withConfig- , parsePaths- , getPaths-) where--import Control.Applicative-import qualified Data.HashMap.Strict as Map-import Development.Shake-import Development.Shake.Classes-import Development.Shake.Config (readConfigFileWithEnv)-import Development.Shake.Language.C.Util (words')--newtype Config = Config ([(String, String)], FilePath, String) deriving (Show,Typeable,Eq,Hashable,Binary,NFData)--{- | Given a list of dependencies, return a function that takes an environment-of variable bindings, a configuration file path and a configuration variable-and returns the corresponding configuration value.--This function is more flexible than `Development.Shake.Config.usingConfigFile`.-It allows the use of multiple configuration files as well as specifying default-variable bindings.--Typical usage would be something like this:--> -- In the Rules monad-> getConfig <- withConfig []-> -- Then in an Action-> ... value <- getConfig [("variable", "default value")] "config.cfg" "variable"--}-withConfig :: [FilePath]- -> Rules ( [(String,String)]- -> FilePath- -> String- -> Action (Maybe String))-withConfig deps = do- fileCache <- newCache $ \(env, file) -> do- need deps- liftIO $ readConfigFileWithEnv env file- query <- addOracle $ \(Config (env, file, key)) -> Map.lookup key <$> fileCache (env, file)- return $ \env file key -> query (Config (env, file, key))---- | Parse a list of space separated paths from an input string. Spaces can be escaped by @\\@ characters.------ prop> parsePaths "a b c\\ d" == ["a", "b", "c d"]-parsePaths :: String -> [FilePath]-parsePaths = words'---- | Given a function that maps a configuration variable to a value and a list of variable names, return a corresponding list of file paths. Missing variables are ignored.-getPaths ::- (String -> Action (Maybe String)) -- ^ Configuration lookup function- -> [String] -- ^ Configuration keys- -> Action [FilePath] -- ^ File paths-getPaths getConfig keys = do- sources <- mapM (fmap (fmap parsePaths) . getConfig) keys- return $ concatMap (maybe [] id) sources
− Development/Shake/Language/C/Host.hs
@@ -1,86 +0,0 @@--- Copyright 2012-2013 Samplecount S.L.------ Licensed under the Apache License, Version 2.0 (the "License");--- you may not use this file except in compliance with the License.--- You may obtain a copy of the License at------ http://www.apache.org/licenses/LICENSE-2.0------ Unless required by applicable law or agreed to in writing, software--- distributed under the License is distributed on an "AS IS" BASIS,--- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.--- See the License for the specific language governing permissions and--- limitations under the License.--{-|-Description: Toolchain definitions and utilities for host platform--}-module Development.Shake.Language.C.Host (- OS(..)- , os- , executableExtension- , sharedLibraryExtension- , loadableLibraryExtension- , defaultToolChain-) where--import Development.Shake (Action)-import Development.Shake.Language.C.Target (Target)-import qualified Development.Shake.Language.C.Target.Linux as Linux-import qualified Development.Shake.Language.C.Target.OSX as OSX-import qualified Development.Shake.Language.C.Target.Windows as Windows-import Development.Shake.Language.C.ToolChain (ToolChain)-import qualified System.Info as System-import System.IO.Unsafe (unsafePerformIO)---- | Host operating system.-data OS =- Linux- | OSX- | Windows- deriving (Eq, Ord, Show)---- | This host's operating system.-os :: OS-os =- case System.os of- "darwin" -> OSX- "mingw32" -> Windows- "linux" -> Linux- _ -> error $ "Unknown host operating system: " ++ System.os---- | File extension for executables.-executableExtension :: String-executableExtension =- case os of- Windows -> "exe"- _ -> ""---- | File extension for dynamic shared libraries.-sharedLibraryExtension :: String-sharedLibraryExtension =- case os of- Linux -> "so"- OSX -> "dylib"- Windows -> "dll"---- | File extension for dynamic loadable libraries.-loadableLibraryExtension :: String-loadableLibraryExtension =- case os of- Linux -> "so"- OSX -> "bundle"- Windows -> "dll"---- | Get host's default toolchain.------ This function can be used for targeting the host operating system, see also "Development.Shake.Language.C.Rules".-defaultToolChain :: (Target, Action ToolChain)-{-# NOINLINE defaultToolChain #-}-defaultToolChain = unsafePerformIO $ do- -- The assumption here is that target and toolchain don't change while the program is running.- case os of- Linux -> Linux.getDefaultToolChain- OSX -> OSX.getDefaultToolChain- Windows -> Windows.getDefaultToolChain- -- _ -> error $ "No default toolchain for this operating system (" ++ show os ++ ")"
− Development/Shake/Language/C/Label.hs
@@ -1,46 +0,0 @@--- Copyright 2012-2013 Samplecount S.L.------ Licensed under the Apache License, Version 2.0 (the "License");--- you may not use this file except in compliance with the License.--- You may obtain a copy of the License at------ http://www.apache.org/licenses/LICENSE-2.0------ Unless required by applicable law or agreed to in writing, software--- distributed under the License is distributed on an "AS IS" BASIS,--- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.--- See the License for the specific language governing permissions and--- limitations under the License.--{-# LANGUAGE TypeOperators #-}--{-|-Description: Lens utilities--This module provides some lens utilities and also re-exports "Data.Label",-which can avoid a package dependency on @fclabels@ in some cases.--}-module Development.Shake.Language.C.Label (- module Data.Label- , append- , prepend-) where--import Data.Label-import Data.Monoid (Monoid, mappend)---- | Append stuff to the 'Monoid' in record field specified by lens.-append :: Monoid a =>- (f :-> a) -- ^ lens- -> a -- ^ stuff to append- -> f -- ^ original record- -> f -- ^ modified record-append l n = modify l (`mappend` n)---- | Prepend stuff to the 'Monoid' in record field specified by lens.-prepend :: Monoid a =>- (f :-> a) -- ^ lens- -> a -- ^ stuff to prepend- -> f -- ^ original record- -> f -- ^ modified record-prepend l n = modify l (n `mappend`)
− Development/Shake/Language/C/Language.hs
@@ -1,47 +0,0 @@--- Copyright 2012-2014 Samplecount S.L.------ Licensed under the Apache License, Version 2.0 (the "License");--- you may not use this file except in compliance with the License.--- You may obtain a copy of the License at------ http://www.apache.org/licenses/LICENSE-2.0------ Unless required by applicable law or agreed to in writing, software--- distributed under the License is distributed on an "AS IS" BASIS,--- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.--- See the License for the specific language governing permissions and--- limitations under the License.--{-# LANGUAGE TemplateHaskell #-}--module Development.Shake.Language.C.Language (- Language(..)- , defaultLanguageMap- , languageOf-) where--import Development.Shake.FilePath (takeExtension)---- | Source language.------ Currently something derived from @C@.-data Language =- C -- ^ Plain old C- | Cpp -- ^ C++- | ObjC -- ^ Objective-C- | ObjCpp -- ^ Objective-C with C++ (Apple extension)- deriving (Enum, Eq, Show)---- | Default mapping from file extension to source language.-defaultLanguageMap :: [(String, Language)]-defaultLanguageMap = concatMap f [- (C, [".c"])- , (Cpp, [".cc", ".CC", ".cpp", ".CPP", ".C", ".cxx", ".CXX"])- , (ObjC, [".m"])- , (ObjCpp, [".mm", ".M"])- ]- where f (lang, exts) = map (\ext -> (ext, lang)) exts---- | Determine the source language of a file based on its extension.-languageOf :: FilePath -> Maybe Language-languageOf = flip lookup defaultLanguageMap . takeExtension
− Development/Shake/Language/C/PkgConfig.hs
@@ -1,132 +0,0 @@--- Copyright 2012-2013 Samplecount S.L.------ Licensed under the Apache License, Version 2.0 (the "License");--- you may not use this file except in compliance with the License.--- You may obtain a copy of the License at------ http://www.apache.org/licenses/LICENSE-2.0------ Unless required by applicable law or agreed to in writing, software--- distributed under the License is distributed on an "AS IS" BASIS,--- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.--- See the License for the specific language governing permissions and--- limitations under the License.--{-|-Description: Query build flags with @pkg-config@--This module provides utilities for querying 'BuildFlags' from the-<http://www.freedesktop.org/wiki/Software/pkg-config/ pkg-config> database,-which is available on many Unix like operating systems.--}-module Development.Shake.Language.C.PkgConfig (- Options(..)- , defaultOptions- , pkgConfigWithOptions- , pkgConfig- , fromConfig- , fromConfigWithOptions-) where--import Control.Applicative-import Data.Char (toLower)-import Data.List (intercalate, isPrefixOf)-import Development.Shake-import Development.Shake.FilePath-import Development.Shake.Language.C ( BuildFlags- , compilerFlags- , libraries- , libraryPath- , linkerFlags- , systemIncludes- , userIncludes )-import Development.Shake.Language.C.Label (append)-import Development.Shake.Language.C.Util (words')---- ====================================================================--- PkgConfig---- TODO:--- * Use parsec or attoparsec for more robust parser--- * Parse preprocessor defines--- * Parse framework path (-F) and -framework flags--parseCflags :: [String] -> (BuildFlags -> BuildFlags)-parseCflags [] = id-parseCflags (x:xs)- | isPrefixOf "-I" x = parseCflags xs . append systemIncludes [drop 2 x]- | isPrefixOf "-i" x = parseCflags xs . append userIncludes [drop 2 x]- | otherwise = append compilerFlags [(Nothing, [x])]--parseLibs :: [String] -> (BuildFlags -> BuildFlags)-parseLibs [] = id-parseLibs (x:xs)- | isPrefixOf "-l" x = parseLibs xs . append libraries [drop 2 x]- | isPrefixOf "-L" x = parseLibs xs . append libraryPath [drop 2 x]- | otherwise = parseLibs xs . append linkerFlags [x]--parseFlags :: String -> [String]-parseFlags = words' . head . lines---- | PkgConfig options.-data Options = Options {- searchPath :: Maybe [FilePath] -- ^ List of directories where @.pc@ files are searched, corresponding to the @PKG_CONFIG_PATH@ environment variable- , static :: Bool -- ^ Return flags appropriate for static linking- } deriving (Eq, Show)---- | Default options.-defaultOptions :: Options-defaultOptions = Options {- searchPath = Nothing- , static = False- }---- | Call @pkg-config@ with options and a package name and return a 'BuildFlags' modification function.------ The @pkg-config@ executable must be installed on the build host.-pkgConfigWithOptions :: Options -> String -> Action (BuildFlags -> BuildFlags)-pkgConfigWithOptions options pkg = do- env <- case searchPath options of- Nothing -> return []- Just path -> do- env <- addEnv [("PKG_CONFIG_PATH", intercalate [searchPathSeparator] path)]- return [env]- let flags = if static options then ["--static"] else []- pkgconfig which = command ([Traced ""] ++ env) "pkg-config" (flags ++ ["--" ++ which, pkg])- Stdout cflags <- pkgconfig "cflags"- Stdout libs <- pkgconfig "libs"- return ( parseCflags (parseFlags cflags)- . parseLibs (parseFlags libs) )---- | Call @pkg-config@ with default options and a package name and return a 'BuildFlags' modification function.------ The @pkg-config@ executable must be installed on the build host.-pkgConfig :: String -> Action (BuildFlags -> BuildFlags)-pkgConfig = pkgConfigWithOptions defaultOptions---- | Given an initial 'Options' record and a configuration variable lookup function, call @pkg-config@ based on configuration variable settings and return a 'BuildFlags' modification function.------ The following configuration variables are recognised:------ [@PkgConfig.packages@] List of package names for which build flags should be queried--- [@PkgConfig.options.searchPath@] Space-separated list of file paths, corresponds to the `searchPath` option--- [@PkgConfig.options.static@] @true@ or @false@, corresponds to the `static` option-fromConfigWithOptions :: Options -> (String -> Action (Maybe String)) -> Action (BuildFlags -> BuildFlags)-fromConfigWithOptions initialOptions cfg = do- config_searchPath <- fmap words' <$> cfg "PkgConfig.options.searchPath"- config_static <- fmap (bool . words) <$> cfg "PkgConfig.options.static"- config_packages <- fmap words <$> cfg "PkgConfig.packages"- let options = initialOptions {- searchPath = maybe (searchPath initialOptions) Just config_searchPath,- static = maybe (static initialOptions) id config_static- }- flags <- mapM (pkgConfigWithOptions options)- (maybe [] id config_packages)- return $ foldl (.) id $ flags- where- bool (x:_) = map toLower x == "true"- bool _ = False---- | Like `fromConfigWithOptions` but with default options.-fromConfig :: (String -> Action (Maybe String)) -> Action (BuildFlags -> BuildFlags)-fromConfig = fromConfigWithOptions defaultOptions
− Development/Shake/Language/C/Rules.hs
@@ -1,154 +0,0 @@--- Copyright 2012-2014 Samplecount S.L.------ Licensed under the Apache License, Version 2.0 (the "License");--- you may not use this file except in compliance with the License.--- You may obtain a copy of the License at------ http://www.apache.org/licenses/LICENSE-2.0------ Unless required by applicable law or agreed to in writing, software--- distributed under the License is distributed on an "AS IS" BASIS,--- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.--- See the License for the specific language governing permissions and--- limitations under the License.--{-|-Description: High-level Shake rules--This module provides a few high-level rules for building executables and-libraries. Below is an example that builds both a static library and an executable. See-"Development.Shake.Language.C.ToolChain" for examples of toolchain definitions.--> let toolChain = ...-> lib <- staticLibrary toolChain "libexample.a" (pure mempty) (pure ["example_lib.c"])-> exe <- staticLibrary toolChain ("example" <.> exe) (pure mempty) (pure ["example_exe.c"])-> want [lib, exe]--Sometimes you want to structure your project in a set of static libraries that-are later linked into one or more executables. For Shake to recognise the-libraries as dependencies of the executable you need to add them to the-`localLibraries` field of the `BuildFlags` record:--> let toolChain = ...-> buildFlags = ...-> lib <- staticLibrary toolChain "libexample.a"-> (pure buildFlags)-> (pure ["example_lib.c"])-> exe <- executable toolChain ("example" <.> exe)-> (pure $ buildFlags . append localLibraries [lib])-> (pure ["example_exe.c"])-> want [exe]--The rule functions expect their arguments in the 'Action' monad in order to be-able to derive them from side-effecting configuration actions. For example it-can be useful to determine certain toolchain settings either from the-environment, or from configuration files. Using "Control.Applicative" we could-write:--> Android.toolChain-> <$> getEnvWithDefault-> (error "ANDROID_NDK is undefined")-> "ANDROID_NDK"-> <*> pure (Android.sdkVersion 9)-> <*> pure (LLVM, Version [3,4] [])-> <*> pure (Android.target (Arm Armv7))--}--module Development.Shake.Language.C.Rules (- executable- , staticLibrary- , sharedLibrary- , loadableLibrary-) where--import Data.Monoid (mempty)-import Development.Shake-import Development.Shake.FilePath-import Development.Shake.Language.C.BuildFlags as BuildFlags-import Development.Shake.Language.C.ToolChain as ToolChain-import Development.Shake.Language.C.Label (get)--mkObjectsDir :: FilePath -> FilePath-mkObjectsDir path = takeDirectory path </> map tr (takeFileName path) ++ "_obj"- where tr '.' = '_'- tr x = x--buildProduct :: (ToolChain -> Linker)- -> Action ToolChain- -> FilePath- -> Action (BuildFlags -> BuildFlags)- -> Action [FilePath]- -> Rules FilePath-buildProduct getLinker getToolChain result getBuildFlags getSources = do- let objectsDir = mkObjectsDir result- cachedObjects <- newCache $ \() -> do- sources <- getSources- return $ [ objectsDir </> makeRelative "/" (src <.> if isAbsolute src then "abs.o" else "rel.o")- | src <- sources ]- cachedToolChain <- newCache $ \() -> getToolChain- cachedBuildFlags <- newCache $ \() -> do- tc <- cachedToolChain ()- f1 <- get ToolChain.defaultBuildFlags tc- f2 <- getBuildFlags- return $ f2 . f1 $ mempty- result *> \_ -> do- tc <- cachedToolChain ()- flags <- cachedBuildFlags ()- objs <- cachedObjects ()- need objs- (getLinker tc)- tc- flags- objs- result- (dropTrailingPathSeparator objectsDir ++ "//*.o") *> \obj -> do- tc <- cachedToolChain ()- flags <- cachedBuildFlags ()- -- Compute source file name from object file name- -- Using getSources here would result in a dependency of every object file on the list of sources, leading to unnecessary rebuilds.- let src = case splitExtension $ dropExtension $ makeRelative objectsDir obj of- (x, ".abs") -> "/" </> x -- Source file had absolute path- (x, ".rel") -> x- (_, ext) -> error $ "BUG: Unexpected object file extension " ++ ext- (get compiler tc)- tc- flags- src- obj- return result---- TODO: The following result type would be more composable, e.g. allowing for further build product processing:--- Rules (FilePath -> Action ())--- See https://github.com/samplecount/shake-language-c/issues/15---- | Shake rule for building an executable.-executable :: Action ToolChain -- ^ Action returning a target 'ToolChain'- -> FilePath -- ^ Output file- -> Action (BuildFlags -> BuildFlags) -- ^ Action returning a 'BuildFlags' modifier- -> Action [FilePath] -- ^ Action returning a list of input source files- -> Rules FilePath -- ^ Rule returning the output file path-executable toolChain = buildProduct (flip (get linker) Executable) toolChain---- | Shake rule for building a static library.-staticLibrary :: Action ToolChain -- ^ Action returning a target 'ToolChain'- -> FilePath -- ^ Output file- -> Action (BuildFlags -> BuildFlags) -- ^ Action returning a 'BuildFlags' modifier- -> Action [FilePath] -- ^ Action returning a list of input source files- -> Rules FilePath -- ^ Rule returning the output file path-staticLibrary toolChain = buildProduct (get archiver) toolChain---- | Shake rule for building a shared (dynamically linked) library.-sharedLibrary :: Action ToolChain -- ^ Action returning a target 'ToolChain'- -> FilePath -- ^ Output file- -> Action (BuildFlags -> BuildFlags) -- ^ Action returning a 'BuildFlags' modifier- -> Action [FilePath] -- ^ Action returning a list of input source files- -> Rules FilePath -- ^ Rule returning the output file path-sharedLibrary toolChain = buildProduct (flip (get linker) SharedLibrary) toolChain---- | Shake rule for building a dynamically loadable library.-loadableLibrary :: Action ToolChain -- ^ Action returning a target 'ToolChain'- -> FilePath -- ^ Output file- -> Action (BuildFlags -> BuildFlags) -- ^ Action returning a 'BuildFlags' modifier- -> Action [FilePath] -- ^ Action returning a list of input source files- -> Rules FilePath -- ^ Rule returning the output file path-loadableLibrary toolChain = buildProduct (flip (get linker) LoadableLibrary) toolChain
− Development/Shake/Language/C/Target.hs
@@ -1,104 +0,0 @@--- Copyright 2012-2014 Samplecount S.L.------ Licensed under the Apache License, Version 2.0 (the "License");--- you may not use this file except in compliance with the License.--- You may obtain a copy of the License at------ http://www.apache.org/licenses/LICENSE-2.0------ Unless required by applicable law or agreed to in writing, software--- distributed under the License is distributed on an "AS IS" BASIS,--- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.--- See the License for the specific language governing permissions and--- limitations under the License.--module Development.Shake.Language.C.Target (- OS(..)- , Platform(..)- , ArmVersion(..)- , X86Version(..)- , Arch(..)- , archString- , Target(..)- , ToBuildPrefix(..)-) where--import Data.Char (toLower)-import Development.Shake.FilePath---- | Target operating system.-data OS =- Android -- ^ Google Android- | Linux -- ^ GNU Linux- | OSX -- ^ Apple Mac OSX and iOS- | Pepper -- ^ Google Portable Native Client (PNaCl)- | Windows -- ^ Microsoft Windows- deriving (Eq, Ord, Show)---- | Target platform.------ Basically just a platform identifier string.-data Platform = Platform {- platformName :: String- } deriving (Eq, Show)---- | `X86` architecture version.-data X86Version =- I386 -- ^ @i386@, 32-bit architecture without @SSE@- | I686 -- ^ @i686@, 32-bit architecture with @SSE@ (/Pentium-Pro/)- | X86_64 -- ^ @x86_64@, 64-bit architecture- deriving (Eq, Show)---- | `Arm` architecture version.-data ArmVersion =- Armv5- | Armv6- | Armv7- | Armv7s- deriving (Eq, Show)---- | Target architecture.-data Arch =- X86 X86Version -- ^ Intel @x86@ architecture- | Arm ArmVersion -- ^ Arm architecture- | LLVM_IR -- ^ LLVM intermediate representation, used by `Pepper` (PNaCl)- deriving (Eq, Show)---- | Architecture short string.------ Mainly useful for constructing build output directories.-archString :: Arch -> String-archString arch =- case arch of- X86 I386 -> "i386"- X86 I686 -> "i686"- X86 X86_64 -> "x86_64"- Arm Armv5 -> "armv5"- Arm Armv6 -> "armv6"- Arm Armv7 -> "armv7"- Arm Armv7s -> "armv7s"- LLVM_IR -> "llvm_ir"---- | Compilation target triple consisting of operating system, platform and architecture.-data Target = Target {- targetOS :: OS -- ^ Target operating system- , targetPlatform :: Platform -- ^ Target platform- , targetArch :: Arch -- ^ Target architecture- } deriving (Show)---- | Convert a value to a build directory prefix.------ The idea is that several such values can be combined to form more complex build directory hierarchies. This can be important for disambiguating build product paths in Shake rules.-class ToBuildPrefix a where- toBuildPrefix :: a -> FilePath--instance ToBuildPrefix Platform where- toBuildPrefix = map toLower . platformName--instance ToBuildPrefix Arch where- toBuildPrefix = archString--instance ToBuildPrefix Target where- toBuildPrefix target =- toBuildPrefix (targetPlatform target)- </> toBuildPrefix (targetArch target)
− Development/Shake/Language/C/Target/Android.hs
@@ -1,202 +0,0 @@--- Copyright 2012-2013 Samplecount S.L.------ Licensed under the Apache License, Version 2.0 (the "License");--- you may not use this file except in compliance with the License.--- You may obtain a copy of the License at------ http://www.apache.org/licenses/LICENSE-2.0------ Unless required by applicable law or agreed to in writing, software--- distributed under the License is distributed on an "AS IS" BASIS,--- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.--- See the License for the specific language governing permissions and--- limitations under the License.--{-|-Description: Toolchain definitions and utilities for Android--This module provides toolchain definitions and utilities for targeting Android.-See "Development.Shake.Language.C.Rules" for examples of how to use a target-toolchain.--}-module Development.Shake.Language.C.Target.Android (- target- , sdkVersion- , toolChain- , abiString- , gnustl- , libcxx- , native_app_glue-) where--import Control.Arrow-import Development.Shake.FilePath-import Data.Version (Version(..), showVersion)-import Development.Shake.Language.C.BuildFlags-import Development.Shake.Language.C.Target-import Development.Shake.Language.C.Label-import Development.Shake.Language.C.Language-import Development.Shake.Language.C.ToolChain-import qualified System.Info as System--unsupportedArch :: Arch -> a-unsupportedArch arch = error $ "Unsupported Android target architecture " ++ archString arch--toolChainPrefix :: Target -> String-toolChainPrefix x =- case targetArch x of- X86 _ -> "x86-"- Arm _ -> "arm-linux-androideabi-"- arch -> unsupportedArch arch--osPrefix :: String-osPrefix = System.os ++ "-" ++ cpu- where cpu = case System.arch of- "i386" -> "x86"- arch -> arch---- | Android target for architecture.-target :: Arch -> Target-target = Target Android (Platform "android")--mkDefaultBuildFlags :: FilePath -> Version -> Arch -> BuildFlags -> BuildFlags-mkDefaultBuildFlags ndk version arch =- append compilerFlags [(Nothing, [sysroot, march])]- >>> append compilerFlags (archCompilerFlags arch)- >>> append compilerFlags [(Nothing, [- "-fpic"- , "-ffunction-sections"- , "-funwind-tables"- , "-fstack-protector"- , "-no-canonical-prefixes"])]- >>> append linkerFlags [sysroot, march]- >>> append linkerFlags (archLinkerFlags arch)- >>> append linkerFlags ["-Wl,--no-undefined", "-Wl,-z,relro", "-Wl,-z,now"]- >>> append linkerFlags ["-no-canonical-prefixes"]- >>> append archiverFlags ["crs"]- where- sysroot = "--sysroot="- ++ ndk- </> "platforms"- </> "android-" ++ show (head (versionBranch version))- </> "arch-" ++ case arch of- (X86 _) -> "x86"- (Arm _) -> "arm"- _ -> unsupportedArch arch- march = "-march=" ++ case arch of- (Arm Armv5) -> "armv5te"- (Arm Armv6) -> "armv5te"- (Arm Armv7) -> "armv7-a"- _ -> archString arch- archCompilerFlags (Arm Armv7) = [(Nothing, ["-mfloat-abi=softfp", "-mfpu=neon" {- vfpv3-d16 -}])]- archCompilerFlags (Arm _) = [(Nothing, ["-mtune=xscale", "-msoft-float"])]- archCompilerFlags _ = []- archLinkerFlags (Arm Armv7) = ["-Wl,--fix-cortex-a8"]- archLinkerFlags _ = []---- | Construct a version record from an integral Android SDK version.------ prop> sdkVersion 19 == Version [19] []-sdkVersion :: Int -> Version-sdkVersion n = Version [n] []---- | Construct an Android toolchain.-toolChain :: FilePath -- ^ NDK source directory- -> Version -- ^ SDK version, see `sdkVersion`- -> (ToolChainVariant, Version) -- ^ Toolchain variant and version- -> Target -- ^ Build target, see `target`- -> ToolChain -- ^ Resulting toolchain-toolChain "" _ (_, _) _ = error "Empty NDK directory"-toolChain ndk version (GCC, tcVersion) t =- set variant GCC- $ set toolDirectory (Just (ndk </> "toolchains"- </> toolChainPrefix t ++ showVersion tcVersion- </> "prebuilt"- </> osPrefix- </> "bin"))- $ set toolPrefix (toolChainPrefix t)- $ set compilerCommand "gcc"- $ set archiverCommand "ar"- $ set linkerCommand "g++"- $ set defaultBuildFlags (return $ mkDefaultBuildFlags ndk version (targetArch t))- $ defaultToolChain-toolChain ndk version (LLVM, tcVersion) t =- set variant LLVM- $ set toolDirectory (Just (ndk </> "toolchains"- </> "llvm-" ++ showVersion tcVersion- </> "prebuilt"- </> osPrefix- </> "bin"))- $ set compilerCommand "clang"- $ set archiverCommand "llvm-ar"- $ set linkerCommand "clang++"- $ set defaultBuildFlags (return $- let gcc_toolchain = ndk </> "toolchains/arm-linux-androideabi-4.8/prebuilt" </> osPrefix- flags = ["-target", llvmTarget t, "-gcc-toolchain", gcc_toolchain]- in mkDefaultBuildFlags ndk version (targetArch t)- . append compilerFlags [(Nothing, flags)]- . append linkerFlags flags- )- $ defaultToolChain- where- llvmTarget x =- case targetArch x of- Arm Armv5 -> "armv5te-none-linux-androideabi"- Arm Armv7 -> "armv7-none-linux-androideabi"- X86 I386 -> "i686-none-linux-android"- arch -> unsupportedArch arch-toolChain _ _ (tcVariant, tcVersion) _ =- error $ "Unknown toolchain variant "- ++ show tcVariant ++ " "- ++ showVersion tcVersion---- | Valid Android ABI identifier for the given architecture.-abiString :: Arch -> String-abiString (Arm Armv5) = "armeabi"-abiString (Arm Armv6) = "armeabi"-abiString (Arm Armv7) = "armeabi-v7a"-abiString (X86 _) = "x86"-abiString arch = unsupportedArch arch---- | Source paths and build flags for the @native_app_glue@ module.-native_app_glue :: FilePath -- ^ NDK source directory- -> ([FilePath], BuildFlags -> BuildFlags)-native_app_glue ndk =- ( [ndk </> "sources/android/native_app_glue/android_native_app_glue.c"]- , append systemIncludes [ndk </> "sources/android/native_app_glue"] )---- | Build flags for building with and linking against the GNU @gnustl@ standard C++ library.-gnustl :: Version -- ^ GNU STL version- -> Linkage -- ^ `Static` or `Shared`- -> FilePath -- ^ NDK source directory- -> Target -- ^ Build target, see `target`- -> (BuildFlags -> BuildFlags) -- ^ 'BuildFlags' modification function-gnustl version linkage ndk t =- append systemIncludes [stlPath </> "include", stlPath </> "libs" </> abi </> "include"]- . append libraryPath [stlPath </> "libs" </> abi]- . append libraries [lib]- where stlPath = ndk </> "sources/cxx-stl/gnu-libstdc++" </> showVersion version- abi = abiString (targetArch t)- lib = case linkage of- Static -> "gnustl_static"- Shared -> "gnustl_shared"---- | Build flags for building with and linking against the LLVM @libc++@ standard C++ library.-libcxx :: Linkage -- ^ `Static` or `Shared`- -> FilePath -- ^ NDK source directory- -> Target -- ^ Build target, see `target`- -> (BuildFlags -> BuildFlags) -- ^ 'BuildFlags' modification function-libcxx linkage ndk t =- append systemIncludes [ libcxxPath </> "libcxx" </> "include"- -- NOTE: libcxx needs to be first in include path!- , stlPath </> "gabi++" </> "include"- , ndk </> "sources" </> "android" </> "support" </> "include" ]- . append compilerFlags [(Just Cpp, ["-stdlib=libc++"])]- . append libraryPath [libcxxPath </> "libs" </> abi]- . prepend libraries [lib]- where stlPath = ndk </> "sources" </> "cxx-stl"- libcxxPath = stlPath </> "llvm-libc++"- abi = abiString (targetArch t)- lib = case linkage of- Static -> "c++_static"- Shared -> "c++_shared"
− Development/Shake/Language/C/Target/Linux.hs
@@ -1,80 +0,0 @@--- Copyright 2012-2013 Samplecount S.L.------ Licensed under the Apache License, Version 2.0 (the "License");--- you may not use this file except in compliance with the License.--- You may obtain a copy of the License at------ http://www.apache.org/licenses/LICENSE-2.0------ Unless required by applicable law or agreed to in writing, software--- distributed under the License is distributed on an "AS IS" BASIS,--- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.--- See the License for the specific language governing permissions and--- limitations under the License.--{-|-Description: Toolchain definitions and utilities for Linux--This module provides toolchain definitions and utilities for targeting Linux.-See "Development.Shake.Language.C.Rules" for examples of how to use a target-toolchain.--Linux is also a supported host operating system, see-"Development.Shake.Language.C.Host" for examples of how to target the host.--}--module Development.Shake.Language.C.Target.Linux (- toolChain- , getDefaultToolChain-) where--import Data.Label (get, set)-import Development.Shake-import Development.Shake.Language.C.BuildFlags-import Development.Shake.Language.C.Target-import Development.Shake.Language.C.ToolChain-import System.Process (readProcess)--getHostArch :: IO Arch-getHostArch = do- arch <- fmap (head.lines) $ readProcess "arch" [] ""- return $ case arch of- "i386" -> X86 I386- "i686" -> X86 I686- "x86_64" -> X86 X86_64- _ -> error $ "Unknown host architecture " ++ arch--target :: Arch -> Target-target = Target Linux (Platform "linux")--platformArchiver :: Archiver-platformArchiver tc buildFlags inputs output = do- need inputs- command_ [] (tool tc archiverCommand)- $ ["cr"]- ++ get archiverFlags buildFlags- ++ [output]- ++ inputs- command_ [] (toolFromString tc "ranlib") [output]---- | Linux toolchain.-toolChain :: ToolChainVariant -> ToolChain-toolChain GCC =- set variant GCC- $ set compilerCommand "gcc"- $ set archiverCommand "ar"- $ set archiver platformArchiver- $ set linkerCommand "g++"- $ defaultToolChain-toolChain LLVM =- set variant LLVM- $ set compilerCommand "gcc"- $ set archiverCommand "ar"- $ set linkerCommand "g++"- $ defaultToolChain-toolChain Generic = toolChain GCC--getDefaultToolChain :: IO (Target, Action ToolChain)-getDefaultToolChain = do- t <- fmap target getHostArch- return (t, return $ toolChain GCC)
− Development/Shake/Language/C/Target/NaCl.hs
@@ -1,193 +0,0 @@--- Copyright 2013 Samplecount S.L.------ Licensed under the Apache License, Version 2.0 (the "License");--- you may not use this file except in compliance with the License.--- You may obtain a copy of the License at------ http://www.apache.org/licenses/LICENSE-2.0------ Unless required by applicable law or agreed to in writing, software--- distributed under the License is distributed on an "AS IS" BASIS,--- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.--- See the License for the specific language governing permissions and--- limitations under the License.--{-|-Description: Toolchain definitions and utilities for Google Pepper and Portable Native Client--This module provides toolchain definitions and utilities for targeting Google-Pepper and Portable Native Client (PNaCl). Arguably it should be renamed-appropriately. See "Development.Shake.Language.C.Rules" for examples of how to-use a target toolchain.--}-module Development.Shake.Language.C.Target.NaCl (- pepper- , canary- , target- , Config(..)- , toolChain- , finalize- , translate- , libppapi- , libppapi_cpp- , libnacl_io- , libppapi_simple- , Arch(..)- , mk_nmf-) where--import Data.List (intercalate)-import Development.Shake-import Development.Shake.FilePath-import Data.Version (Version(..))-import Development.Shake.Language.C hiding (Arch)-import qualified Development.Shake.Language.C.Target as C-import Development.Shake.Language.C.ToolChain-import qualified Development.Shake.Language.C.Host as Host-import Development.Shake.Language.C.Label---- | Stable /Pepper/ API version.-pepper :: Int -> Version-pepper apiVersion = Version [apiVersion] []---- | Unstable /Pepper Canary/ API version.-canary :: Version-canary = Version [] ["canary"]---- | Pepper target.------ `LLVM_IR` (PNaCl) is the only supported target architecture at the moment.-target :: Target-target = Target Pepper (Platform "pepper") LLVM_IR--hostString :: String-hostString =- case Host.os of- Host.Linux -> "linux"- Host.OSX -> "mac"- Host.Windows -> "win"---- | Pepper build configuration.------ This is used to select the respective library versions when linking.-data Config = Debug | Release deriving (Eq, Show)---- | Construct Pepper toolchain.-toolChain :: FilePath -- ^ Pepper SDK directory (@nacl_sdk@)- -> Version -- ^ Pepper API version, see `pepper` and `canary`- -> Config -- ^ Build configuration for linked libraries- -> Target -- ^ Target, see `target`- -> ToolChain -- ^ Resulting toolchain-toolChain sdk sdkVersion config t =- set variant LLVM- $ set toolDirectory (Just (platformDir </> "toolchain" </> hostString ++ "_" ++ "pnacl" </> "bin"))- $ set toolPrefix "pnacl-"- $ set compilerCommand "clang"- $ set archiverCommand "ar"- $ set archiver (\tc flags inputs output -> do- need inputs- command_ [] (tool tc archiverCommand)- $ ["cr"]- ++ get archiverFlags flags- ++ [output]- ++ inputs- command_ [] (toolFromString tc "ranlib") [output]- )- $ set linkerCommand "clang++"- $ set defaultBuildFlags- ( return $- append systemIncludes [includeDir]- . append userIncludes [includeDir]- . append systemIncludes [includeDir </> "pnacl"]- . append libraryPath [platformDir </> "lib" </> "pnacl" </> show config] )- $ defaultToolChain- where- platformDir =- sdk- </> platformName (targetPlatform t)- ++ "_"- ++ case versionTags sdkVersion of- ["canary"] -> "canary"- _ -> show $ head (versionBranch sdkVersion)- includeDir = platformDir </> "include"---- | Finalize a bit code executable.-finalize :: ToolChain -- ^ Toolchain, see `toolChain`- -> FilePath -- ^ Bit code input executable- -> FilePath -- ^ Finalised bit code output executable- -> Action ()-finalize tc input output = do- need [input]- command_ [] (toolFromString tc "finalize")- ["-o", output, input]---- | Translate bit code to native code.-translate :: ToolChain -> C.Arch -> FilePath -> FilePath -> Action ()-translate tc arch input output = do- let archName =- case arch of- X86 I686 -> "i686"- X86 X86_64 -> "x86-64"- Arm Armv7 -> "armv7"- _ -> error $ "Unsupported architecture: " ++ show arch- need [input]- command_ [] (toolFromString tc "finalize")- ["-arch", archName, "-o", output, input]---- | Link against the Pepper C API library.-libppapi :: BuildFlags -> BuildFlags-libppapi = append libraries ["ppapi"]---- | Link against the Pepper C++ API library.-libppapi_cpp :: BuildFlags -> BuildFlags-libppapi_cpp = append libraries ["ppapi_cpp"]---- | Link against @libnacl_io@.-libnacl_io :: BuildFlags -> BuildFlags-libnacl_io = append libraries ["nacl_io"]---- | Link against the Simple Pepper C API library.-libppapi_simple :: BuildFlags -> BuildFlags-libppapi_simple = append libraries ["ppapi_simple"]---- | Pepper target architecture-data Arch =- PNaCl -- ^ Portable Native Client architecture- | NaCl C.Arch -- ^ Native Client architecture for specific CPU architecture- deriving (Eq, Show)---- | Create Native Client Manifest (nmf) file.------ This file is needed for serving NaCl/PNaCl outside the Google Play store. See the native client <https://developer.chrome.com/native-client/reference/nacl-manifest-format documentation> for more information on the file format.-mk_nmf :: [(Arch, FilePath)] -- ^ List of executables with the corresponding architecture- -> FilePath -- ^ Output file- -> Action ()-mk_nmf inputs output = do- need $ map snd inputs- writeFileLines output $ [- "{"- , " \"program\": {"- ]- ++ intercalate [","] (map program inputs) ++- [ " }"- , " }"- , "}"- ]- where- program (PNaCl, input) = [- " \"portable\": {"- , " \"pnacl-translate\": {"- , " \"url\": \"" ++ makeRelative (takeDirectory output) input ++ "\""- , " }"- ]- program (NaCl arch, input) =- let archName = case arch of- X86 X86_64 -> "x86_64"- X86 _ -> "x86_32"- Arm _ -> "arm"- _ -> error $ "mk_nmf: Unsupported architecture " ++ show arch- in [- " \"" ++ archName ++ "\": {"- , " \"url\": \"" ++ makeRelative (takeDirectory output) input ++ "\""- , " }"- ]
− Development/Shake/Language/C/Target/OSX.hs
@@ -1,190 +0,0 @@--- Copyright 2012-2013 Samplecount S.L.------ Licensed under the Apache License, Version 2.0 (the "License");--- you may not use this file except in compliance with the License.--- You may obtain a copy of the License at------ http://www.apache.org/licenses/LICENSE-2.0------ Unless required by applicable law or agreed to in writing, software--- distributed under the License is distributed on an "AS IS" BASIS,--- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.--- See the License for the specific language governing permissions and--- limitations under the License.--{-|-Description: Toolchain definitions and utilities for OSX and iOS--This module provides toolchain definitions and utilities for targeting OSX-and iOS. See "Development.Shake.Language.C.Rules" for examples of how to use a-target toolchain.--OSX is also a supported host operating system, see-"Development.Shake.Language.C.Host" for examples of how to target the host.--}--module Development.Shake.Language.C.Target.OSX (- DeveloperPath- , getSDKRoot- , macOSX- , iPhoneOS- , iPhoneSimulator- , target- , sdkVersion- , toolChain- , getPlatformVersions- , getDefaultToolChain- , macosx_version_min- , iphoneos_version_min- , universalBinary-) where--import Control.Applicative hiding ((*>))-import Data.List (stripPrefix)-import Data.List.Split (splitOn)-import Data.Maybe-import Data.Version (Version(..), showVersion)-import Development.Shake as Shake-import Development.Shake.FilePath-import Development.Shake.Language.C.BuildFlags-import Development.Shake.Language.C.Target-import Development.Shake.Language.C.Label-import Development.Shake.Language.C.ToolChain-import System.Process (readProcess)-import Text.Read (readEither)--archFlags :: Target -> [String]-archFlags t = ["-arch", archString (targetArch t)]---- | Base path of development tools on OSX.-newtype DeveloperPath = DeveloperPath FilePath- deriving (Show)---- | Get base path of development tools on OSX.-getSDKRoot :: Action DeveloperPath-getSDKRoot = liftIO $- (DeveloperPath . head . splitOn "\n")- <$> readProcess "xcode-select" ["--print-path"] ""---- | Mac OSX platform.-macOSX :: Platform-macOSX = Platform "MacOSX"---- | iOS platform.-iPhoneOS :: Platform-iPhoneOS = Platform "iPhoneOS"---- | iOS simulator platform.-iPhoneSimulator :: Platform-iPhoneSimulator = Platform "iPhoneSimulator"---- | Build target given a platform and an architecture.-target :: Platform -> Arch -> Target-target = Target OSX--sdkDirectory :: FilePath -> Platform -> FilePath-sdkDirectory sdkRoot platform =- sdkRoot- </> "Platforms"- </> (platformName platform ++ ".platform")- </> "Developer"- </> "SDKs"--platformSDKPath :: FilePath -> Platform -> Version -> FilePath-platformSDKPath sdkRoot platform version =- sdkDirectory sdkRoot platform- </> platformName platform ++ showVersion version ++ ".sdk"--getPlatformVersionsWithRoot :: Platform -> DeveloperPath -> Action [Version]-getPlatformVersionsWithRoot platform (DeveloperPath sdkRoot) = do- dirs <- getDirectoryDirs (sdkDirectory sdkRoot platform)- case mapMaybe (stripPrefix name) dirs of- [] -> error $ "OSX: No SDK found for " ++ name- xs -> return [ flip Version []- . map (either error id . readEither)- . splitOn "."- . dropExtension $ x- | x <- xs ]- where name = platformName platform---- | Return a list of available platform SDK versions.------ For example in order to get the latest iOS SDK version:------ > maximum <$> getPlatformVersions iPhoneOS-getPlatformVersions :: Platform -> Action [Version]-getPlatformVersions platform =- getPlatformVersionsWithRoot platform =<< getSDKRoot---- | Get OSX system version (first two digits).-systemVersion :: Action Version-systemVersion = liftIO $- flip Version []- <$> (map read . take 2 . splitOn ".")- <$> readProcess "sw_vers" ["-productVersion"] ""--getDefaultToolChain :: IO (Target, Action ToolChain)-getDefaultToolChain = do- let defaultTarget = target macOSX (X86 X86_64)- return ( defaultTarget- , toolChain- <$> getSDKRoot- <*> systemVersion- <*> pure defaultTarget )---- | SDK version given major and minor version numbers.-sdkVersion :: Int -> Int -> Version-sdkVersion major minor = Version [major, minor] []---- | Construct an OSX or iOS toolchain.-toolChain :: DeveloperPath -- ^ Developer tools base path, see `getSDKRoot`- -> Version -- ^ Target SDK version- -> Target -- ^ Build target, see `target`- -> ToolChain -- ^ Resulting toolchain-toolChain (DeveloperPath sdkRoot) version t =- set variant LLVM- $ set toolDirectory (Just (sdkRoot </> "Toolchains/XcodeDefault.xctoolchain/usr/bin"))- $ set compilerCommand "clang"- $ set archiverCommand "libtool"- $ set archiver (\tc flags inputs output -> do- need inputs- command_ [] (tool tc archiverCommand)- $ get archiverFlags flags- ++ ["-static"]- ++ ["-o", output]- ++ inputs- )- $ set linkerCommand "clang++"- $ set linker (\lr tc ->- case lr of- Executable -> defaultLinker tc- SharedLibrary -> defaultLinker tc . prepend linkerFlags ["-dynamiclib"]- LoadableLibrary -> defaultLinker tc . prepend linkerFlags ["-bundle"]- )- $ set defaultBuildFlags- ( return $- append preprocessorFlags [ "-isysroot", sysRoot ]- . append compilerFlags [(Nothing, archFlags t)]- . append linkerFlags (archFlags t ++ [ "-isysroot", sysRoot ]) )- $ defaultToolChain- where sysRoot = platformSDKPath sdkRoot (targetPlatform t) version---- | Specify the @-mmacosx-version-min@ compiler flag.-macosx_version_min :: Version -> BuildFlags -> BuildFlags-macosx_version_min version =- append compilerFlags [(Nothing, ["-mmacosx-version-min=" ++ showVersion version])]---- | Specify the @-miphoneos-version-min@ compiler flag.-iphoneos_version_min :: Version -> BuildFlags -> BuildFlags-iphoneos_version_min version =- append compilerFlags [(Nothing, ["-miphoneos-version-min=" ++ showVersion version])]---- | Create a universal binary a list of input files.------ Calls <http://www.manpages.info/macosx/lipo.1.html lipo> with the @-create@ option.-universalBinary :: [FilePath] -- ^ Input files, can be executables, dynamic libraries or static archives but should be all of the same type- -> FilePath -- ^ Output file path- -> Action ()-universalBinary inputs output = do- need inputs- command_ [] "lipo" $ ["-create", "-output", output] ++ inputs
− Development/Shake/Language/C/Target/Windows.hs
@@ -1,82 +0,0 @@--- Copyright 2014 Samplecount S.L.------ Licensed under the Apache License, Version 2.0 (the "License");--- you may not use this file except in compliance with the License.--- You may obtain a copy of the License at------ http://www.apache.org/licenses/LICENSE-2.0------ Unless required by applicable law or agreed to in writing, software--- distributed under the License is distributed on an "AS IS" BASIS,--- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.--- See the License for the specific language governing permissions and--- limitations under the License.--{-|-Description: Toolchain definitions and utilities for Windows--This module provides toolchain definitions and utilities for targeting Windows.-See "Development.Shake.Language.C.Rules" for examples of how to use a target-toolchain.--Windows is also a supported host operating system, see-"Development.Shake.Language.C.Host" for examples of how to target the host.--On Windows currently only the <http://www.mingw.org/ MinGW> toolchain is-supported.--}--module Development.Shake.Language.C.Target.Windows (- toolChain- , getDefaultToolChain-) where--import Data.Label (get, set)-import Development.Shake-import Development.Shake.Language.C.BuildFlags-import Development.Shake.Language.C.Target-import Development.Shake.Language.C.ToolChain-import qualified System.Info as System--getHostArch :: IO Arch-getHostArch = do- -- TODO: Get the info from the environment- let arch = System.arch- return $ case arch of- "i386" -> X86 I386- "i686" -> X86 I686- "x86_64" -> X86 X86_64- _ -> error $ "Unknown host architecture " ++ arch--target :: Arch -> Target-target = Target Windows (Platform "windows")---- | Windows toolchain.-toolChain :: ToolChainVariant -> ToolChain-toolChain GCC =- set variant GCC- $ set compilerCommand "gcc"- $ set archiverCommand "ar"- $ set archiver (\tc flags inputs output -> do- need inputs- command_ [] (tool tc archiverCommand)- $ ["cr"]- ++ get archiverFlags flags- ++ [output]- ++ inputs- command_ [] (toolFromString tc "ranlib") [output]- )- $ set linkerCommand "g++"- $ defaultToolChain-toolChain LLVM =- set variant LLVM- $ set compilerCommand "gcc"- $ set archiverCommand "ar"- $ set linkerCommand "g++"- $ defaultToolChain-toolChain Generic = toolChain GCC--getDefaultToolChain :: IO (Target, Action ToolChain)-getDefaultToolChain = do- t <- fmap target getHostArch- return (t, return $ toolChain Generic)
− Development/Shake/Language/C/ToolChain.hs
@@ -1,271 +0,0 @@--- Copyright 2012-2014 Samplecount S.L.------ Licensed under the Apache License, Version 2.0 (the "License");--- you may not use this file except in compliance with the License.--- You may obtain a copy of the License at------ http://www.apache.org/licenses/LICENSE-2.0------ Unless required by applicable law or agreed to in writing, software--- distributed under the License is distributed on an "AS IS" BASIS,--- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.--- See the License for the specific language governing permissions and--- limitations under the License.--{-# LANGUAGE TemplateHaskell #-}-{-# LANGUAGE TypeOperators #-}--{-|-Description: Types and functions for working with target tool chains--}--module Development.Shake.Language.C.ToolChain (- Linkage(..)- -- ** Working with toolchains- , ToolChain- , ToolChainVariant(..)- , toolDirectory -- | Directory prefix for tools in a `ToolChain`, e.g. @\/usr\/local\/linux-armv5-eabi\/bin@.- , toolPrefix -- | Prefix string for tools in a `ToolChain`, e.g. @"linux-armv5-eabi-"@.- , variant -- | Toolchain variant.- , compilerCommand -- | Compiler command, usually used in the `compiler` action.- , Compiler- , compiler -- | Compiler action for this `ToolChain`.- , archiverCommand -- | Archiver command, usually used in the `archiver` action.- , Archiver- , archiver -- | Archiver action for this `ToolChain`.- , linkerCommand -- | Linker command, usually used in the `linker` action.- , Linker- , LinkResult(..)- , linker -- | Linker action for this `ToolChain`.- , defaultBuildFlags -- | Action returning the default `BuildFlags` for this `ToolChain`.-- -- ** Interfacing with other build systems- , applyEnv- , toEnv- -- ** Utilities for toolchain writers- , defaultToolChain- , defaultCompiler- , defaultArchiver- , defaultLinker- , toolFromString- , tool-) where--import Control.Applicative-import Data.Char (toLower)-import Data.List (isInfixOf, isSuffixOf)-import Data.Monoid (mempty)-import Development.Shake-import Development.Shake.FilePath-import Development.Shake.Util (needMakefileDependencies)-import Development.Shake.Language.C.Label-import Development.Shake.Language.C.BuildFlags-import Development.Shake.Language.C.Language (Language(..), languageOf)-import Development.Shake.Language.C.Util---- | Linkage type, static or shared.-data Linkage = Static | Shared deriving (Enum, Eq, Show)---- | Link result type-data LinkResult =- Executable -- ^ Executable- | SharedLibrary -- ^ Shared (dynamically linked) library- | LoadableLibrary -- ^ Dynamically loadable library- deriving (Enum, Eq, Show)---- | Toolchain variant.-data ToolChainVariant =- Generic -- ^ Unspecified toolchain- | GCC -- ^ GNU Compiler Collection (gcc) toolchain- | LLVM -- ^ Low-Level Virtual Machine (LLVM) toolchain- deriving (Eq, Show)---- | `Action` type for producing an object file from a source file.-type Compiler =- ToolChain -- ^ Toolchain- -> BuildFlags -- ^ Compiler flags- -> FilePath -- ^ Input source file- -> FilePath -- ^ Output object file- -> Action ()---- | `Action` type for linking object files into an executable or a library.-type Linker =- ToolChain -- ^ Toolchain- -> BuildFlags -- ^ Linker flags- -> [FilePath] -- ^ Input object files- -> FilePath -- ^ Output link product- -> Action ()---- | `Action` type for archiving object files into a static library.-type Archiver =- ToolChain -- ^ Toolchain- -> BuildFlags -- ^ Archiver flags- -> [FilePath] -- ^ Input object files- -> FilePath -- ^ Output object archive (static library)- -> Action ()--data ToolChain = ToolChain {- _variant :: ToolChainVariant- , _toolDirectory :: Maybe FilePath- , _toolPrefix :: String- , _compilerCommand :: FilePath- , _compiler :: Compiler- , _archiverCommand :: FilePath- , _archiver :: Archiver- , _linkerCommand :: FilePath- , _linker :: LinkResult -> Linker- , _defaultBuildFlags :: Action (BuildFlags -> BuildFlags)- }--mkLabel ''ToolChain---- | Default compiler action.-defaultCompiler :: Compiler-defaultCompiler toolChain buildFlags input output = do- need $ [input]- let depFile = output <.> "d"- command_ [] (tool toolChain compilerCommand)- $ concatMapFlag "-I" (get systemIncludes buildFlags)- ++ mapFlag "-iquote" (get userIncludes buildFlags)- ++ defineFlags buildFlags- ++ get preprocessorFlags buildFlags- ++ compilerFlagsFor (languageOf input) buildFlags- ++ ["-MD", "-MF", depFile]- ++ ["-c", "-o", output, input]- needMakefileDependencies depFile---- | Default archiver action.-defaultArchiver :: Archiver-defaultArchiver toolChain buildFlags inputs output = do- need inputs- command_ [] (tool toolChain archiverCommand)- $ get archiverFlags buildFlags- ++ [output]- ++ inputs---- | Default linker action.-defaultLinker :: Linker-defaultLinker toolChain buildFlags inputs output = do- let localLibs = get localLibraries buildFlags- buildFlags' = append libraryPath (map takeDirectory localLibs)- -- Local libraries must be passed to the linker before system libraries they depend on- . prepend libraries (map (strip.dropExtension.takeFileName) localLibs)- $ buildFlags- need $ inputs ++ localLibs- command_ [] (tool toolChain linkerCommand)- $ inputs- ++ get linkerFlags buildFlags'- ++ concatMapFlag "-L" (get libraryPath buildFlags')- ++ concatMapFlag "-l" (get libraries buildFlags')- ++ ["-o", output]- where- strip ('l':'i':'b':rest) = rest- strip x = x---- | Default toolchain.------ Probably not useful without modification.-defaultToolChain :: ToolChain-defaultToolChain =- ToolChain {- _variant = GCC- , _toolDirectory = Nothing- , _toolPrefix = ""- , _compilerCommand = "gcc"- , _compiler = defaultCompiler- , _archiverCommand = "ar"- , _archiver = defaultArchiver- , _linkerCommand = "gcc"- , _linker = \linkResult linkerCmd ->- case linkResult of- Executable -> defaultLinker linkerCmd- _ -> defaultLinker linkerCmd . append linkerFlags ["-shared"]- , _defaultBuildFlags = return id- }---- | Given a tool chain command name, construct the command's full path, taking into account the toolchain's `toolPrefix`.-toolFromString ::- ToolChain -- ^ Toolchain- -> String -- ^ Command name- -> FilePath -- ^ Full command path-toolFromString toolChain name =- let c = _toolPrefix toolChain ++ name- in maybe c (</> c) (_toolDirectory toolChain)---- | Construct the full path of a predefined tool given a `ToolChain` accessor.-tool ::- ToolChain -- ^ Toolchain- -> (ToolChain :-> String) -- ^ Toolchain accessor- -> FilePath -- ^ Full command path-tool toolChain getter = toolFromString toolChain (get getter toolChain)---- | Apply the current environment and return a modified toolchain.------ This function is experimental and subject to change!------ Currently recognised environment variables are------ [@CC@] Path to @C@ compiler.------ [@SHAKE_TOOLCHAIN_VARIANT@] One of the values of 'ToolChainVariant' (case insensitive). If this variable is not present, an attempt is made to determine the toolchain variant from the @C@ compiler command.-applyEnv :: ToolChain -> Action ToolChain-applyEnv toolChain = do- cc <- getEnv "CC"- vendor <- getEnv "SHAKE_TOOLCHAIN_VARIANT"- return $ maybe id (set compilerCommand) cc- . maybe id (set variant) ((vendor >>= parseVendor) <|> (cc >>= vendorFromCommand))- $ toolChain- where- parseVendor s =- case map toLower s of- "gcc" -> Just GCC- "llvm" -> Just LLVM- "clang" -> Just LLVM- _ -> Nothing- vendorFromCommand path =- let x = takeFileName path- in if "gcc" `isInfixOf` x || "g++" `isInfixOf` x- then Just GCC- else if "clang" `isInfixOf` x- then Just LLVM- else Just Generic---- | Export a 'ToolChain' definition to a list of environment variable mappings, suitable e.g. for calling third-party configure scripts in cross-compilation mode.------ Needs some fleshing out; currently only works for "standard" binutil toolchains.-toEnv :: ToolChain -> Action [(String,String)]-toEnv tc = do- flags <- (\f -> f mempty) <$> get defaultBuildFlags tc- let cflags = concatMapFlag "-I" (map escapeSpaces (get systemIncludes flags))- ++ concatMapFlag "-I" (map escapeSpaces (get userIncludes flags))- ++ defineFlags flags- ++ get preprocessorFlags flags- ++ compilerFlagsFor (Just C) flags- cxxflags = cflags ++ compilerFlagsFor (Just Cpp) flags- ldflags = get linkerFlags flags- ++ concatMapFlag "-L" (get libraryPath flags)- ++ concatMapFlag "-l" (get libraries flags)- c2cxx path = let x = takeFileName path- in if "gcc" `isSuffixOf` x- then (++"g++") . reverse . drop 3 . reverse $ path- else if "clang" `isSuffixOf` x- then path ++ "++"- else path- let cc = tool tc compilerCommand- cpp = toolFromString tc "cpp"- cppExists <- doesFileExist cpp- let cpp' = if cppExists then cpp else cc ++ " -E"- return $ [- ("CPP", cpp')- , ("AR", tool tc archiverCommand)- , ("NM", toolFromString tc "nm")- , ("CC", tool tc compilerCommand)- -- FIXME: Configure toolchain with compiler command per language?- , ("CXX", c2cxx $ tool tc compilerCommand)- , ("LD", toolFromString tc "ld")- , ("RANLIB", toolFromString tc "ranlib")- , ("CFLAGS", unwords cflags)- , ("CPPFLAGS", unwords cflags)- , ("CXXFLAGS", unwords cxxflags)- , ("LDFLAGS", unwords ldflags)- ]
− Development/Shake/Language/C/Util.hs
@@ -1,51 +0,0 @@--- Copyright 2012-2014 Samplecount S.L.------ Licensed under the Apache License, Version 2.0 (the "License");--- you may not use this file except in compliance with the License.--- You may obtain a copy of the License at------ http://www.apache.org/licenses/LICENSE-2.0------ Unless required by applicable law or agreed to in writing, software--- distributed under the License is distributed on an "AS IS" BASIS,--- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.--- See the License for the specific language governing permissions and--- limitations under the License.--module Development.Shake.Language.C.Util (- mapFlag- , concatMapFlag- , escapeSpaces- , words'-) where--import Data.List--mapFlag :: String -> [String] -> [String]-mapFlag f = concatMap (\x -> [f, x])--concatMapFlag :: String -> [String] -> [String]-concatMapFlag f = map (f++)---- | Escape spaces with '\\' character.-escapeSpaces :: String -> String-escapeSpaces [] = []-escapeSpaces (' ':xs) = '\\' : ' ' : escapeSpaces xs-escapeSpaces ('\\':xs) = '\\' : '\\' : escapeSpaces xs-escapeSpaces (x:xs) = x : escapeSpaces xs---- | Split a list of space separated strings.------ Spaces can be escaped by '\\'.-words' :: String -> [String]-words' = unescape . words- where- escape = "\\"- escapeLength = length escape- isEscaped = isSuffixOf escape- dropEscape = (++" ") . reverse . drop escapeLength . reverse- unescape [] = []- unescape [x] = [if isEscaped x then dropEscape x else x]- unescape (x1:x2:xs)- | isEscaped x1 = unescape ((dropEscape x1 ++ x2):xs)- | otherwise = [x1] ++ unescape (x2:xs)
README.md view
@@ -1,10 +1,12 @@ # shake-language-c +[](https://travis-ci.org/samplecount/shake-language-c)+ **shake-language-c** is a cross-platform build system based on the [Shake](https://github.com/ndmitchell/shake) Haskell library. The focus is on cross-compilation of *C*, *C++* and *Objective C* source code to various target platforms. Currently supported target platforms are *iOS*, *Android NDK*, *Google Portable Native Client*, *MacOS X*, *Linux* and *Windows* (*MinGW*). Supported host platforms are *MacOS X*, *Linux* and *Windows*. ## Documentation -Please see the [manual](/docs/Manual.md) for beginnings of developer documentation.+Please see the [package documentation](http://hackage.haskell.org/package/shake-language-c). Feel free to open an [issue](https://github.com/samplecount/shake-language-c/issues) or send a pull request if there's anything missing that you want to see covered. ## Examples
shake-language-c.cabal view
@@ -13,7 +13,7 @@ -- limitations under the License. Name: shake-language-c-Version: 0.5.0+Version: 0.6.2 Synopsis: Utilities for cross-compiling with Shake Description: This library provides <http://hackage.haskell.org/package/shake Shake> utilities for cross-compiling @C@, @C++@ and @ObjC@ code for various target platforms. Currently supported target platforms are Android, iOS, Linux, MacOS X, Windows\/MinGW and Google Portable Native Client (PNaCl). Supported host platforms are MacOS X, Linux and Windows. Category: Development@@ -25,15 +25,15 @@ Bug-Reports: https://github.com/samplecount/shake-language-c/issues Maintainer: stefan@samplecount.com -Cabal-Version: >= 1.6+Cabal-Version: >= 1.8 Build-Type: Simple Extra-Source-Files:- Changelog.md+ CHANGELOG.md README.md Library- Hs-Source-Dirs: .+ Hs-Source-Dirs: src Exposed-Modules: Development.Shake.Language.C Development.Shake.Language.C.BuildFlags@@ -41,25 +41,43 @@ Development.Shake.Language.C.Host Development.Shake.Language.C.Label Development.Shake.Language.C.PkgConfig- Development.Shake.Language.C.Rules+ Development.Shake.Language.C.ToolChain Development.Shake.Language.C.Target.Android Development.Shake.Language.C.Target.Linux Development.Shake.Language.C.Target.NaCl Development.Shake.Language.C.Target.OSX Development.Shake.Language.C.Target.Windows Other-Modules:+ Development.Shake.Language.C.Host.Linux+ Development.Shake.Language.C.Host.OSX+ Development.Shake.Language.C.Host.Windows Development.Shake.Language.C.Language+ Development.Shake.Language.C.Rules Development.Shake.Language.C.Target- Development.Shake.Language.C.ToolChain Development.Shake.Language.C.Util Ghc-Options: -Wall Build-Depends: base >= 4 && < 5+ , data-default-class , fclabels >= 2 , process , shake >= 0.10 , split , unordered-containers++test-suite doctests+ type: exitcode-stdio-1.0+ hs-source-dirs: tests+ main-is: doctests.hs+ ghc-options: -threaded+ build-depends: base, doctest >= 0.8, shake >= 0.10, shake-language-c++test-suite spectests+ type: exitcode-stdio-1.0+ hs-source-dirs: tests+ main-is: spectests.hs+ ghc-options: -threaded+ build-depends: base, directory, hspec, shake >= 0.10, shake-language-c Source-Repository head Type: git
+ src/Development/Shake/Language/C.hs view
@@ -0,0 +1,49 @@+-- Copyright 2012-2014 Samplecount S.L.+--+-- Licensed under the Apache License, Version 2.0 (the "License");+-- you may not use this file except in compliance with the License.+-- You may obtain a copy of the License at+--+-- http://www.apache.org/licenses/LICENSE-2.0+--+-- Unless required by applicable law or agreed to in writing, software+-- distributed under the License is distributed on an "AS IS" BASIS,+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+-- See the License for the specific language governing permissions and+-- limitations under the License.++{-|+Description: Build @C@ language projects for various target platforms+-}++module Development.Shake.Language.C (+ -- * Build targets+ -- $targets+ module Development.Shake.Language.C.Target+ -- * High-level build rules+ , Linkage(..)+ , module Development.Shake.Language.C.BuildFlags+ , module Development.Shake.Language.C.Rules+ -- * Toolchain types and utilities+ , ToolChain+ , ToolChainVariant(..)+ , applyEnv+ , toEnv+) where++import Development.Shake.Language.C.BuildFlags+import Development.Shake.Language.C.Rules+import Development.Shake.Language.C.Target+import Development.Shake.Language.C.ToolChain++{- $targets++This library's focus is on cross compilation. Here's a list of modules that+provide support for targeting specific platforms:++ * "Development.Shake.Language.C.Target.Android"+ * "Development.Shake.Language.C.Target.Linux"+ * "Development.Shake.Language.C.Target.NaCl"+ * "Development.Shake.Language.C.Target.OSX"+ * "Development.Shake.Language.C.Target.Windows"+-}
+ src/Development/Shake/Language/C/BuildFlags.hs view
@@ -0,0 +1,218 @@+-- Copyright 2012-2014 Samplecount S.L.+--+-- Licensed under the Apache License, Version 2.0 (the "License");+-- you may not use this file except in compliance with the License.+-- You may obtain a copy of the License at+--+-- http://www.apache.org/licenses/LICENSE-2.0+--+-- Unless required by applicable law or agreed to in writing, software+-- distributed under the License is distributed on an "AS IS" BASIS,+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+-- See the License for the specific language governing permissions and+-- limitations under the License.++{-# LANGUAGE TemplateHaskell #-}++{-|+Description: Build flags record for building @C@ language projects++The `BuildFlags` record is an abstraction for various toolchain flags for+building executables and libraries from source files in a @C@-based language.+It's intended to be toolchain-independent, but currently there's a+bias towards binutils\/gcc/clang toolchains.+-}++module Development.Shake.Language.C.BuildFlags (+ -- * Source Language+ Language(..)+ -- * Build flags+ , BuildFlags+ -- Poor man's documentation for TH generated functions.+ , systemIncludes -- | System include directories, referenced by @#include \<...\>@ in code and usually passed to the compiler with the @-I@ flag.+ , userIncludes -- | User include directories, referenced by @#include "..."@ in code and usually passed to the compiler with the @-iquote@ flag.+ , defines -- | Preprocessor defines, a list of pairs of names with or without a value.+ , preprocessorFlags -- | Other preprocessor flags.+ , compilerFlags -- | Compiler flags, either generic ones or for a specific source 'Language'.+ , libraryPath -- | Linker search path for libraries.+ , libraries -- | List of libraries to link against. Note that you should use the library name without the @lib@ prefix and without extension.+ , linkerFlags -- | Flags passed to the linker.+ , localLibraries -- | Locally built static libraries to be linked against. See also the corresponding section in the <https://github.com/samplecount/shake-language-c/blob/master/docs/Manual.md#locally-built-libraries manual>.+ , archiverFlags -- | Flags passed to the object archiver.+ -- ** Utilities for toolchain writers+ , defineFlags+ , compilerFlagsFor+ -- ** Working with config files+ , fromConfig+ -- * Utilities+ , (>>>=)+ , append+ , prepend+) where++import Control.Category ((>>>))+import Control.Monad+import Data.Char (isSpace)+import Data.Default.Class (Default(..))+import Data.Monoid+import Data.List+import Data.List.Split+import Data.Maybe+import Development.Shake.Language.C.Language (Language(..))+import Development.Shake.Language.C.Label+import Development.Shake.Language.C.Util++{-| Record type for abstracting various toolchain command line flags.++`BuildFlags` is an instance of `Default`, you can create a default record with+`def`. `BuildFlags` is also an instance `Monoid`, you can create an empty record with+`mempty` and append flags with `mappend`. `def` and `mempty` are synonyms:++>>> (def :: BuildFlags) == (mempty :: BuildFlags)+True++Record accessors are `Data.Label.Mono.Lens`es from the+<https://hackage.haskell.org/package/fclabels fclabels> package, which+makes accessing and modifying record fields a bit more convenient.+@fclabels@ was chosen over <https://hackage.haskell.org/package/lens lens>+because it has far fewer dependencies, which is convenient when installing+the Shake build system in a per-project cabal sandbox. We might switch to+@lens@ when it gets included in the Haskell platform.++There are two convenience functions for working with `BuildFlags` record fields+containing lists of flags, `append` and `prepend`. Since most combinators in+this library expect a function @BuildFlags -> BuildFlags@, the following is a+common idiom:++@+buildFlags . append `systemIncludes` ["path"]+@++Note that when modifying the same record field, order of function composition+matters and you might want to use the arrow combinator '>>>' for appending in+source statement order:++>>> :{+ get systemIncludes+ $ append systemIncludes ["path1"] . append systemIncludes ["path2"]+ $ mempty+:}+["path2","path1"]++>>> :{+ get systemIncludes+ $ append systemIncludes ["path1"] >>> append systemIncludes ["path2"]+ $ mempty+:}+["path1","path2"]++See "Development.Shake.Language.C.Rules" for how to use 'BuildFlags' in build+product rules.+-}+data BuildFlags = BuildFlags {+ _systemIncludes :: [FilePath]+ , _userIncludes :: [FilePath]+ , _defines :: [(String, Maybe String)]+ , _preprocessorFlags :: [String]+ , _compilerFlags :: [(Maybe Language, [String])]+ , _libraryPath :: [FilePath]+ , _libraries :: [String]+ , _linkerFlags :: [String]+ -- This is needed for linking against local libraries built by shake (the linker `needs' its inputs).+ , _localLibraries :: [FilePath]+ , _archiverFlags :: [String]+ } deriving (Eq, Show)++mkLabel ''BuildFlags++defaultBuildFlags :: BuildFlags+defaultBuildFlags =+ BuildFlags {+ _systemIncludes = []+ , _userIncludes = []+ , _defines = []+ , _preprocessorFlags = []+ , _compilerFlags = []+ , _libraryPath = []+ , _libraries = []+ , _linkerFlags = []+ , _localLibraries = []+ , _archiverFlags = []+ }++instance Default BuildFlags where+ def = defaultBuildFlags++instance Monoid BuildFlags where+ mempty = defaultBuildFlags+ a `mappend` b =+ append systemIncludes (get systemIncludes a)+ . append userIncludes (get userIncludes a)+ . append defines (get defines a)+ . append preprocessorFlags (get preprocessorFlags a)+ . append compilerFlags (get compilerFlags a)+ . append libraryPath (get libraryPath a)+ . append libraries (get libraries a)+ . append linkerFlags (get linkerFlags a)+ . append localLibraries (get localLibraries a)+ . append archiverFlags (get archiverFlags a)+ $ b++-- | Construct preprocessor flags from the 'defines' field of 'BuildFlags'.+defineFlags :: BuildFlags -> [String]+defineFlags = concatMapFlag "-D"+ . map (\(a, b) -> maybe a (\b' -> a++"="++b') b)+ . get defines++-- | Return a list of compiler flags for a specific source language.+compilerFlagsFor :: Maybe Language -> BuildFlags -> [String]+compilerFlagsFor lang = concat+ . maybe (map snd . filter (isNothing.fst))+ (mapMaybe . f) lang+ . get compilerFlags+ where f _ (Nothing, x) = Just x+ f l (Just l', x) | l == l' = Just x+ | otherwise = Nothing++-- | Construct a 'BuildFlags' modifier function from a config file.+--+-- See also "Development.Shake.Language.C.Config".+fromConfig :: (Functor m, Monad m) => (String -> m (Maybe String)) -> m (BuildFlags -> BuildFlags)+fromConfig getConfig = do+ let parseConfig parser = fmap (maybe [] parser) . getConfig . ("BuildFlags."++)++ config_systemIncludes <- parseConfig paths "systemIncludes"+ config_userIncludes <- parseConfig paths "userIncludes"+ config_defines <- parseConfig defines' "defines"+ config_preprocessorFlags <- parseConfig flags "preprocessorFlags"+ config_compilerFlags <- parseConfig ((:[]) . ((,)Nothing) . flags) "compilerFlags"+ config_compilerFlags_c <- parseConfig ((:[]) . ((,)(Just C)) . flags) "compilerFlags.c"+ config_compilerFlags_cxx <- parseConfig ((:[]) . ((,)(Just Cpp)) . flags) "compilerFlags.cxx"+ config_libraryPath <- parseConfig paths "libraryPath"+ config_libraries <- parseConfig flags "libraries"+ config_linkerFlags <- parseConfig flags "linkerFlags"+ config_localLibraries <- parseConfig paths "localLibraries"+ config_archiverFlags <- parseConfig flags "archiverFlags"++ return $ append systemIncludes config_systemIncludes+ . append userIncludes config_userIncludes+ . append defines config_defines+ . append preprocessorFlags config_preprocessorFlags+ . append compilerFlags (config_compilerFlags ++ config_compilerFlags_c ++ config_compilerFlags_cxx)+ . append libraryPath config_libraryPath+ . append libraries config_libraries+ . append linkerFlags config_linkerFlags+ . append localLibraries config_localLibraries+ . append archiverFlags config_archiverFlags+ where+ flags = words' . dropWhile isSpace+ paths = words' . dropWhile isSpace+ define [] = error "Empty preprocessor definition"+ define [k] = (k, Nothing)+ define [k,v] = (k, Just v)+ define (k:vs) = (k, Just (intercalate "=" vs))+ defines' = map (define . splitOn "=") . flags++-- | Utility function for composing functions in a monad.+(>>>=) :: Monad m => m (a -> b) -> m (b -> c) -> m (a -> c)+(>>>=) = liftM2 (>>>)
+ src/Development/Shake/Language/C/Config.hs view
@@ -0,0 +1,65 @@+{-# LANGUAGE DeriveDataTypeable, GeneralizedNewtypeDeriving #-}++{-|+Description: Read values from configuration files++This module provides utilities for reading values from configuration files,+similar to the functions provided by "Development.Shake.Config".+-}+module Development.Shake.Language.C.Config(+ withConfig+ , parsePaths+ , getPaths+) where++import Control.Applicative+import qualified Data.HashMap.Strict as Map+import Development.Shake+import Development.Shake.Classes+import Development.Shake.Config (readConfigFileWithEnv)+import Development.Shake.Language.C.Util (words')++newtype Config = Config ([(String, String)], FilePath, String) deriving (Show,Typeable,Eq,Hashable,Binary,NFData)++{- | Given a list of dependencies, return a function that takes an environment+of variable bindings, a configuration file path and a configuration variable+and returns the corresponding configuration value.++This function is more flexible than `Development.Shake.Config.usingConfigFile`.+It allows the use of multiple configuration files as well as specifying default+variable bindings.++Typical usage would be something like this:++> -- In the Rules monad+> getConfig <- withConfig []+> -- Then in an Action+> ... value <- getConfig [("variable", "default value")] "config.cfg" "variable"+-}+withConfig :: [FilePath]+ -> Rules ( [(String,String)]+ -> FilePath+ -> String+ -> Action (Maybe String))+withConfig deps = do+ fileCache <- newCache $ \(env, file) -> do+ need deps+ liftIO $ readConfigFileWithEnv env file+ query <- addOracle $ \(Config (env, file, key)) -> Map.lookup key <$> fileCache (env, file)+ return $ \env file key -> query (Config (env, file, key))++-- | Parse a list of space separated paths from an input string. Spaces can be escaped by @\\@ characters.+--+-- >>> parsePaths "/a /a/b /a/b/c\\ d"+-- ["/a","/a/b","/a/b/c d"]+parsePaths :: String -> [FilePath]+parsePaths = words'++-- | Given a function that maps a configuration variable to a value and a list of variable names, return a corresponding list of file paths. Missing variables are ignored.+getPaths ::+ (String -> Action (Maybe String)) -- ^ Configuration lookup function+ -> [String] -- ^ Configuration keys+ -> Action [FilePath] -- ^ File paths+getPaths getConfig keys = do+ sources <- mapM (fmap (fmap parsePaths) . getConfig) keys+ return $ concatMap (maybe [] id) sources
+ src/Development/Shake/Language/C/Host.hs view
@@ -0,0 +1,86 @@+-- Copyright 2012-2013 Samplecount S.L.+--+-- Licensed under the Apache License, Version 2.0 (the "License");+-- you may not use this file except in compliance with the License.+-- You may obtain a copy of the License at+--+-- http://www.apache.org/licenses/LICENSE-2.0+--+-- Unless required by applicable law or agreed to in writing, software+-- distributed under the License is distributed on an "AS IS" BASIS,+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+-- See the License for the specific language governing permissions and+-- limitations under the License.++{-|+Description: Toolchain definitions and utilities for host platform+-}+module Development.Shake.Language.C.Host (+ OS(..)+ , os+ , executableExtension+ , sharedLibraryExtension+ , loadableLibraryExtension+ , defaultToolChain+) where++import Development.Shake (Action)+import Development.Shake.Language.C.Target (Target)+import qualified Development.Shake.Language.C.Host.Linux as Linux+import qualified Development.Shake.Language.C.Host.OSX as OSX+import qualified Development.Shake.Language.C.Host.Windows as Windows+import Development.Shake.Language.C.ToolChain (ToolChain)+import qualified System.Info as System+import System.IO.Unsafe (unsafePerformIO)++-- | Host operating system.+data OS =+ Linux+ | OSX+ | Windows+ deriving (Eq, Ord, Show)++-- | This host's operating system.+os :: OS+os =+ case System.os of+ "darwin" -> OSX+ "mingw32" -> Windows+ "linux" -> Linux+ _ -> error $ "Unknown host operating system: " ++ System.os++-- | File extension for executables.+executableExtension :: String+executableExtension =+ case os of+ Windows -> "exe"+ _ -> ""++-- | File extension for dynamic shared libraries.+sharedLibraryExtension :: String+sharedLibraryExtension =+ case os of+ Linux -> "so"+ OSX -> "dylib"+ Windows -> "dll"++-- | File extension for dynamic loadable libraries.+loadableLibraryExtension :: String+loadableLibraryExtension =+ case os of+ Linux -> "so"+ OSX -> "bundle"+ Windows -> "dll"++-- | Get host's default toolchain.+--+-- This function can be used for targeting the host operating system, see also "Development.Shake.Language.C.Rules".+defaultToolChain :: (Target, Action ToolChain)+{-# NOINLINE defaultToolChain #-}+defaultToolChain = unsafePerformIO $ do+ -- The assumption here is that target and toolchain don't change while the program is running.+ case os of+ Linux -> Linux.getHostToolChain+ OSX -> OSX.getHostToolChain+ Windows -> Windows.getHostToolChain+ -- _ -> error $ "No default toolchain for this operating system (" ++ show os ++ ")"
+ src/Development/Shake/Language/C/Host/Linux.hs view
@@ -0,0 +1,39 @@+-- Copyright 2012-2014 Samplecount S.L.+--+-- Licensed under the Apache License, Version 2.0 (the "License");+-- you may not use this file except in compliance with the License.+-- You may obtain a copy of the License at+--+-- http://www.apache.org/licenses/LICENSE-2.0+--+-- Unless required by applicable law or agreed to in writing, software+-- distributed under the License is distributed on an "AS IS" BASIS,+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+-- See the License for the specific language governing permissions and+-- limitations under the License.++module Development.Shake.Language.C.Host.Linux (+ getHostToolChain+) where++import Development.Shake+import Development.Shake.Language.C.Target+import Development.Shake.Language.C.Target.Linux+import Development.Shake.Language.C.ToolChain+import System.Process (readProcess)++-- | Get host architecture.+getHostArch :: IO Arch+getHostArch = do+ arch <- fmap (head.lines) $ readProcess "arch" [] ""+ return $ case arch of+ "i386" -> X86 I386+ "i686" -> X86 I686+ "x86_64" -> X86 X86_64+ _ -> error $ "Unknown host architecture " ++ arch++-- | Get host toolchain.+getHostToolChain :: IO (Target, Action ToolChain)+getHostToolChain = do+ t <- fmap target getHostArch+ return (t, return $ toolChain GCC)
+ src/Development/Shake/Language/C/Host/OSX.hs view
@@ -0,0 +1,33 @@+-- Copyright 2012-2014 Samplecount S.L.+--+-- Licensed under the Apache License, Version 2.0 (the "License");+-- you may not use this file except in compliance with the License.+-- You may obtain a copy of the License at+--+-- http://www.apache.org/licenses/LICENSE-2.0+--+-- Unless required by applicable law or agreed to in writing, software+-- distributed under the License is distributed on an "AS IS" BASIS,+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+-- See the License for the specific language governing permissions and+-- limitations under the License.++module Development.Shake.Language.C.Host.OSX (+ getHostToolChain+) where++import Control.Applicative hiding ((*>))+import Development.Shake+import Development.Shake.Language.C.Target+import Development.Shake.Language.C.Target.OSX+import Development.Shake.Language.C.ToolChain++-- | Get host toolchain.+getHostToolChain :: IO (Target, Action ToolChain)+getHostToolChain = do+ let defaultTarget = target macOSX (X86 X86_64)+ return ( defaultTarget+ , toolChain+ <$> getSDKRoot+ <*> (maximum <$> getPlatformVersions (targetPlatform defaultTarget))+ <*> pure defaultTarget )
+ src/Development/Shake/Language/C/Host/Windows.hs view
@@ -0,0 +1,38 @@+-- Copyright 2012-2014 Samplecount S.L.+--+-- Licensed under the Apache License, Version 2.0 (the "License");+-- you may not use this file except in compliance with the License.+-- You may obtain a copy of the License at+--+-- http://www.apache.org/licenses/LICENSE-2.0+--+-- Unless required by applicable law or agreed to in writing, software+-- distributed under the License is distributed on an "AS IS" BASIS,+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+-- See the License for the specific language governing permissions and+-- limitations under the License.++module Development.Shake.Language.C.Host.Windows (+ getHostToolChain+) where++import Development.Shake+import Development.Shake.Language.C.Target+import Development.Shake.Language.C.Target.Linux+import Development.Shake.Language.C.ToolChain+import qualified System.Info as System++getHostArch :: IO Arch+getHostArch = do+ -- TODO: Get the info from the environment+ let arch = System.arch+ return $ case arch of+ "i386" -> X86 I386+ "i686" -> X86 I686+ "x86_64" -> X86 X86_64+ _ -> error $ "Unknown host architecture " ++ arch++getHostToolChain :: IO (Target, Action ToolChain)+getHostToolChain = do+ t <- fmap target getHostArch+ return (t, return $ toolChain Generic)
+ src/Development/Shake/Language/C/Label.hs view
@@ -0,0 +1,46 @@+-- Copyright 2012-2013 Samplecount S.L.+--+-- Licensed under the Apache License, Version 2.0 (the "License");+-- you may not use this file except in compliance with the License.+-- You may obtain a copy of the License at+--+-- http://www.apache.org/licenses/LICENSE-2.0+--+-- Unless required by applicable law or agreed to in writing, software+-- distributed under the License is distributed on an "AS IS" BASIS,+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+-- See the License for the specific language governing permissions and+-- limitations under the License.++{-# LANGUAGE TypeOperators #-}++{-|+Description: Lens utilities++This module provides some lens utilities and also re-exports "Data.Label",+which can avoid a package dependency on @fclabels@ in some cases.+-}+module Development.Shake.Language.C.Label (+ module Data.Label+ , append+ , prepend+) where++import Data.Label+import Data.Monoid (Monoid, mappend)++-- | Append stuff to the 'Monoid' in record field specified by lens.+append :: Monoid a =>+ (f :-> a) -- ^ lens+ -> a -- ^ stuff to append+ -> f -- ^ original record+ -> f -- ^ modified record+append l n = modify l (`mappend` n)++-- | Prepend stuff to the 'Monoid' in record field specified by lens.+prepend :: Monoid a =>+ (f :-> a) -- ^ lens+ -> a -- ^ stuff to prepend+ -> f -- ^ original record+ -> f -- ^ modified record+prepend l n = modify l (n `mappend`)
+ src/Development/Shake/Language/C/Language.hs view
@@ -0,0 +1,47 @@+-- Copyright 2012-2014 Samplecount S.L.+--+-- Licensed under the Apache License, Version 2.0 (the "License");+-- you may not use this file except in compliance with the License.+-- You may obtain a copy of the License at+--+-- http://www.apache.org/licenses/LICENSE-2.0+--+-- Unless required by applicable law or agreed to in writing, software+-- distributed under the License is distributed on an "AS IS" BASIS,+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+-- See the License for the specific language governing permissions and+-- limitations under the License.++{-# LANGUAGE TemplateHaskell #-}++module Development.Shake.Language.C.Language (+ Language(..)+ , defaultLanguageMap+ , languageOf+) where++import Development.Shake.FilePath (takeExtension)++-- | Source language.+--+-- Currently something derived from @C@.+data Language =+ C -- ^ Plain old C+ | Cpp -- ^ C+++ | ObjC -- ^ Objective-C+ | ObjCpp -- ^ Objective-C with C++ (Apple extension)+ deriving (Enum, Eq, Show)++-- | Default mapping from file extension to source language.+defaultLanguageMap :: [(String, Language)]+defaultLanguageMap = concatMap f [+ (C, [".c"])+ , (Cpp, [".cc", ".CC", ".cpp", ".CPP", ".C", ".cxx", ".CXX"])+ , (ObjC, [".m"])+ , (ObjCpp, [".mm", ".M"])+ ]+ where f (lang, exts) = map (\ext -> (ext, lang)) exts++-- | Determine the source language of a file based on its extension.+languageOf :: FilePath -> Maybe Language+languageOf = flip lookup defaultLanguageMap . takeExtension
+ src/Development/Shake/Language/C/PkgConfig.hs view
@@ -0,0 +1,125 @@+-- Copyright 2012-2013 Samplecount S.L.+--+-- Licensed under the Apache License, Version 2.0 (the "License");+-- you may not use this file except in compliance with the License.+-- You may obtain a copy of the License at+--+-- http://www.apache.org/licenses/LICENSE-2.0+--+-- Unless required by applicable law or agreed to in writing, software+-- distributed under the License is distributed on an "AS IS" BASIS,+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+-- See the License for the specific language governing permissions and+-- limitations under the License.++{-|+Description: Query build flags with @pkg-config@++This module provides utilities for querying 'BuildFlags' from the+<http://www.freedesktop.org/wiki/Software/pkg-config/ pkg-config> database,+which is available on many Unix like operating systems.+-}+module Development.Shake.Language.C.PkgConfig (+ Options(..)+ , defaultOptions+ , pkgConfig+ , fromConfig+) where++import Control.Applicative+import Data.Char (toLower)+import Data.Default.Class (Default(..))+import Data.List (intercalate, isPrefixOf)+import Development.Shake+import Development.Shake.FilePath+import Development.Shake.Language.C ( BuildFlags+ , compilerFlags+ , libraries+ , libraryPath+ , linkerFlags+ , systemIncludes+ , userIncludes )+import Development.Shake.Language.C.Label (append)+import Development.Shake.Language.C.Util (words')++-- ====================================================================+-- PkgConfig++-- TODO:+-- * Use parsec or attoparsec for more robust parser+-- * Parse preprocessor defines+-- * Parse framework path (-F) and -framework flags++parseCflags :: [String] -> (BuildFlags -> BuildFlags)+parseCflags [] = id+parseCflags (x:xs)+ | isPrefixOf "-I" x = parseCflags xs . append systemIncludes [drop 2 x]+ | isPrefixOf "-i" x = parseCflags xs . append userIncludes [drop 2 x]+ | otherwise = append compilerFlags [(Nothing, [x])]++parseLibs :: [String] -> (BuildFlags -> BuildFlags)+parseLibs [] = id+parseLibs (x:xs)+ | isPrefixOf "-l" x = parseLibs xs . append libraries [drop 2 x]+ | isPrefixOf "-L" x = parseLibs xs . append libraryPath [drop 2 x]+ | otherwise = parseLibs xs . append linkerFlags [x]++parseFlags :: String -> [String]+parseFlags = words' . head . lines++-- | PkgConfig options.+data Options = Options {+ searchPath :: Maybe [FilePath] -- ^ List of directories where @.pc@ files are searched, corresponding to the @PKG_CONFIG_PATH@ environment variable+ , static :: Bool -- ^ Return flags appropriate for static linking+ } deriving (Eq, Show)++-- | Default @pkg-config@ options.+--+-- This function is an alias for `def`.+defaultOptions :: Options+defaultOptions = Options{+ searchPath = Nothing+ , static = False+ }++instance Default Options where+ def = defaultOptions++-- | Call @pkg-config@ with options and a package name and return a 'BuildFlags' modification function.+--+-- The @pkg-config@ executable must be installed on the build host.+pkgConfig :: Options -> String -> Action (BuildFlags -> BuildFlags)+pkgConfig options pkg = do+ env <- case searchPath options of+ Nothing -> return []+ Just path -> do+ env <- addEnv [("PKG_CONFIG_PATH", intercalate [searchPathSeparator] path)]+ return [env]+ let flags = if static options then ["--static"] else []+ pkgconfig which = command ([Traced ""] ++ env) "pkg-config" (flags ++ ["--" ++ which, pkg])+ Stdout cflags <- pkgconfig "cflags"+ Stdout libs <- pkgconfig "libs"+ return ( parseCflags (parseFlags cflags)+ . parseLibs (parseFlags libs) )++-- | Given an initial 'Options' record and a configuration variable lookup function, call @pkg-config@ based on configuration variable settings and return a 'BuildFlags' modification function.+--+-- The following configuration variables are recognised:+--+-- [@PkgConfig.packages@] List of package names for which build flags should be queried+-- [@PkgConfig.options.searchPath@] Space-separated list of file paths, corresponds to the `searchPath` option+-- [@PkgConfig.options.static@] @true@ or @false@, corresponds to the `static` option+fromConfig :: Options -> (String -> Action (Maybe String)) -> Action (BuildFlags -> BuildFlags)+fromConfig initialOptions cfg = do+ config_searchPath <- fmap words' <$> cfg "PkgConfig.options.searchPath"+ config_static <- fmap (bool . words) <$> cfg "PkgConfig.options.static"+ config_packages <- fmap words <$> cfg "PkgConfig.packages"+ let options = initialOptions {+ searchPath = maybe (searchPath initialOptions) Just config_searchPath,+ static = maybe (static initialOptions) id config_static+ }+ flags <- mapM (pkgConfig options) (maybe [] id config_packages)+ return $ foldl (.) id $ flags+ where+ bool (x:_) = map toLower x == "true"+ bool _ = False
+ src/Development/Shake/Language/C/Rules.hs view
@@ -0,0 +1,154 @@+-- Copyright 2012-2014 Samplecount S.L.+--+-- Licensed under the Apache License, Version 2.0 (the "License");+-- you may not use this file except in compliance with the License.+-- You may obtain a copy of the License at+--+-- http://www.apache.org/licenses/LICENSE-2.0+--+-- Unless required by applicable law or agreed to in writing, software+-- distributed under the License is distributed on an "AS IS" BASIS,+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+-- See the License for the specific language governing permissions and+-- limitations under the License.++{-|+Description: High-level Shake rules++This module provides a few high-level rules for building executables and+libraries. Below is an example that builds both a static library and an executable. See+"Development.Shake.Language.C.ToolChain" for examples of toolchain definitions.++> let toolChain = ...+> lib <- staticLibrary toolChain "libexample.a" (pure mempty) (pure ["example_lib.c"])+> exe <- staticLibrary toolChain ("example" <.> exe) (pure mempty) (pure ["example_exe.c"])+> want [lib, exe]++Sometimes you want to structure your project in a set of static libraries that+are later linked into one or more executables. For Shake to recognise the+libraries as dependencies of the executable you need to add them to the+`localLibraries` field of the `BuildFlags` record:++> let toolChain = ...+> buildFlags = ...+> lib <- staticLibrary toolChain "libexample.a"+> (pure buildFlags)+> (pure ["example_lib.c"])+> exe <- executable toolChain ("example" <.> exe)+> (pure $ buildFlags . append localLibraries [lib])+> (pure ["example_exe.c"])+> want [exe]++The rule functions expect their arguments in the 'Action' monad in order to be+able to derive them from side-effecting configuration actions. For example it+can be useful to determine certain toolchain settings either from the+environment, or from configuration files. Using "Control.Applicative" we could+write:++> Android.toolChain+> <$> getEnvWithDefault+> (error "ANDROID_NDK is undefined")+> "ANDROID_NDK"+> <*> pure (Android.sdkVersion 9)+> <*> pure (LLVM, Version [3,4] [])+> <*> pure (Android.target (Arm Armv7))+-}++module Development.Shake.Language.C.Rules (+ executable+ , staticLibrary+ , sharedLibrary+ , loadableLibrary+) where++import Data.Monoid (mempty)+import Development.Shake+import Development.Shake.FilePath+import Development.Shake.Language.C.BuildFlags as BuildFlags+import Development.Shake.Language.C.ToolChain as ToolChain+import Development.Shake.Language.C.Label (get)++mkObjectsDir :: FilePath -> FilePath+mkObjectsDir path = takeDirectory path </> map tr (takeFileName path) ++ "_obj"+ where tr '.' = '_'+ tr x = x++buildProduct :: (ToolChain -> Linker)+ -> Action ToolChain+ -> FilePath+ -> Action (BuildFlags -> BuildFlags)+ -> Action [FilePath]+ -> Rules FilePath+buildProduct getLinker getToolChain result getBuildFlags getSources = do+ let objectsDir = mkObjectsDir result+ cachedObjects <- newCache $ \() -> do+ sources <- getSources+ return $ [ objectsDir </> makeRelative "/" (src <.> if isAbsolute src then "abs.o" else "rel.o")+ | src <- sources ]+ cachedToolChain <- newCache $ \() -> getToolChain+ cachedBuildFlags <- newCache $ \() -> do+ tc <- cachedToolChain ()+ f1 <- get ToolChain.defaultBuildFlags tc+ f2 <- getBuildFlags+ return $ f2 . f1 $ mempty+ result *> \_ -> do+ tc <- cachedToolChain ()+ flags <- cachedBuildFlags ()+ objs <- cachedObjects ()+ need objs+ (getLinker tc)+ tc+ flags+ objs+ result+ (dropTrailingPathSeparator objectsDir ++ "//*.o") *> \obj -> do+ tc <- cachedToolChain ()+ flags <- cachedBuildFlags ()+ -- Compute source file name from object file name+ -- Using getSources here would result in a dependency of every object file on the list of sources, leading to unnecessary rebuilds.+ let src = case splitExtension $ dropExtension $ makeRelative objectsDir obj of+ (x, ".abs") -> "/" </> x -- Source file had absolute path+ (x, ".rel") -> x+ (_, ext) -> error $ "BUG: Unexpected object file extension " ++ ext+ (get compiler tc)+ tc+ flags+ src+ obj+ return result++-- TODO: The following result type would be more composable, e.g. allowing for further build product processing:+-- Rules (FilePath -> Action ())+-- See https://github.com/samplecount/shake-language-c/issues/15++-- | Shake rule for building an executable.+executable :: Action ToolChain -- ^ Action returning a target 'ToolChain'+ -> FilePath -- ^ Output file+ -> Action (BuildFlags -> BuildFlags) -- ^ Action returning a 'BuildFlags' modifier+ -> Action [FilePath] -- ^ Action returning a list of input source files+ -> Rules FilePath -- ^ Rule returning the output file path+executable toolChain = buildProduct (flip (get linker) Executable) toolChain++-- | Shake rule for building a static library.+staticLibrary :: Action ToolChain -- ^ Action returning a target 'ToolChain'+ -> FilePath -- ^ Output file+ -> Action (BuildFlags -> BuildFlags) -- ^ Action returning a 'BuildFlags' modifier+ -> Action [FilePath] -- ^ Action returning a list of input source files+ -> Rules FilePath -- ^ Rule returning the output file path+staticLibrary toolChain = buildProduct (get archiver) toolChain++-- | Shake rule for building a shared (dynamically linked) library.+sharedLibrary :: Action ToolChain -- ^ Action returning a target 'ToolChain'+ -> FilePath -- ^ Output file+ -> Action (BuildFlags -> BuildFlags) -- ^ Action returning a 'BuildFlags' modifier+ -> Action [FilePath] -- ^ Action returning a list of input source files+ -> Rules FilePath -- ^ Rule returning the output file path+sharedLibrary toolChain = buildProduct (flip (get linker) SharedLibrary) toolChain++-- | Shake rule for building a dynamically loadable library.+loadableLibrary :: Action ToolChain -- ^ Action returning a target 'ToolChain'+ -> FilePath -- ^ Output file+ -> Action (BuildFlags -> BuildFlags) -- ^ Action returning a 'BuildFlags' modifier+ -> Action [FilePath] -- ^ Action returning a list of input source files+ -> Rules FilePath -- ^ Rule returning the output file path+loadableLibrary toolChain = buildProduct (flip (get linker) LoadableLibrary) toolChain
+ src/Development/Shake/Language/C/Target.hs view
@@ -0,0 +1,114 @@+-- Copyright 2012-2014 Samplecount S.L.+--+-- Licensed under the Apache License, Version 2.0 (the "License");+-- you may not use this file except in compliance with the License.+-- You may obtain a copy of the License at+--+-- http://www.apache.org/licenses/LICENSE-2.0+--+-- Unless required by applicable law or agreed to in writing, software+-- distributed under the License is distributed on an "AS IS" BASIS,+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+-- See the License for the specific language governing permissions and+-- limitations under the License.++module Development.Shake.Language.C.Target (+ OS(..)+ , Platform(..)+ , ArmVersion(..)+ , X86Version(..)+ , Arch(..)+ , Target(..)+ , ToBuildPrefix(..)+) where++import Data.Char (toLower)+import Development.Shake.FilePath++-- $setup+-- >>> :load Development.Shake.Language.C.Target.OSX+-- >>> :module -Development.Shake.Language.C.Target.OSX+-- >>> import Development.Shake.Language.C.Target++-- | Target operating system.+data OS =+ Android -- ^ Google Android+ | Linux -- ^ GNU Linux+ | OSX -- ^ Apple Mac OSX and iOS+ | Pepper -- ^ Google Portable Native Client (PNaCl)+ | Windows -- ^ Microsoft Windows+ deriving (Eq, Ord, Show)++-- | Target platform.+--+-- Basically just a platform identifier string. Use `toBuildPrefix` to convert a platform to a file path prefix that can be used in Shake rules.+data Platform = Platform {+ platformName :: String+ } deriving (Eq, Show)++-- | `X86` architecture version.+data X86Version =+ I386 -- ^ @i386@, 32-bit architecture without @SSE@+ | I686 -- ^ @i686@, 32-bit architecture with @SSE@ (/Pentium-Pro/)+ | X86_64 -- ^ @x86_64@, 64-bit architecture+ deriving (Eq, Show)++-- | `Arm` architecture version.+data ArmVersion =+ Armv5+ | Armv6+ | Armv7+ | Armv7s+ deriving (Eq, Show)++-- | Target architecture.+--+-- Use `toBuildPrefix` to convert an architecture to a short, more or less canonical file path prefix that can be used in Shake rules.+data Arch =+ X86 X86Version -- ^ Intel @x86@ architecture+ | Arm ArmVersion -- ^ Arm architecture+ | LLVM_IR -- ^ LLVM intermediate representation, used by `Pepper` (PNaCl)+ deriving (Eq, Show)++-- | Compilation target triple consisting of operating system, platform and architecture.+--+-- Use `toBuildPrefix` to convert a target to a file path prefix that can be used in Shake rules. The prefix is of the form+--+-- > <platform>/<architecture>+--+-- For example:+--+-- >>> import qualified Development.Shake.Language.C.Target.OSX as OSX+-- >>> toBuildPrefix $ OSX.target OSX.iPhoneOS (Arm Armv7)+-- "iphoneos/armv7"+data Target = Target {+ targetOS :: OS -- ^ Target operating system+ , targetPlatform :: Platform -- ^ Target platform+ , targetArch :: Arch -- ^ Target architecture+ } deriving (Show)++-- | Convert a value to a build directory prefix.+--+-- The idea is that several such values can be combined to form more complex build directory hierarchies. This can be important for disambiguating build product paths in Shake rules.+class ToBuildPrefix a where+ toBuildPrefix :: a -> FilePath++instance ToBuildPrefix Platform where+ toBuildPrefix = map toLower . platformName++instance ToBuildPrefix Arch where+ toBuildPrefix arch =+ case arch of+ X86 I386 -> "i386"+ X86 I686 -> "i686"+ X86 X86_64 -> "x86_64"+ Arm Armv5 -> "armv5"+ Arm Armv6 -> "armv6"+ Arm Armv7 -> "armv7"+ Arm Armv7s -> "armv7s"+ LLVM_IR -> "llvm_ir"++instance ToBuildPrefix Target where+ toBuildPrefix target =+ toBuildPrefix (targetPlatform target)+ </> toBuildPrefix (targetArch target)
+ src/Development/Shake/Language/C/Target/Android.hs view
@@ -0,0 +1,203 @@+-- Copyright 2012-2013 Samplecount S.L.+--+-- Licensed under the Apache License, Version 2.0 (the "License");+-- you may not use this file except in compliance with the License.+-- You may obtain a copy of the License at+--+-- http://www.apache.org/licenses/LICENSE-2.0+--+-- Unless required by applicable law or agreed to in writing, software+-- distributed under the License is distributed on an "AS IS" BASIS,+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+-- See the License for the specific language governing permissions and+-- limitations under the License.++{-|+Description: Toolchain definitions and utilities for Android++This module provides toolchain definitions and utilities for targeting Android.+See "Development.Shake.Language.C.Rules" for examples of how to use a target+toolchain.+-}+module Development.Shake.Language.C.Target.Android (+ target+ , sdkVersion+ , toolChain+ , abiString+ , gnustl+ , libcxx+ , native_app_glue+) where++import Control.Category ((>>>))+import Development.Shake.FilePath+import Data.Version (Version(..), showVersion)+import Development.Shake.Language.C.BuildFlags+import Development.Shake.Language.C.Target+import Development.Shake.Language.C.Label+import Development.Shake.Language.C.ToolChain+import qualified System.Info as System++unsupportedArch :: Arch -> a+unsupportedArch arch = error $ "Unsupported Android target architecture " ++ show arch++toolChainPrefix :: Target -> String+toolChainPrefix x =+ case targetArch x of+ X86 _ -> "x86-"+ Arm _ -> "arm-linux-androideabi-"+ arch -> unsupportedArch arch++osPrefix :: String+osPrefix = System.os ++ "-" ++ cpu+ where cpu = case System.arch of+ "i386" -> "x86"+ arch -> arch++-- | Android target for architecture.+target :: Arch -> Target+target = Target Android (Platform "android")++mkDefaultBuildFlags :: FilePath -> Version -> Arch -> BuildFlags -> BuildFlags+mkDefaultBuildFlags ndk version arch =+ append compilerFlags [(Nothing, [sysroot, march])]+ >>> append compilerFlags (archCompilerFlags arch)+ >>> append compilerFlags [(Nothing, [+ "-fpic"+ , "-ffunction-sections"+ , "-funwind-tables"+ , "-fstack-protector"+ , "-no-canonical-prefixes"])]+ >>> append linkerFlags [sysroot, march]+ >>> append linkerFlags (archLinkerFlags arch)+ >>> append linkerFlags ["-Wl,--no-undefined", "-Wl,-z,relro", "-Wl,-z,now"]+ >>> append linkerFlags ["-no-canonical-prefixes"]+ >>> append archiverFlags ["crs"]+ where+ sysroot = "--sysroot="+ ++ ndk+ </> "platforms"+ </> "android-" ++ show (head (versionBranch version))+ </> "arch-" ++ case arch of+ (X86 _) -> "x86"+ (Arm _) -> "arm"+ _ -> unsupportedArch arch+ march = "-march=" ++ case arch of+ X86 I386 -> "i386"+ X86 I686 -> "i686"+ X86 X86_64 -> "x86_64"+ Arm Armv5 -> "armv5te"+ Arm Armv6 -> "armv5te"+ Arm Armv7 -> "armv7-a"+ _ -> unsupportedArch arch+ archCompilerFlags (Arm Armv7) = [(Nothing, ["-mfloat-abi=softfp", "-mfpu=neon" {- vfpv3-d16 -}])]+ archCompilerFlags (Arm _) = [(Nothing, ["-mtune=xscale", "-msoft-float"])]+ archCompilerFlags _ = []+ archLinkerFlags (Arm Armv7) = ["-Wl,--fix-cortex-a8"]+ archLinkerFlags _ = []++-- | Construct a version record from an integral Android SDK version.+--+-- prop> sdkVersion 19 == Version [19] []+sdkVersion :: Int -> Version+sdkVersion n = Version [n] []++-- | Construct an Android toolchain.+toolChain :: FilePath -- ^ NDK source directory+ -> Version -- ^ SDK version, see `sdkVersion`+ -> (ToolChainVariant, Version) -- ^ Toolchain variant and version+ -> Target -- ^ Build target, see `target`+ -> ToolChain -- ^ Resulting toolchain+toolChain "" _ (_, _) _ = error "Empty NDK directory"+toolChain ndk version (GCC, tcVersion) t =+ set variant GCC+ $ set toolDirectory (Just (ndk </> "toolchains"+ </> toolChainPrefix t ++ showVersion tcVersion+ </> "prebuilt"+ </> osPrefix+ </> "bin"))+ $ set toolPrefix (toolChainPrefix t)+ $ set compilerCommand "gcc"+ $ set archiverCommand "ar"+ $ set linkerCommand "g++"+ $ set defaultBuildFlags (return $ mkDefaultBuildFlags ndk version (targetArch t))+ $ defaultToolChain+toolChain ndk version (LLVM, tcVersion) t =+ set variant LLVM+ $ set toolDirectory (Just (ndk </> "toolchains"+ </> "llvm-" ++ showVersion tcVersion+ </> "prebuilt"+ </> osPrefix+ </> "bin"))+ $ set compilerCommand "clang"+ $ set archiverCommand "llvm-ar"+ $ set linkerCommand "clang++"+ $ set defaultBuildFlags (return $+ let gcc_toolchain = ndk </> "toolchains/arm-linux-androideabi-4.8/prebuilt" </> osPrefix+ flags = ["-target", llvmTarget t, "-gcc-toolchain", gcc_toolchain]+ in mkDefaultBuildFlags ndk version (targetArch t)+ . append compilerFlags [(Nothing, flags)]+ . append linkerFlags flags+ )+ $ defaultToolChain+ where+ llvmTarget x =+ case targetArch x of+ Arm Armv5 -> "armv5te-none-linux-androideabi"+ Arm Armv7 -> "armv7-none-linux-androideabi"+ X86 I386 -> "i686-none-linux-android"+ arch -> unsupportedArch arch+toolChain _ _ (tcVariant, tcVersion) _ =+ error $ "Unknown toolchain variant "+ ++ show tcVariant ++ " "+ ++ showVersion tcVersion++-- | Valid Android ABI identifier for the given architecture.+abiString :: Arch -> String+abiString (Arm Armv5) = "armeabi"+abiString (Arm Armv6) = "armeabi"+abiString (Arm Armv7) = "armeabi-v7a"+abiString (X86 _) = "x86"+abiString arch = unsupportedArch arch++-- | Source paths and build flags for the @native_app_glue@ module.+native_app_glue :: FilePath -- ^ NDK source directory+ -> ([FilePath], BuildFlags -> BuildFlags)+native_app_glue ndk =+ ( [ndk </> "sources/android/native_app_glue/android_native_app_glue.c"]+ , append systemIncludes [ndk </> "sources/android/native_app_glue"] )++-- | Build flags for building with and linking against the GNU @gnustl@ standard C++ library.+gnustl :: Version -- ^ GNU STL version+ -> Linkage -- ^ `Static` or `Shared`+ -> FilePath -- ^ NDK source directory+ -> Target -- ^ Build target, see `target`+ -> (BuildFlags -> BuildFlags) -- ^ 'BuildFlags' modification function+gnustl version linkage ndk t =+ append systemIncludes [stlPath </> "include", stlPath </> "libs" </> abi </> "include"]+ . append libraryPath [stlPath </> "libs" </> abi]+ . append libraries [lib]+ where stlPath = ndk </> "sources/cxx-stl/gnu-libstdc++" </> showVersion version+ abi = abiString (targetArch t)+ lib = case linkage of+ Static -> "gnustl_static"+ Shared -> "gnustl_shared"++-- | Build flags for building with and linking against the LLVM @libc++@ standard C++ library.+libcxx :: Linkage -- ^ `Static` or `Shared`+ -> FilePath -- ^ NDK source directory+ -> Target -- ^ Build target, see `target`+ -> (BuildFlags -> BuildFlags) -- ^ 'BuildFlags' modification function+libcxx linkage ndk t =+ append systemIncludes [ stl </> "llvm-libc++" </> "libcxx" </> "include"+ -- NOTE: libcxx needs to be first in include path!+ , stl </> "llvm-libc++abi" </> "libcxxabi" </> "include"+ , ndk </> "sources" </> "android" </> "support" </> "include" ]+ . append compilerFlags [(Just Cpp, ["-stdlib=libc++"])]+ . append libraryPath [stl </> "llvm-libc++" </> "libs" </> abi]+ . prepend libraries [lib]+ where stl = ndk </> "sources" </> "cxx-stl"+ abi = abiString (targetArch t)+ lib = case linkage of+ Static -> "c++_static"+ Shared -> "c++_shared"
+ src/Development/Shake/Language/C/Target/Linux.hs view
@@ -0,0 +1,66 @@+-- Copyright 2012-2014 Samplecount S.L.+--+-- Licensed under the Apache License, Version 2.0 (the "License");+-- you may not use this file except in compliance with the License.+-- You may obtain a copy of the License at+--+-- http://www.apache.org/licenses/LICENSE-2.0+--+-- Unless required by applicable law or agreed to in writing, software+-- distributed under the License is distributed on an "AS IS" BASIS,+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+-- See the License for the specific language governing permissions and+-- limitations under the License.++{-|+Description: Toolchain definitions and utilities for Linux++This module provides toolchain definitions and utilities for targeting Linux.+See "Development.Shake.Language.C.Rules" for examples of how to use a target+toolchain.++Linux is also a supported host operating system, see+"Development.Shake.Language.C.Host" for examples of how to target the host.+-}++module Development.Shake.Language.C.Target.Linux (+ target+ , toolChain+) where++import Data.Label (get, set)+import Development.Shake+import Development.Shake.Language.C.BuildFlags+import Development.Shake.Language.C.Target+import Development.Shake.Language.C.ToolChain++-- | Build target given an architecture.+target :: Arch -> Target+target = Target Linux (Platform "linux")++platformArchiver :: Archiver+platformArchiver tc buildFlags inputs output = do+ need inputs+ command_ [] (tool tc archiverCommand)+ $ ["cr"]+ ++ get archiverFlags buildFlags+ ++ [output]+ ++ inputs+ command_ [] (toolFromString tc "ranlib") [output]++-- | Linux toolchain.+toolChain :: ToolChainVariant -> ToolChain+toolChain GCC =+ set variant GCC+ $ set compilerCommand "gcc"+ $ set archiverCommand "ar"+ $ set archiver platformArchiver+ $ set linkerCommand "g++"+ $ defaultToolChain+toolChain LLVM =+ set variant LLVM+ $ set compilerCommand "gcc"+ $ set archiverCommand "ar"+ $ set linkerCommand "g++"+ $ defaultToolChain+toolChain Generic = toolChain GCC
+ src/Development/Shake/Language/C/Target/NaCl.hs view
@@ -0,0 +1,173 @@+-- Copyright 2013 Samplecount S.L.+--+-- Licensed under the Apache License, Version 2.0 (the "License");+-- you may not use this file except in compliance with the License.+-- You may obtain a copy of the License at+--+-- http://www.apache.org/licenses/LICENSE-2.0+--+-- Unless required by applicable law or agreed to in writing, software+-- distributed under the License is distributed on an "AS IS" BASIS,+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+-- See the License for the specific language governing permissions and+-- limitations under the License.++{-|+Description: Toolchain definitions and utilities for Google Pepper and Portable Native Client++This module provides toolchain definitions and utilities for targeting Google+Pepper and Portable Native Client (PNaCl). Arguably it should be renamed+appropriately. See "Development.Shake.Language.C.Rules" for examples of how to+use a target toolchain.+-}+module Development.Shake.Language.C.Target.NaCl (+ pepper+ , canary+ , target+ , Config(..)+ , toolChain+ , finalize+ , translate+ , Arch(..)+ , mk_nmf+) where++import Data.List (intercalate)+import Development.Shake+import Development.Shake.FilePath+import Data.Version (Version(..))+import Development.Shake.Language.C hiding (Arch)+import qualified Development.Shake.Language.C.Target as C+import Development.Shake.Language.C.ToolChain+import qualified Development.Shake.Language.C.Host as Host+import Development.Shake.Language.C.Label++-- | Stable /Pepper/ API version.+pepper :: Int -> Version+pepper apiVersion = Version [apiVersion] []++-- | Unstable /Pepper Canary/ API version.+canary :: Version+canary = Version [] ["canary"]++-- | Pepper target.+--+-- `LLVM_IR` (PNaCl) is the only supported target architecture at the moment.+target :: Target+target = Target Pepper (Platform "pepper") LLVM_IR++hostString :: String+hostString =+ case Host.os of+ Host.Linux -> "linux"+ Host.OSX -> "mac"+ Host.Windows -> "win"++-- | Pepper build configuration.+--+-- This is used to select the respective library versions when linking.+data Config = Debug | Release deriving (Eq, Show)++-- | Construct Pepper toolchain.+toolChain :: FilePath -- ^ Pepper SDK directory (@nacl_sdk@)+ -> Version -- ^ Pepper API version, see `pepper` and `canary`+ -> Config -- ^ Build configuration for linked libraries+ -> Target -- ^ Target, see `target`+ -> ToolChain -- ^ Resulting toolchain+toolChain sdk sdkVersion config t =+ set variant LLVM+ $ set toolDirectory (Just (platformDir </> "toolchain" </> hostString ++ "_" ++ "pnacl" </> "bin"))+ $ set toolPrefix "pnacl-"+ $ set compilerCommand "clang"+ $ set archiverCommand "ar"+ $ set archiver (\tc flags inputs output -> do+ need inputs+ command_ [] (tool tc archiverCommand)+ $ ["cr"]+ ++ get archiverFlags flags+ ++ [output]+ ++ inputs+ command_ [] (toolFromString tc "ranlib") [output]+ )+ $ set linkerCommand "clang++"+ $ set defaultBuildFlags+ ( return $+ append systemIncludes [includeDir]+ . append userIncludes [includeDir]+ . append systemIncludes [includeDir </> "pnacl"]+ . append libraryPath [platformDir </> "lib" </> "pnacl" </> show config] )+ $ defaultToolChain+ where+ platformDir =+ sdk+ </> platformName (targetPlatform t)+ ++ "_"+ ++ case versionTags sdkVersion of+ ["canary"] -> "canary"+ _ -> show $ head (versionBranch sdkVersion)+ includeDir = platformDir </> "include"++-- | Finalize a bit code executable.+finalize :: ToolChain -- ^ Toolchain, see `toolChain`+ -> FilePath -- ^ Bit code input executable+ -> FilePath -- ^ Finalised bit code output executable+ -> Action ()+finalize tc input output = do+ need [input]+ command_ [] (toolFromString tc "finalize")+ ["-o", output, input]++-- | Translate bit code to native code.+translate :: ToolChain -> C.Arch -> FilePath -> FilePath -> Action ()+translate tc arch input output = do+ let archName =+ case arch of+ X86 I686 -> "i686"+ X86 X86_64 -> "x86-64"+ Arm Armv7 -> "armv7"+ _ -> error $ "Unsupported architecture: " ++ show arch+ need [input]+ command_ [] (toolFromString tc "finalize")+ ["-arch", archName, "-o", output, input]++-- | Pepper target architecture+data Arch =+ PNaCl -- ^ Portable Native Client architecture+ | NaCl C.Arch -- ^ Native Client architecture for specific CPU architecture+ deriving (Eq, Show)++-- | Create Native Client Manifest (nmf) file.+--+-- This file is needed for serving NaCl\/PNaCl outside the Google Play store. See the native client <https://developer.chrome.com/native-client/reference/nacl-manifest-format documentation> for more information on the file format.+mk_nmf :: [(Arch, FilePath)] -- ^ List of executables with the corresponding architecture+ -> FilePath -- ^ Output file+ -> Action ()+mk_nmf inputs output = do+ need $ map snd inputs+ writeFileLines output $ [+ "{"+ , " \"program\": {"+ ]+ ++ intercalate [","] (map program inputs) +++ [ " }"+ , " }"+ , "}"+ ]+ where+ program (PNaCl, input) = [+ " \"portable\": {"+ , " \"pnacl-translate\": {"+ , " \"url\": \"" ++ makeRelative (takeDirectory output) input ++ "\""+ , " }"+ ]+ program (NaCl arch, input) =+ let archName = case arch of+ X86 X86_64 -> "x86_64"+ X86 _ -> "x86_32"+ Arm _ -> "arm"+ _ -> error $ "mk_nmf: Unsupported architecture " ++ show arch+ in [+ " \"" ++ archName ++ "\": {"+ , " \"url\": \"" ++ makeRelative (takeDirectory output) input ++ "\""+ , " }"+ ]
+ src/Development/Shake/Language/C/Target/OSX.hs view
@@ -0,0 +1,185 @@+-- Copyright 2012-2014 Samplecount S.L.+--+-- Licensed under the Apache License, Version 2.0 (the "License");+-- you may not use this file except in compliance with the License.+-- You may obtain a copy of the License at+--+-- http://www.apache.org/licenses/LICENSE-2.0+--+-- Unless required by applicable law or agreed to in writing, software+-- distributed under the License is distributed on an "AS IS" BASIS,+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+-- See the License for the specific language governing permissions and+-- limitations under the License.++{-|+Description: Toolchain definitions and utilities for OSX and iOS++This module provides toolchain definitions and utilities for targeting OSX+and iOS. See "Development.Shake.Language.C.Rules" for examples of how to use a+target toolchain.++OSX is also a supported host operating system, see+"Development.Shake.Language.C.Host" for examples of how to target the host.+-}++module Development.Shake.Language.C.Target.OSX (+ DeveloperPath+ , getSDKRoot+ , macOSX+ , iPhoneOS+ , iPhoneSimulator+ , target+ , sdkVersion+ , toolChain+ , getPlatformVersions+ , macosx_version_min+ , iphoneos_version_min+ , universalBinary+) where++import Control.Applicative hiding ((*>))+import Data.List (stripPrefix)+import Data.List.Split (splitOn)+import Data.Maybe+import Data.Version (Version(..), showVersion)+import Development.Shake as Shake+import Development.Shake.FilePath+import Development.Shake.Language.C.BuildFlags+import Development.Shake.Language.C.Target+import Development.Shake.Language.C.Label+import Development.Shake.Language.C.ToolChain+import System.Process (readProcess)+import Text.Read (readEither)++archString :: Arch -> String+archString arch =+ case arch of+ X86 I386 -> "i386"+ X86 I686 -> "i686"+ X86 X86_64 -> "x86_64"+ Arm Armv5 -> "armv5"+ Arm Armv6 -> "armv6"+ Arm Armv7 -> "armv7"+ Arm Armv7s -> "armv7s"+ _ -> error $ "Unsupported OSX target architecture " ++ show arch++archFlags :: Target -> [String]+archFlags t = ["-arch", archString (targetArch t)]++-- | Base path of development tools on OSX.+newtype DeveloperPath = DeveloperPath FilePath+ deriving (Show)++-- | Get base path of development tools on OSX.+getSDKRoot :: Action DeveloperPath+getSDKRoot = liftIO $+ (DeveloperPath . head . splitOn "\n")+ <$> readProcess "xcode-select" ["--print-path"] ""++-- | Mac OSX platform.+macOSX :: Platform+macOSX = Platform "MacOSX"++-- | iOS platform.+iPhoneOS :: Platform+iPhoneOS = Platform "iPhoneOS"++-- | iOS simulator platform.+iPhoneSimulator :: Platform+iPhoneSimulator = Platform "iPhoneSimulator"++-- | Build target given a platform and an architecture.+target :: Platform -> Arch -> Target+target = Target OSX++sdkDirectory :: FilePath -> Platform -> FilePath+sdkDirectory sdkRoot platform =+ sdkRoot+ </> "Platforms"+ </> (platformName platform ++ ".platform")+ </> "Developer"+ </> "SDKs"++platformSDKPath :: FilePath -> Platform -> Version -> FilePath+platformSDKPath sdkRoot platform version =+ sdkDirectory sdkRoot platform+ </> platformName platform ++ showVersion version ++ ".sdk"++getPlatformVersionsWithRoot :: Platform -> DeveloperPath -> Action [Version]+getPlatformVersionsWithRoot platform (DeveloperPath sdkRoot) = do+ dirs <- getDirectoryDirs (sdkDirectory sdkRoot platform)+ case mapMaybe (stripPrefix name) dirs of+ [] -> error $ "OSX: No SDK found for " ++ name+ xs -> return [ flip Version []+ . map (either error id . readEither)+ . splitOn "."+ . dropExtension $ x+ | x <- xs ]+ where name = platformName platform++-- | Return a list of available platform SDK versions.+--+-- For example in order to get the latest iOS SDK version:+--+-- > maximum <$> getPlatformVersions iPhoneOS+getPlatformVersions :: Platform -> Action [Version]+getPlatformVersions platform =+ getPlatformVersionsWithRoot platform =<< getSDKRoot++-- | SDK version given major and minor version numbers.+sdkVersion :: Int -> Int -> Version+sdkVersion major minor = Version [major, minor] []++-- | Construct an OSX or iOS toolchain.+toolChain :: DeveloperPath -- ^ Developer tools base path, see `getSDKRoot`+ -> Version -- ^ Target SDK version+ -> Target -- ^ Build target, see `target`+ -> ToolChain -- ^ Resulting toolchain+toolChain (DeveloperPath sdkRoot) version t =+ set variant LLVM+ $ set toolDirectory (Just (sdkRoot </> "Toolchains/XcodeDefault.xctoolchain/usr/bin"))+ $ set compilerCommand "clang"+ $ set archiverCommand "libtool"+ $ set archiver (\tc flags inputs output -> do+ need inputs+ command_ [] (tool tc archiverCommand)+ $ get archiverFlags flags+ ++ ["-static"]+ ++ ["-o", output]+ ++ inputs+ )+ $ set linkerCommand "clang++"+ $ set linker (\lr tc ->+ case lr of+ Executable -> defaultLinker tc+ SharedLibrary -> defaultLinker tc . prepend linkerFlags ["-dynamiclib"]+ LoadableLibrary -> defaultLinker tc . prepend linkerFlags ["-bundle"]+ )+ $ set defaultBuildFlags+ ( return $+ append preprocessorFlags [ "-isysroot", sysRoot ]+ . append compilerFlags [(Nothing, archFlags t)]+ . append linkerFlags (archFlags t ++ [ "-isysroot", sysRoot ]) )+ $ defaultToolChain+ where sysRoot = platformSDKPath sdkRoot (targetPlatform t) version++-- | Specify the @-mmacosx-version-min@ compiler flag.+macosx_version_min :: Version -> BuildFlags -> BuildFlags+macosx_version_min version =+ append compilerFlags [(Nothing, ["-mmacosx-version-min=" ++ showVersion version])]++-- | Specify the @-miphoneos-version-min@ compiler flag.+iphoneos_version_min :: Version -> BuildFlags -> BuildFlags+iphoneos_version_min version =+ append compilerFlags [(Nothing, ["-miphoneos-version-min=" ++ showVersion version])]++-- | Create a universal binary a list of input files.+--+-- Calls <http://www.manpages.info/macosx/lipo.1.html lipo> with the @-create@ option.+universalBinary :: [FilePath] -- ^ Input files, can be executables, dynamic libraries or static archives but should be all of the same type+ -> FilePath -- ^ Output file path+ -> Action ()+universalBinary inputs output = do+ need inputs+ command_ [] "lipo" $ ["-create", "-output", output] ++ inputs
+ src/Development/Shake/Language/C/Target/Windows.hs view
@@ -0,0 +1,67 @@+-- Copyright 2014 Samplecount S.L.+--+-- Licensed under the Apache License, Version 2.0 (the "License");+-- you may not use this file except in compliance with the License.+-- You may obtain a copy of the License at+--+-- http://www.apache.org/licenses/LICENSE-2.0+--+-- Unless required by applicable law or agreed to in writing, software+-- distributed under the License is distributed on an "AS IS" BASIS,+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+-- See the License for the specific language governing permissions and+-- limitations under the License.++{-|+Description: Toolchain definitions and utilities for Windows++This module provides toolchain definitions and utilities for targeting Windows.+See "Development.Shake.Language.C.Rules" for examples of how to use a target+toolchain.++Windows is also a supported host operating system, see+"Development.Shake.Language.C.Host" for examples of how to target the host.++On Windows currently only the <http://www.mingw.org/ MinGW> toolchain is+supported.+-}++module Development.Shake.Language.C.Target.Windows (+ target+ , toolChain+) where++import Data.Label (get, set)+import Development.Shake+import Development.Shake.Language.C.BuildFlags+import Development.Shake.Language.C.Target+import Development.Shake.Language.C.ToolChain++-- | Build target given an architecture.+target :: Arch -> Target+target = Target Windows (Platform "windows")++-- | Windows toolchain.+toolChain :: ToolChainVariant -> ToolChain+toolChain GCC =+ set variant GCC+ $ set compilerCommand "gcc"+ $ set archiverCommand "ar"+ $ set archiver (\tc flags inputs output -> do+ need inputs+ command_ [] (tool tc archiverCommand)+ $ ["cr"]+ ++ get archiverFlags flags+ ++ [output]+ ++ inputs+ command_ [] (toolFromString tc "ranlib") [output]+ )+ $ set linkerCommand "g++"+ $ defaultToolChain+toolChain LLVM =+ set variant LLVM+ $ set compilerCommand "gcc"+ $ set archiverCommand "ar"+ $ set linkerCommand "g++"+ $ defaultToolChain+toolChain Generic = toolChain GCC
+ src/Development/Shake/Language/C/ToolChain.hs view
@@ -0,0 +1,271 @@+-- Copyright 2012-2014 Samplecount S.L.+--+-- Licensed under the Apache License, Version 2.0 (the "License");+-- you may not use this file except in compliance with the License.+-- You may obtain a copy of the License at+--+-- http://www.apache.org/licenses/LICENSE-2.0+--+-- Unless required by applicable law or agreed to in writing, software+-- distributed under the License is distributed on an "AS IS" BASIS,+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+-- See the License for the specific language governing permissions and+-- limitations under the License.++{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE TypeOperators #-}++{-|+Description: Types and functions for working with target tool chains+-}++module Development.Shake.Language.C.ToolChain (+ Linkage(..)+ -- ** Working with toolchains+ , ToolChain+ , ToolChainVariant(..)+ , toolDirectory -- | Directory prefix for tools in a `ToolChain`, e.g. @\/usr\/local\/linux-armv5-eabi\/bin@.+ , toolPrefix -- | Prefix string for tools in a `ToolChain`, e.g. @"linux-armv5-eabi-"@.+ , variant -- | Toolchain variant.+ , compilerCommand -- | Compiler command, usually used in the `compiler` action.+ , Compiler+ , compiler -- | Compiler action for this `ToolChain`.+ , archiverCommand -- | Archiver command, usually used in the `archiver` action.+ , Archiver+ , archiver -- | Archiver action for this `ToolChain`.+ , linkerCommand -- | Linker command, usually used in the `linker` action.+ , Linker+ , LinkResult(..)+ , linker -- | Linker action for this `ToolChain`.+ , defaultBuildFlags -- | Action returning the default `BuildFlags` for this `ToolChain`.++ -- ** Interfacing with other build systems+ , applyEnv+ , toEnv+ -- ** Utilities for toolchain writers+ , defaultToolChain+ , defaultCompiler+ , defaultArchiver+ , defaultLinker+ , toolFromString+ , tool+) where++import Control.Applicative+import Data.Char (toLower)+import Data.List (isInfixOf, isSuffixOf)+import Data.Monoid (mempty)+import Development.Shake+import Development.Shake.FilePath+import Development.Shake.Util (needMakefileDependencies)+import Development.Shake.Language.C.Label+import Development.Shake.Language.C.BuildFlags+import Development.Shake.Language.C.Language (languageOf)+import Development.Shake.Language.C.Util++-- | Linkage type, static or shared.+data Linkage = Static | Shared deriving (Enum, Eq, Show)++-- | Link result type+data LinkResult =+ Executable -- ^ Executable+ | SharedLibrary -- ^ Shared (dynamically linked) library+ | LoadableLibrary -- ^ Dynamically loadable library+ deriving (Enum, Eq, Show)++-- | Toolchain variant.+data ToolChainVariant =+ Generic -- ^ Unspecified toolchain+ | GCC -- ^ GNU Compiler Collection (gcc) toolchain+ | LLVM -- ^ Low-Level Virtual Machine (LLVM) toolchain+ deriving (Eq, Show)++-- | `Action` type for producing an object file from a source file.+type Compiler =+ ToolChain -- ^ Toolchain+ -> BuildFlags -- ^ Compiler flags+ -> FilePath -- ^ Input source file+ -> FilePath -- ^ Output object file+ -> Action ()++-- | `Action` type for linking object files into an executable or a library.+type Linker =+ ToolChain -- ^ Toolchain+ -> BuildFlags -- ^ Linker flags+ -> [FilePath] -- ^ Input object files+ -> FilePath -- ^ Output link product+ -> Action ()++-- | `Action` type for archiving object files into a static library.+type Archiver =+ ToolChain -- ^ Toolchain+ -> BuildFlags -- ^ Archiver flags+ -> [FilePath] -- ^ Input object files+ -> FilePath -- ^ Output object archive (static library)+ -> Action ()++data ToolChain = ToolChain {+ _variant :: ToolChainVariant+ , _toolDirectory :: Maybe FilePath+ , _toolPrefix :: String+ , _compilerCommand :: FilePath+ , _compiler :: Compiler+ , _archiverCommand :: FilePath+ , _archiver :: Archiver+ , _linkerCommand :: FilePath+ , _linker :: LinkResult -> Linker+ , _defaultBuildFlags :: Action (BuildFlags -> BuildFlags)+ }++mkLabel ''ToolChain++-- | Default compiler action.+defaultCompiler :: Compiler+defaultCompiler toolChain buildFlags input output = do+ need $ [input]+ let depFile = output <.> "d"+ command_ [] (tool toolChain compilerCommand)+ $ concatMapFlag "-I" (get systemIncludes buildFlags)+ ++ mapFlag "-iquote" (get userIncludes buildFlags)+ ++ defineFlags buildFlags+ ++ get preprocessorFlags buildFlags+ ++ compilerFlagsFor (languageOf input) buildFlags+ ++ ["-MD", "-MF", depFile]+ ++ ["-c", "-o", output, input]+ needMakefileDependencies depFile++-- | Default archiver action.+defaultArchiver :: Archiver+defaultArchiver toolChain buildFlags inputs output = do+ need inputs+ command_ [] (tool toolChain archiverCommand)+ $ get archiverFlags buildFlags+ ++ [output]+ ++ inputs++-- | Default linker action.+defaultLinker :: Linker+defaultLinker toolChain buildFlags inputs output = do+ let localLibs = get localLibraries buildFlags+ buildFlags' = append libraryPath (map takeDirectory localLibs)+ -- Local libraries must be passed to the linker before system libraries they depend on+ . prepend libraries (map (strip.dropExtension.takeFileName) localLibs)+ $ buildFlags+ need $ inputs ++ localLibs+ command_ [] (tool toolChain linkerCommand)+ $ inputs+ ++ get linkerFlags buildFlags'+ ++ concatMapFlag "-L" (get libraryPath buildFlags')+ ++ concatMapFlag "-l" (get libraries buildFlags')+ ++ ["-o", output]+ where+ strip ('l':'i':'b':rest) = rest+ strip x = x++-- | Default toolchain.+--+-- Probably not useful without modification.+defaultToolChain :: ToolChain+defaultToolChain =+ ToolChain {+ _variant = GCC+ , _toolDirectory = Nothing+ , _toolPrefix = ""+ , _compilerCommand = "gcc"+ , _compiler = defaultCompiler+ , _archiverCommand = "ar"+ , _archiver = defaultArchiver+ , _linkerCommand = "gcc"+ , _linker = \linkResult linkerCmd ->+ case linkResult of+ Executable -> defaultLinker linkerCmd+ _ -> defaultLinker linkerCmd . append linkerFlags ["-shared"]+ , _defaultBuildFlags = return id+ }++-- | Given a tool chain command name, construct the command's full path, taking into account the toolchain's `toolPrefix`.+toolFromString ::+ ToolChain -- ^ Toolchain+ -> String -- ^ Command name+ -> FilePath -- ^ Full command path+toolFromString toolChain name =+ let c = _toolPrefix toolChain ++ name+ in maybe c (</> c) (_toolDirectory toolChain)++-- | Construct the full path of a predefined tool given a `ToolChain` accessor.+tool ::+ ToolChain -- ^ Toolchain+ -> (ToolChain :-> String) -- ^ Toolchain accessor+ -> FilePath -- ^ Full command path+tool toolChain getter = toolFromString toolChain (get getter toolChain)++-- | Apply the current environment and return a modified toolchain.+--+-- This function is experimental and subject to change!+--+-- Currently recognised environment variables are+--+-- [@CC@] Path to @C@ compiler.+--+-- [@SHAKE_TOOLCHAIN_VARIANT@] One of the values of 'ToolChainVariant' (case insensitive). If this variable is not present, an attempt is made to determine the toolchain variant from the @C@ compiler command.+applyEnv :: ToolChain -> Action ToolChain+applyEnv toolChain = do+ cc <- getEnv "CC"+ vendor <- getEnv "SHAKE_TOOLCHAIN_VARIANT"+ return $ maybe id (set compilerCommand) cc+ . maybe id (set variant) ((vendor >>= parseVendor) <|> (cc >>= vendorFromCommand))+ $ toolChain+ where+ parseVendor s =+ case map toLower s of+ "gcc" -> Just GCC+ "llvm" -> Just LLVM+ "clang" -> Just LLVM+ _ -> Nothing+ vendorFromCommand path =+ let x = takeFileName path+ in if "gcc" `isInfixOf` x || "g++" `isInfixOf` x+ then Just GCC+ else if "clang" `isInfixOf` x+ then Just LLVM+ else Just Generic++-- | Export a 'ToolChain' definition to a list of environment variable mappings, suitable e.g. for calling third-party configure scripts in cross-compilation mode.+--+-- Needs some fleshing out; currently only works for "standard" binutil toolchains.+toEnv :: ToolChain -> Action [(String,String)]+toEnv tc = do+ flags <- (\f -> f mempty) <$> get defaultBuildFlags tc+ let cflags = concatMapFlag "-I" (map escapeSpaces (get systemIncludes flags))+ ++ concatMapFlag "-I" (map escapeSpaces (get userIncludes flags))+ ++ defineFlags flags+ ++ get preprocessorFlags flags+ ++ compilerFlagsFor (Just C) flags+ cxxflags = cflags ++ compilerFlagsFor (Just Cpp) flags+ ldflags = get linkerFlags flags+ ++ concatMapFlag "-L" (get libraryPath flags)+ ++ concatMapFlag "-l" (get libraries flags)+ c2cxx path = let x = takeFileName path+ in if "gcc" `isSuffixOf` x+ then (++"g++") . reverse . drop 3 . reverse $ path+ else if "clang" `isSuffixOf` x+ then path ++ "++"+ else path+ let cc = tool tc compilerCommand+ cpp = toolFromString tc "cpp"+ cppExists <- doesFileExist cpp+ let cpp' = if cppExists then cpp else cc ++ " -E"+ return $ [+ ("CPP", cpp')+ , ("AR", tool tc archiverCommand)+ , ("NM", toolFromString tc "nm")+ , ("CC", tool tc compilerCommand)+ -- FIXME: Configure toolchain with compiler command per language?+ , ("CXX", c2cxx $ tool tc compilerCommand)+ , ("LD", toolFromString tc "ld")+ , ("RANLIB", toolFromString tc "ranlib")+ , ("CFLAGS", unwords cflags)+ , ("CPPFLAGS", unwords cflags)+ , ("CXXFLAGS", unwords cxxflags)+ , ("LDFLAGS", unwords ldflags)+ ]
+ src/Development/Shake/Language/C/Util.hs view
@@ -0,0 +1,65 @@+-- Copyright 2012-2014 Samplecount S.L.+--+-- Licensed under the Apache License, Version 2.0 (the "License");+-- you may not use this file except in compliance with the License.+-- You may obtain a copy of the License at+--+-- http://www.apache.org/licenses/LICENSE-2.0+--+-- Unless required by applicable law or agreed to in writing, software+-- distributed under the License is distributed on an "AS IS" BASIS,+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+-- See the License for the specific language governing permissions and+-- limitations under the License.++module Development.Shake.Language.C.Util (+ mapFlag+ , concatMapFlag+ , escapeSpaces+ , words'+) where++import Data.List++mapFlag :: String -> [String] -> [String]+mapFlag f = concatMap (\x -> [f, x])++concatMapFlag :: String -> [String] -> [String]+concatMapFlag f = map (f++)++-- | Escape spaces with '\\' character.+--+-- >>> escapeSpaces "string contains spaces"+-- "string\\ contains\\ spaces"+--+-- >>> escapeSpaces " leading and trailing spaces "+-- "\\ leading\\ and\\ trailing\\ spaces\\ "+--+-- >>> escapeSpaces "noSpaces"+-- "noSpaces"+--+escapeSpaces :: String -> String+escapeSpaces [] = []+escapeSpaces (' ':xs) = '\\' : ' ' : escapeSpaces xs+escapeSpaces ('\\':xs) = '\\' : '\\' : escapeSpaces xs+escapeSpaces (x:xs) = x : escapeSpaces xs++-- | Split a list of space separated strings.+--+-- Spaces can be escaped by '\\'.+--+-- >>> words' "word and word\\ with\\ spaces"+-- ["word","and","word with spaces"]+--+words' :: String -> [String]+words' = unescape . words+ where+ escape = "\\"+ escapeLength = length escape+ isEscaped = isSuffixOf escape+ dropEscape = (++" ") . reverse . drop escapeLength . reverse+ unescape [] = []+ unescape [x] = [if isEscaped x then dropEscape x else x]+ unescape (x1:x2:xs)+ | isEscaped x1 = unescape ((dropEscape x1 ++ x2):xs)+ | otherwise = [x1] ++ unescape (x2:xs)
+ tests/doctests.hs view
@@ -0,0 +1,23 @@+-- Copyright 2014 Samplecount S.L.+--+-- Licensed under the Apache License, Version 2.0 (the "License");+-- you may not use this file except in compliance with the License.+-- You may obtain a copy of the License at+--+-- http://www.apache.org/licenses/LICENSE-2.0+--+-- Unless required by applicable law or agreed to in writing, software+-- distributed under the License is distributed on an "AS IS" BASIS,+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+-- See the License for the specific language governing permissions and+-- limitations under the License.++import Test.DocTest++main = doctest [+ "-isrc"+ , "src/Development/Shake/Language/C/BuildFlags.hs"+ , "src/Development/Shake/Language/C/Config.hs"+ , "src/Development/Shake/Language/C/Target.hs"+ , "src/Development/Shake/Language/C/Util.hs"+ ]
+ tests/spectests.hs view
@@ -0,0 +1,90 @@+module Main where++import Control.Applicative hiding ((*>))+import Control.Monad+import Data.IORef+import Development.Shake+import Development.Shake.FilePath+import Development.Shake.Language.C+import qualified Development.Shake.Language.C.Host as Host+import qualified System.Directory as Dir+import Test.Hspec++buildDir :: FilePath+buildDir = "tests/build"++withShake :: String+ -> (FilePath+ -> (FilePath -> FilePath)+ -> (FilePath -> FilePath)+ -> Rules FilePath)+ -> IO String+withShake name mkRules = do+ ref <- newIORef undefined+ shake shakeOptions { shakeFiles = addTrailingPathSeparator buildDir } $ do+ output <- mkRules name+ (\x -> buildDir </> name </> "input" </> x)+ (\x -> buildDir </> name </> "output" </> x)+ action $ do+ need [output]+ liftIO $ writeIORef ref output+ readFile =<< readIORef ref++shouldBeBuiltBy :: String+ -> (FilePath+ -> (FilePath -> FilePath)+ -> (FilePath -> FilePath)+ -> Rules FilePath)+ -> Expectation+shouldBeBuiltBy name mkRules =+ withShake name mkRules `shouldReturn` unlines [name]++cstring :: String -> String+cstring = show++main :: IO ()+main = hspec $ do+ runIO $ do+ b <- Dir.doesDirectoryExist buildDir+ when b $ Dir.removeDirectoryRecursive buildDir+ describe "Host toolchain" $ do+ it "compiles a C file to an executable" $ do+ "host_toolchain_compile_c" `shouldBeBuiltBy` \name mkInput mkOutput -> do+ let (_, toolChain) = Host.defaultToolChain+ input = mkInput "source.c"+ outputGen = mkOutput $ "result" <.> exe+ output = mkOutput "result.txt"+ input *> \path -> do+ writeFileLines path [+ "#include <stdio.h>"+ , "int main(int argc, char** argv)"+ , "{"+ , " printf(\"%s\\n\", " ++ cstring name ++ ");"+ , " return 0;"+ , "}"+ ]+ _ <- executable toolChain outputGen (pure id) (pure [input])+ output *> \path -> do+ need [outputGen]+ cmd Shell (outputGen ++ " > " ++ path)+ return output+ it "compiles a C++ file to an executable" $ do+ "host_toolchain_compile_cpp" `shouldBeBuiltBy` \name mkInput mkOutput -> do+ let (_, toolChain) = Host.defaultToolChain+ input = mkInput "source.cpp"+ outputGen = mkOutput $ "result" <.> exe+ output = mkOutput "result.txt"+ input *> \path -> do+ writeFileLines path [+ "#include <iostream>"+ , "int main(int argc, char** argv)"+ , "{"+ , " std::cout << " ++ cstring name ++ " << std::endl;"+ , " return 0;"+ , "}"+ ]+ _ <- executable toolChain outputGen (pure id) (pure [input])+ output *> \path -> do+ need [outputGen]+ cmd Shell (outputGen ++ " > " ++ path)+ return output