packages feed

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 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 +[![Build Status](https://travis-ci.org/samplecount/shake-language-c.svg?branch=develop)](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