-- 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