├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── Setup.hs ├── default.nix ├── examples ├── shakefile.hs └── src │ └── foo.cpp ├── shake-language-c.cabal ├── src └── Development │ └── Shake │ └── Language │ ├── C.hs │ └── C │ ├── BuildFlags.hs │ ├── Config.hs │ ├── Host.hs │ ├── Host │ ├── Linux.hs │ ├── OSX.hs │ └── Windows.hs │ ├── Label.hs │ ├── Language.hs │ ├── PkgConfig.hs │ ├── Rules.hs │ ├── Target.hs │ ├── Target │ ├── Android.hs │ ├── Linux.hs │ ├── NaCl.hs │ ├── OSX.hs │ └── Windows.hs │ ├── ToolChain.hs │ └── Util.hs ├── stack.yaml ├── tests ├── doctests.hs └── spectests.hs └── tools └── git-hooks └── pre-commit /.gitignore: -------------------------------------------------------------------------------- 1 | /.shake.database 2 | /.stack-work 3 | /build/ 4 | /tests/build/ 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # Use container infrastructure to enable caching 2 | sudo: false 3 | 4 | # Do not choose a language; we provide our own build tools. 5 | language: haskell 6 | 7 | # Caching so the next build will be fast too. 8 | cache: 9 | directories: 10 | - $HOME/.stack 11 | 12 | # Ensure necessary system libraries are present 13 | addons: 14 | apt: 15 | packages: 16 | - libgmp-dev 17 | 18 | before_install: 19 | # Download and unpack the stack executable 20 | - mkdir -p ~/.local/bin 21 | - export SAVED_PATH="$PATH" 22 | - export PATH=$HOME/.local/bin:$PATH 23 | - travis_retry curl -L https://www.stackage.org/stack/linux-x86_64 | tar xz --wildcards --strip-components=1 -C ~/.local/bin '*/stack' 24 | 25 | install: 26 | # Build dependencies 27 | - stack --no-terminal --install-ghc test --only-dependencies 28 | 29 | script: 30 | # Build the package, its tests, and its docs and run the tests 31 | - stack --no-terminal test --haddock --no-haddock-deps 32 | 33 | before_deploy: 34 | # Restore PATH in order to use globally installed Cabal. 35 | # The one installed by stack needs an extra upload --publish flag for fully 36 | # automatic deployment. 37 | - export PATH="$SAVED_PATH" 38 | 39 | deploy: 40 | provider: hackage 41 | username: StefanKersten 42 | password: 43 | secure: OeylLs0HzrV/W756CTWLtsaVJXENE9y6axQVeFPxLCQA42YxiHOS7xaiE/QHXWVNtXKSIqSmbrPZ4/ycMl+80YxrlY7QwoFD9ikZsENIAe6xg80z2eRzWgtp2pdlpfG0bBfAdOZ3p5aKcwF69bM2UaQWcWuHGxNcMljpN7I0TL8= 44 | on: 45 | repo: samplecount/shake-language-c 46 | branch: master 47 | 48 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog for shake-language-c 2 | 3 | ## v0.13.0 4 | 5 | * Add "Asm" source language 6 | * Add "None" OS 7 | 8 | ## v0.12.0 9 | 10 | * Support GHC 8.4.1 11 | 12 | ## v0.11.0 13 | 14 | * Support shake 0.16 15 | 16 | ## v0.10.1 17 | 18 | * Add support for Linux ARMv7 19 | 20 | ## v0.10.0 21 | 22 | * Add mkConfig function that caches dependencies 23 | 24 | ## v0.9.1 25 | 26 | * Fix host architecture detection on Windows 10 27 | 28 | ## v0.9.0 29 | 30 | * Add support for the Android *arm64-v8a* target architecture and drop support for specifying the toolchain version; this API breaking change requires a minimum Android NDK revision 11c 31 | 32 | ## v0.8.6 33 | 34 | * Fix Windows host target 35 | * Get host architecture from environment on Windows 36 | 37 | ## v0.8.3 38 | 39 | * Allow to set linker command via `LD` environment variable 40 | 41 | ## v0.8.2 42 | 43 | * Fix compiler and linker commands for Clang toolchain on Linux 44 | 45 | ## v0.8.1 46 | 47 | * Use `-I` compiler flag for the `userIncludes` of `BuildFlags` and `-isystem` for `systemIncludes`; semantics should be as before for `gcc` and `clang` but `-isystem` suppresses warnings in system headers 48 | 49 | ## v0.8.0 50 | 51 | * Refactor NMF file creation in NaCl module 52 | 53 | ## v0.7.1 54 | 55 | * Fix compilation error with GHC 7.10 in test suite (#25) 56 | 57 | ## v0.7.0 58 | 59 | * Add `arm64` ARM version 60 | * Add support for `arm64` to OSX toolchains 61 | * Fix compilation error with GHC 7.10 (#25) 62 | 63 | ## v0.6.4 64 | 65 | * Fix Android toolchain definition for `x86` architecture 66 | 67 | ## v0.6.3 68 | 69 | * Fix bug in `Development.Shake.Language.C.Target.OSX`: `getPlatformVersionsWithRoot` works correctly now with SDK directories without version number, as introduced by Xcode 6 70 | 71 | ## v0.6.2 72 | 73 | Bug fix release. 74 | 75 | ## v0.6.1 76 | 77 | Bug fix release. 78 | 79 | ## v0.6.0 80 | 81 | ### Added 82 | 83 | * Add `Data.Default.Class.Default` instances for some data types; add dependency on package `data-default-class`. 84 | 85 | ### Changed 86 | 87 | * 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. 88 | * Export `Development.Shake.Language.C.Language.Language` from `Development.Shake.Language.C.BuildFlags` instead of `Development.Shake.Language.C`. 89 | * Export `Development.Shake.Language.C.Rules` from `Development.Shake.Language.C`; hide `Development.Shake.Language.C.Rules` in Cabal file. 90 | * **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'`. 91 | 92 | ### Removed 93 | 94 | * Remove `libppapi`, `libppapi_cpp`, `libnacl_io`, `libppapi_simple` from `Development.Shake.Language.C.Target.NaCl`. 95 | * Remove `Development.Shake.Language.C.Target.archString`. 96 | 97 | ## v0.5.0 98 | 99 | First released version. 100 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # shake-language-c 2 | 3 | [![Hackage version](https://img.shields.io/hackage/v/shake-language-c.svg?style=flat)](https://hackage.haskell.org/package/shake-language-c) 4 | [![Stackage LTS](http://stackage.org/package/shake-language-c/badge/lts)](http://stackage.org/lts/package/shake-language-c) 5 | [![Stackage Nightly](http://stackage.org/package/shake-language-c/badge/nightly)](http://stackage.org/nightly/package/shake-language-c) 6 | [![Build Status](https://travis-ci.org/samplecount/shake-language-c.svg?branch=develop)](https://travis-ci.org/samplecount/shake-language-c) 7 | 8 | **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*. 9 | 10 | ## Documentation 11 | 12 | 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. 13 | 14 | ## Examples 15 | 16 | Here's an *iOS* example that compiles all `.cpp` files in the `src` directory. The resulting static library `libexample.a` can then be used e.g. from an `XCode` project. 17 | 18 | import Control.Applicative 19 | import Control.Arrow 20 | import Development.Shake 21 | import Development.Shake.FilePath 22 | import Development.Shake.Language.C 23 | import qualified Development.Shake.Language.C.Target.OSX as OSX 24 | 25 | main :: IO () 26 | main = shakeArgs shakeOptions { shakeFiles = "build/" } $ do 27 | let target = OSX.target OSX.iPhoneOS (Arm Armv7s) 28 | toolChain = OSX.toolChain 29 | <$> OSX.getSDKRoot 30 | <*> (maximum <$> OSX.getPlatformVersions (targetPlatform target)) 31 | <*> pure target 32 | 33 | lib <- staticLibrary toolChain 34 | ("build" toBuildPrefix target "libexample.a") 35 | (return $ 36 | append compilerFlags [(Just Cpp, ["-std=c++11"])] 37 | >>> append compilerFlags [(Nothing, ["-O3"])] 38 | >>> append userIncludes ["include"] ) 39 | (getDirectoryFiles "" ["src//*.cpp"]) 40 | 41 | want [lib] 42 | 43 | A more complex [build script](https://github.com/samplecount/methcla/tree/develop/Shake_Methcla.hs) is used by the [Methcla](http://methc.la) sound engine library. It defines Shake rules for building the library on various platforms and also exports functions for transparently including the library into other build systems. The build script makes extensive use of Shake [configuration files](https://github.com/samplecount/methcla/tree/develop/config). 44 | -------------------------------------------------------------------------------- /Setup.hs: -------------------------------------------------------------------------------- 1 | -- Copyright 2012-2013 Samplecount S.L. 2 | -- 3 | -- Licensed under the Apache License, Version 2.0 (the "License"); 4 | -- you may not use this file except in compliance with the License. 5 | -- You may obtain a copy of the License at 6 | -- 7 | -- http://www.apache.org/licenses/LICENSE-2.0 8 | -- 9 | -- Unless required by applicable law or agreed to in writing, software 10 | -- distributed under the License is distributed on an "AS IS" BASIS, 11 | -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | -- See the License for the specific language governing permissions and 13 | -- limitations under the License. 14 | 15 | import Distribution.Simple (defaultMain) 16 | 17 | main :: IO () 18 | main = defaultMain 19 | -------------------------------------------------------------------------------- /default.nix: -------------------------------------------------------------------------------- 1 | { mkDerivation, base, data-default-class, directory, doctest 2 | , fclabels, hspec, lib, process, shake, split, unordered-containers 3 | }: 4 | mkDerivation { 5 | pname = "shake-language-c"; 6 | version = "0.13.1"; 7 | src = ./.; 8 | libraryHaskellDepends = [ 9 | base data-default-class fclabels process shake split 10 | unordered-containers 11 | ]; 12 | testHaskellDepends = [ base directory doctest hspec shake ]; 13 | doHaddock = false; 14 | doCheck = false; 15 | homepage = "https://github.com/samplecount/shake-language-c"; 16 | description = "Utilities for cross-compiling with Shake"; 17 | license = lib.licenses.asl20; 18 | } 19 | -------------------------------------------------------------------------------- /examples/shakefile.hs: -------------------------------------------------------------------------------- 1 | module Main where 2 | 3 | import Control.Applicative 4 | import Control.Arrow 5 | import Development.Shake 6 | import Development.Shake.FilePath 7 | import Development.Shake.Language.C 8 | import qualified Development.Shake.Language.C.Target.OSX as OSX 9 | 10 | main :: IO () 11 | main = shakeArgs shakeOptions { shakeFiles = "build/" } $ do 12 | let target = OSX.target OSX.iPhoneOS (Arm Armv7s) 13 | toolChain = OSX.toolChain 14 | <$> OSX.getSDKRoot 15 | <*> (maximum <$> OSX.getPlatformVersions (targetPlatform target)) 16 | <*> pure target 17 | 18 | lib <- staticLibrary toolChain 19 | ("build" toBuildPrefix target "libexample.a") 20 | (return $ 21 | append compilerFlags [(Just Cpp, ["-std=c++11"])] 22 | >>> append compilerFlags [(Nothing, ["-O3"])] 23 | >>> append userIncludes ["include"] ) 24 | (getDirectoryFiles "" ["src//*.cpp"]) 25 | 26 | want [lib] 27 | -------------------------------------------------------------------------------- /examples/src/foo.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void foo() 4 | { 5 | std::cout << "foo!" << std::endl; 6 | } 7 | 8 | -------------------------------------------------------------------------------- /shake-language-c.cabal: -------------------------------------------------------------------------------- 1 | -- Copyright 2012-2014 Samplecount S.L. 2 | -- 3 | -- Licensed under the Apache License, Version 2.0 (the "License"); 4 | -- you may not use this file except in compliance with the License. 5 | -- You may obtain a copy of the License at 6 | -- 7 | -- http://www.apache.org/licenses/LICENSE-2.0 8 | -- 9 | -- Unless required by applicable law or agreed to in writing, software 10 | -- distributed under the License is distributed on an "AS IS" BASIS, 11 | -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | -- See the License for the specific language governing permissions and 13 | -- limitations under the License. 14 | 15 | Name: shake-language-c 16 | Version: 0.13.1 17 | Synopsis: Utilities for cross-compiling with Shake 18 | Description: This library provides 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. 19 | Category: Development 20 | License: Apache-2.0 21 | License-File: LICENSE 22 | 23 | Copyright: Copyright (c) 2012 Samplecount S.L. 24 | Homepage: https://github.com/samplecount/shake-language-c 25 | Bug-Reports: https://github.com/samplecount/shake-language-c/issues 26 | Maintainer: stefan@samplecount.com 27 | 28 | Cabal-Version: >= 1.8 29 | Build-Type: Simple 30 | 31 | Extra-Source-Files: 32 | CHANGELOG.md 33 | README.md 34 | 35 | Library 36 | Hs-Source-Dirs: src 37 | Exposed-Modules: 38 | Development.Shake.Language.C 39 | Development.Shake.Language.C.BuildFlags 40 | Development.Shake.Language.C.Config 41 | Development.Shake.Language.C.Host 42 | Development.Shake.Language.C.Label 43 | Development.Shake.Language.C.PkgConfig 44 | Development.Shake.Language.C.ToolChain 45 | Development.Shake.Language.C.Target.Android 46 | Development.Shake.Language.C.Target.Linux 47 | Development.Shake.Language.C.Target.NaCl 48 | Development.Shake.Language.C.Target.OSX 49 | Development.Shake.Language.C.Target.Windows 50 | Other-Modules: 51 | Development.Shake.Language.C.Host.Linux 52 | Development.Shake.Language.C.Host.OSX 53 | Development.Shake.Language.C.Host.Windows 54 | Development.Shake.Language.C.Language 55 | Development.Shake.Language.C.Rules 56 | Development.Shake.Language.C.Target 57 | Development.Shake.Language.C.Util 58 | Ghc-Options: -Wall 59 | Build-Depends: 60 | base >= 4 && < 5 61 | , data-default-class 62 | , fclabels >= 2 63 | , process 64 | , shake >= 0.16 65 | , split 66 | , unordered-containers 67 | if impl(ghc < 8.0) 68 | Build-Depends: semigroups >= 0.18 69 | 70 | test-suite doctests 71 | type: exitcode-stdio-1.0 72 | hs-source-dirs: tests 73 | main-is: doctests.hs 74 | ghc-options: -threaded 75 | build-depends: base, doctest >= 0.8, shake, shake-language-c 76 | 77 | test-suite spectests 78 | type: exitcode-stdio-1.0 79 | hs-source-dirs: tests 80 | main-is: spectests.hs 81 | ghc-options: -threaded 82 | build-depends: base, directory, hspec, shake, shake-language-c 83 | 84 | Source-Repository head 85 | Type: git 86 | Location: https://github.com/samplecount/shake-language-c.git 87 | -------------------------------------------------------------------------------- /src/Development/Shake/Language/C.hs: -------------------------------------------------------------------------------- 1 | -- Copyright 2012-2014 Samplecount S.L. 2 | -- 3 | -- Licensed under the Apache License, Version 2.0 (the "License"); 4 | -- you may not use this file except in compliance with the License. 5 | -- You may obtain a copy of the License at 6 | -- 7 | -- http://www.apache.org/licenses/LICENSE-2.0 8 | -- 9 | -- Unless required by applicable law or agreed to in writing, software 10 | -- distributed under the License is distributed on an "AS IS" BASIS, 11 | -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | -- See the License for the specific language governing permissions and 13 | -- limitations under the License. 14 | 15 | {-| 16 | Description: Build @C@ language projects for various target platforms 17 | -} 18 | 19 | module Development.Shake.Language.C ( 20 | -- * Build targets 21 | -- $targets 22 | module Development.Shake.Language.C.Target 23 | -- * High-level build rules 24 | , Linkage(..) 25 | , module Development.Shake.Language.C.BuildFlags 26 | , module Development.Shake.Language.C.Rules 27 | -- * Toolchain types and utilities 28 | , ToolChain 29 | , ToolChainVariant(..) 30 | , applyEnv 31 | , toEnv 32 | ) where 33 | 34 | import Development.Shake.Language.C.BuildFlags 35 | import Development.Shake.Language.C.Rules 36 | import Development.Shake.Language.C.Target 37 | import Development.Shake.Language.C.ToolChain 38 | 39 | {- $targets 40 | 41 | This library's focus is on cross compilation. Here's a list of modules that 42 | provide support for targeting specific platforms: 43 | 44 | * "Development.Shake.Language.C.Target.Android" 45 | * "Development.Shake.Language.C.Target.Linux" 46 | * "Development.Shake.Language.C.Target.NaCl" 47 | * "Development.Shake.Language.C.Target.OSX" 48 | * "Development.Shake.Language.C.Target.Windows" 49 | -} 50 | -------------------------------------------------------------------------------- /src/Development/Shake/Language/C/BuildFlags.hs: -------------------------------------------------------------------------------- 1 | -- Copyright 2012-2014 Samplecount S.L. 2 | -- 3 | -- Licensed under the Apache License, Version 2.0 (the "License"); 4 | -- you may not use this file except in compliance with the License. 5 | -- You may obtain a copy of the License at 6 | -- 7 | -- http://www.apache.org/licenses/LICENSE-2.0 8 | -- 9 | -- Unless required by applicable law or agreed to in writing, software 10 | -- distributed under the License is distributed on an "AS IS" BASIS, 11 | -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | -- See the License for the specific language governing permissions and 13 | -- limitations under the License. 14 | 15 | {-# LANGUAGE TemplateHaskell #-} 16 | 17 | {-| 18 | Description: Build flags record for building @C@ language projects 19 | 20 | The `BuildFlags` record is an abstraction for various toolchain flags for 21 | building executables and libraries from source files in a @C@-based language. 22 | It's intended to be toolchain-independent, but currently there's a 23 | bias towards binutils\/gcc/clang toolchains. 24 | -} 25 | 26 | module Development.Shake.Language.C.BuildFlags ( 27 | -- * Source Language 28 | Language(..) 29 | -- * Build flags 30 | , BuildFlags 31 | -- Poor man's documentation for TH generated functions. 32 | , systemIncludes -- | System include directories, referenced by @#include \<...\>@ in code and usually passed to the compiler with the @-isystem@ flag. 33 | , userIncludes -- | User include directories, referenced by @#include "..."@ in code and usually passed to the compiler with the @-I@ flag. 34 | , defines -- | Preprocessor defines, a list of pairs of names with or without a value. 35 | , preprocessorFlags -- | Other preprocessor flags. 36 | , compilerFlags -- | Compiler flags, either generic ones or for a specific source 'Language'. 37 | , libraryPath -- | Linker search path for libraries. 38 | , libraries -- | List of libraries to link against. Note that you should use the library name without the @lib@ prefix and without extension. 39 | , linkerFlags -- | Flags passed to the linker. 40 | , localLibraries -- | Locally built static libraries to be linked against. See also the corresponding section in the . 41 | , archiverFlags -- | Flags passed to the object archiver. 42 | -- ** Utilities for toolchain writers 43 | , defineFlags 44 | , compilerFlagsFor 45 | -- ** Working with config files 46 | , fromConfig 47 | -- * Utilities 48 | , (>>>=) 49 | , append 50 | , prepend 51 | ) where 52 | 53 | import Control.Category ((>>>)) 54 | import Control.Monad 55 | import Data.Char (isSpace) 56 | import Data.Default.Class (Default(..)) 57 | import Data.List 58 | import Data.List.Split 59 | import Data.Maybe 60 | import Data.Semigroup 61 | import Development.Shake.Language.C.Language (Language(..)) 62 | import Development.Shake.Language.C.Label 63 | import Development.Shake.Language.C.Util 64 | 65 | {-| Record type for abstracting various toolchain command line flags. 66 | 67 | `BuildFlags` is an instance of `Default`, you can create a default record with 68 | `def`. `BuildFlags` is also an instance `Monoid`, you can create an empty record with 69 | `mempty` and append flags with `mappend`. `def` and `mempty` are synonyms: 70 | 71 | >>> (def :: BuildFlags) == (mempty :: BuildFlags) 72 | True 73 | 74 | Record accessors are `Data.Label.Mono.Lens`es from the 75 | package, which 76 | makes accessing and modifying record fields a bit more convenient. 77 | @fclabels@ was chosen over 78 | because it has far fewer dependencies, which is convenient when installing 79 | the Shake build system in a per-project cabal sandbox. We might switch to 80 | @lens@ when it gets included in the Haskell platform. 81 | 82 | There are two convenience functions for working with `BuildFlags` record fields 83 | containing lists of flags, `append` and `prepend`. Since most combinators in 84 | this library expect a function @BuildFlags -> BuildFlags@, the following is a 85 | common idiom: 86 | 87 | @ 88 | buildFlags . append `systemIncludes` ["path"] 89 | @ 90 | 91 | Note that when modifying the same record field, order of function composition 92 | matters and you might want to use the arrow combinator '>>>' for appending in 93 | source statement order: 94 | 95 | >>> :{ 96 | get systemIncludes 97 | $ append systemIncludes ["path1"] . append systemIncludes ["path2"] 98 | $ mempty 99 | :} 100 | ["path2","path1"] 101 | 102 | >>> :{ 103 | get systemIncludes 104 | $ append systemIncludes ["path1"] >>> append systemIncludes ["path2"] 105 | $ mempty 106 | :} 107 | ["path1","path2"] 108 | 109 | See "Development.Shake.Language.C.Rules" for how to use 'BuildFlags' in build 110 | product rules. 111 | -} 112 | data BuildFlags = BuildFlags { 113 | _systemIncludes :: [FilePath] 114 | , _userIncludes :: [FilePath] 115 | , _defines :: [(String, Maybe String)] 116 | , _preprocessorFlags :: [String] 117 | , _compilerFlags :: [(Maybe Language, [String])] 118 | , _libraryPath :: [FilePath] 119 | , _libraries :: [String] 120 | , _linkerFlags :: [String] 121 | -- This is needed for linking against local libraries built by shake (the linker `needs' its inputs). 122 | , _localLibraries :: [FilePath] 123 | , _archiverFlags :: [String] 124 | } deriving (Eq, Show) 125 | 126 | mkLabel ''BuildFlags 127 | 128 | defaultBuildFlags :: BuildFlags 129 | defaultBuildFlags = 130 | BuildFlags { 131 | _systemIncludes = [] 132 | , _userIncludes = [] 133 | , _defines = [] 134 | , _preprocessorFlags = [] 135 | , _compilerFlags = [] 136 | , _libraryPath = [] 137 | , _libraries = [] 138 | , _linkerFlags = [] 139 | , _localLibraries = [] 140 | , _archiverFlags = [] 141 | } 142 | 143 | instance Default BuildFlags where 144 | def = defaultBuildFlags 145 | 146 | instance Semigroup BuildFlags where 147 | a <> b = 148 | append systemIncludes (get systemIncludes a) 149 | . append userIncludes (get userIncludes a) 150 | . append defines (get defines a) 151 | . append preprocessorFlags (get preprocessorFlags a) 152 | . append compilerFlags (get compilerFlags a) 153 | . append libraryPath (get libraryPath a) 154 | . append libraries (get libraries a) 155 | . append linkerFlags (get linkerFlags a) 156 | . append localLibraries (get localLibraries a) 157 | . append archiverFlags (get archiverFlags a) 158 | $ b 159 | 160 | instance Monoid BuildFlags where 161 | mempty = defaultBuildFlags 162 | mappend = (<>) 163 | 164 | -- | Construct preprocessor flags from the 'defines' field of 'BuildFlags'. 165 | defineFlags :: BuildFlags -> [String] 166 | defineFlags = concatMapFlag "-D" 167 | . map (\(a, b) -> maybe a (\b' -> a++"="++b') b) 168 | . get defines 169 | 170 | -- | Return a list of compiler flags for a specific source language. 171 | compilerFlagsFor :: Maybe Language -> BuildFlags -> [String] 172 | compilerFlagsFor lang = concat 173 | . maybe (map snd . filter (isNothing.fst)) 174 | (mapMaybe . f) lang 175 | . get compilerFlags 176 | where f _ (Nothing, x) = Just x 177 | f l (Just l', x) | l == l' = Just x 178 | | otherwise = Nothing 179 | 180 | -- | Construct a 'BuildFlags' modifier function from a config file. 181 | -- 182 | -- See also "Development.Shake.Language.C.Config". 183 | fromConfig :: (Functor m, Monad m) => (String -> m (Maybe String)) -> m (BuildFlags -> BuildFlags) 184 | fromConfig getConfig = do 185 | let parseConfig parser = fmap (maybe [] parser) . getConfig . ("BuildFlags."++) 186 | 187 | config_systemIncludes <- parseConfig paths "systemIncludes" 188 | config_userIncludes <- parseConfig paths "userIncludes" 189 | config_defines <- parseConfig defines' "defines" 190 | config_preprocessorFlags <- parseConfig flags "preprocessorFlags" 191 | config_compilerFlags <- parseConfig ((:[]) . ((,)Nothing) . flags) "compilerFlags" 192 | config_compilerFlags_c <- parseConfig ((:[]) . ((,)(Just C)) . flags) "compilerFlags.c" 193 | config_compilerFlags_cxx <- parseConfig ((:[]) . ((,)(Just Cpp)) . flags) "compilerFlags.cxx" 194 | config_libraryPath <- parseConfig paths "libraryPath" 195 | config_libraries <- parseConfig flags "libraries" 196 | config_linkerFlags <- parseConfig flags "linkerFlags" 197 | config_localLibraries <- parseConfig paths "localLibraries" 198 | config_archiverFlags <- parseConfig flags "archiverFlags" 199 | 200 | return $ append systemIncludes config_systemIncludes 201 | . append userIncludes config_userIncludes 202 | . append defines config_defines 203 | . append preprocessorFlags config_preprocessorFlags 204 | . append compilerFlags (config_compilerFlags ++ config_compilerFlags_c ++ config_compilerFlags_cxx) 205 | . append libraryPath config_libraryPath 206 | . append libraries config_libraries 207 | . append linkerFlags config_linkerFlags 208 | . append localLibraries config_localLibraries 209 | . append archiverFlags config_archiverFlags 210 | where 211 | flags = words' . dropWhile isSpace 212 | paths = words' . dropWhile isSpace 213 | define [] = error "Empty preprocessor definition" 214 | define [k] = (k, Nothing) 215 | define [k,v] = (k, Just v) 216 | define (k:vs) = (k, Just (intercalate "=" vs)) 217 | defines' = map (define . splitOn "=") . flags 218 | 219 | -- | Utility function for composing functions in a monad. 220 | (>>>=) :: Monad m => m (a -> b) -> m (b -> c) -> m (a -> c) 221 | (>>>=) = liftM2 (>>>) 222 | -------------------------------------------------------------------------------- /src/Development/Shake/Language/C/Config.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE DeriveDataTypeable #-} 2 | {-# LANGUAGE GeneralizedNewtypeDeriving #-} 3 | {-# LANGUAGE TypeFamilies #-} 4 | 5 | {-| 6 | Description: Read values from configuration files 7 | 8 | This module provides utilities for reading values from configuration files, 9 | similar to the functions provided by "Development.Shake.Config". 10 | -} 11 | module Development.Shake.Language.C.Config( 12 | withConfig 13 | , mkConfig 14 | , parsePaths 15 | , getPaths 16 | ) where 17 | 18 | import qualified Data.HashMap.Strict as Map 19 | import Development.Shake 20 | import Development.Shake.Classes 21 | import Development.Shake.Config (readConfigFileWithEnv) 22 | import Development.Shake.Language.C.Util (words') 23 | 24 | newtype Config = Config ([FilePath], FilePath, [(String, String)], String) deriving (Show,Typeable,Eq,Hashable,Binary,NFData) 25 | 26 | type instance RuleResult Config = Maybe String 27 | 28 | {- | Given a list of dependencies, return a function that takes an environment 29 | of variable bindings, a configuration file path and a configuration variable 30 | and returns the corresponding configuration value. 31 | 32 | This function is more flexible than `Development.Shake.Config.usingConfigFile`. 33 | It allows the use of multiple configuration files as well as specifying default 34 | variable bindings. 35 | 36 | Typical usage would be something like this: 37 | 38 | > -- In the Rules monad 39 | > getConfig <- withConfig [] 40 | > -- Then in an Action 41 | > ... value <- getConfig [("variable", "default value")] "config.cfg" "variable" 42 | -} 43 | withConfig :: [FilePath] 44 | -> Rules ( [(String,String)] 45 | -> FilePath 46 | -> String 47 | -> Action (Maybe String)) 48 | withConfig deps = do 49 | fileCache <- newCache $ \(file, env) -> do 50 | need deps 51 | liftIO $ readConfigFileWithEnv env file 52 | query <- addOracle $ \(Config (_, file, env, key)) -> Map.lookup key <$> fileCache (file, env) 53 | return $ \env file key -> query (Config ([], file, env, key)) 54 | 55 | {- | Return a function that takes a list of dependencies, a configuration file 56 | path, an environment of variable bindings and a configuration variable and 57 | returns the corresponding configuration value. 58 | 59 | This function is more flexible than `Development.Shake.Config.usingConfigFile`. 60 | It allows the use of multiple configuration files as well as specifying default 61 | variable bindings. 62 | 63 | Typical usage would be something like this: 64 | 65 | > -- In the Rules monad 66 | > getConfig <- mkConfig 67 | > -- Then in an Action 68 | > ... value <- getConfig ["some_generated_config.cfg"] [("variable", "default value")] "config.cfg" "variable" 69 | -} 70 | mkConfig :: Rules ( [FilePath] 71 | -> FilePath 72 | -> [(String,String)] 73 | -> String 74 | -> Action (Maybe String)) 75 | mkConfig = do 76 | fileCache <- newCache $ \(deps, file, env) -> do 77 | need deps 78 | liftIO $ readConfigFileWithEnv env file 79 | query <- addOracle $ \(Config (deps, file, env, key)) -> Map.lookup key <$> fileCache (deps, file, env) 80 | return $ \deps file env key -> query (Config (deps, file, env, key)) 81 | 82 | -- | Parse a list of space separated paths from an input string. Spaces can be escaped by @\\@ characters. 83 | -- 84 | -- >>> parsePaths "/a /a/b /a/b/c\\ d" 85 | -- ["/a","/a/b","/a/b/c d"] 86 | parsePaths :: String -> [FilePath] 87 | parsePaths = words' 88 | 89 | -- | 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. 90 | getPaths :: 91 | (String -> Action (Maybe String)) -- ^ Configuration lookup function 92 | -> [String] -- ^ Configuration keys 93 | -> Action [FilePath] -- ^ File paths 94 | getPaths getConfig keys = do 95 | sources <- mapM (fmap (fmap parsePaths) . getConfig) keys 96 | return $ concatMap (maybe [] id) sources 97 | -------------------------------------------------------------------------------- /src/Development/Shake/Language/C/Host.hs: -------------------------------------------------------------------------------- 1 | -- Copyright 2012-2013 Samplecount S.L. 2 | -- 3 | -- Licensed under the Apache License, Version 2.0 (the "License"); 4 | -- you may not use this file except in compliance with the License. 5 | -- You may obtain a copy of the License at 6 | -- 7 | -- http://www.apache.org/licenses/LICENSE-2.0 8 | -- 9 | -- Unless required by applicable law or agreed to in writing, software 10 | -- distributed under the License is distributed on an "AS IS" BASIS, 11 | -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | -- See the License for the specific language governing permissions and 13 | -- limitations under the License. 14 | 15 | {-| 16 | Description: Toolchain definitions and utilities for host platform 17 | -} 18 | module Development.Shake.Language.C.Host ( 19 | OS(..) 20 | , os 21 | , executableExtension 22 | , sharedLibraryExtension 23 | , loadableLibraryExtension 24 | , defaultToolChain 25 | ) where 26 | 27 | import Development.Shake (Action) 28 | import Development.Shake.Language.C.Target (Target) 29 | import qualified Development.Shake.Language.C.Host.Linux as Linux 30 | import qualified Development.Shake.Language.C.Host.OSX as OSX 31 | import qualified Development.Shake.Language.C.Host.Windows as Windows 32 | import Development.Shake.Language.C.ToolChain (ToolChain) 33 | import qualified System.Info as System 34 | import System.IO.Unsafe (unsafePerformIO) 35 | 36 | -- | Host operating system. 37 | data OS = 38 | Linux 39 | | OSX 40 | | Windows 41 | deriving (Eq, Ord, Show) 42 | 43 | -- | This host's operating system. 44 | os :: OS 45 | os = 46 | case System.os of 47 | "darwin" -> OSX 48 | "mingw32" -> Windows 49 | "linux" -> Linux 50 | _ -> error $ "Unknown host operating system: " ++ System.os 51 | 52 | -- | File extension for executables. 53 | executableExtension :: String 54 | executableExtension = 55 | case os of 56 | Windows -> "exe" 57 | _ -> "" 58 | 59 | -- | File extension for dynamic shared libraries. 60 | sharedLibraryExtension :: String 61 | sharedLibraryExtension = 62 | case os of 63 | Linux -> "so" 64 | OSX -> "dylib" 65 | Windows -> "dll" 66 | 67 | -- | File extension for dynamic loadable libraries. 68 | loadableLibraryExtension :: String 69 | loadableLibraryExtension = 70 | case os of 71 | Linux -> "so" 72 | OSX -> "bundle" 73 | Windows -> "dll" 74 | 75 | -- | Get host's default toolchain. 76 | -- 77 | -- This function can be used for targeting the host operating system, see also "Development.Shake.Language.C.Rules". 78 | defaultToolChain :: (Target, Action ToolChain) 79 | {-# NOINLINE defaultToolChain #-} 80 | defaultToolChain = unsafePerformIO $ do 81 | -- The assumption here is that target and toolchain don't change while the program is running. 82 | case os of 83 | Linux -> Linux.getHostToolChain 84 | OSX -> OSX.getHostToolChain 85 | Windows -> Windows.getHostToolChain 86 | -- _ -> error $ "No default toolchain for this operating system (" ++ show os ++ ")" 87 | -------------------------------------------------------------------------------- /src/Development/Shake/Language/C/Host/Linux.hs: -------------------------------------------------------------------------------- 1 | -- Copyright 2012-2014 Samplecount S.L. 2 | -- 3 | -- Licensed under the Apache License, Version 2.0 (the "License"); 4 | -- you may not use this file except in compliance with the License. 5 | -- You may obtain a copy of the License at 6 | -- 7 | -- http://www.apache.org/licenses/LICENSE-2.0 8 | -- 9 | -- Unless required by applicable law or agreed to in writing, software 10 | -- distributed under the License is distributed on an "AS IS" BASIS, 11 | -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | -- See the License for the specific language governing permissions and 13 | -- limitations under the License. 14 | 15 | module Development.Shake.Language.C.Host.Linux ( 16 | getHostToolChain 17 | ) where 18 | 19 | import Development.Shake 20 | import Development.Shake.Language.C.Target 21 | import Development.Shake.Language.C.Target.Linux 22 | import Development.Shake.Language.C.ToolChain 23 | import System.Process (readProcess) 24 | 25 | -- | Get host architecture. 26 | getHostArch :: IO Arch 27 | getHostArch = do 28 | arch <- fmap (head.lines) $ readProcess "uname" ["-m"] "" 29 | return $ case arch of 30 | "i386" -> X86 I386 31 | "i686" -> X86 I686 32 | "x86_64" -> X86 X86_64 33 | "armv7l" -> Arm Armv7 34 | _ -> error $ "Unknown host architecture " ++ arch 35 | 36 | -- | Get host toolchain. 37 | getHostToolChain :: IO (Target, Action ToolChain) 38 | getHostToolChain = do 39 | t <- fmap target getHostArch 40 | return (t, return $ toolChain GCC) 41 | -------------------------------------------------------------------------------- /src/Development/Shake/Language/C/Host/OSX.hs: -------------------------------------------------------------------------------- 1 | -- Copyright 2012-2014 Samplecount S.L. 2 | -- 3 | -- Licensed under the Apache License, Version 2.0 (the "License"); 4 | -- you may not use this file except in compliance with the License. 5 | -- You may obtain a copy of the License at 6 | -- 7 | -- http://www.apache.org/licenses/LICENSE-2.0 8 | -- 9 | -- Unless required by applicable law or agreed to in writing, software 10 | -- distributed under the License is distributed on an "AS IS" BASIS, 11 | -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | -- See the License for the specific language governing permissions and 13 | -- limitations under the License. 14 | 15 | module Development.Shake.Language.C.Host.OSX ( 16 | getHostToolChain 17 | ) where 18 | 19 | import Development.Shake 20 | import Development.Shake.Language.C.Target 21 | import Development.Shake.Language.C.Target.OSX 22 | import Development.Shake.Language.C.ToolChain 23 | 24 | -- | Get host toolchain. 25 | getHostToolChain :: IO (Target, Action ToolChain) 26 | getHostToolChain = do 27 | let defaultTarget = target macOSX (X86 X86_64) 28 | return ( defaultTarget 29 | , toolChain 30 | <$> getSDKRoot 31 | <*> (maximum <$> getPlatformVersions (targetPlatform defaultTarget)) 32 | <*> pure defaultTarget ) 33 | -------------------------------------------------------------------------------- /src/Development/Shake/Language/C/Host/Windows.hs: -------------------------------------------------------------------------------- 1 | -- Copyright 2012-2016 Samplecount S.L. 2 | -- 3 | -- Licensed under the Apache License, Version 2.0 (the "License"); 4 | -- you may not use this file except in compliance with the License. 5 | -- You may obtain a copy of the License at 6 | -- 7 | -- http://www.apache.org/licenses/LICENSE-2.0 8 | -- 9 | -- Unless required by applicable law or agreed to in writing, software 10 | -- distributed under the License is distributed on an "AS IS" BASIS, 11 | -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | -- See the License for the specific language governing permissions and 13 | -- limitations under the License. 14 | 15 | module Development.Shake.Language.C.Host.Windows ( 16 | getHostToolChain 17 | ) where 18 | 19 | import Data.Char (isSpace, toLower) 20 | import Development.Shake 21 | import Development.Shake.Language.C.Target 22 | import Development.Shake.Language.C.Target.Windows 23 | import Development.Shake.Language.C.ToolChain 24 | 25 | trim :: String -> String 26 | trim = reverse . dropWhile isSpace . reverse 27 | 28 | getHostArch :: IO Arch 29 | getHostArch = do 30 | Stdout out <- cmd "wmic os get osarchitecture" 31 | let spec = map trim . lines $ out 32 | case map (map toLower) spec of 33 | ("osarchitecture":"32-bit":_) -> return $ X86 I686 34 | ("osarchitecture":"64-bit":_) -> return $ X86 X86_64 35 | ("osarchitecture":arch:_) -> error $ "Unknown host architecture " ++ arch 36 | _ -> error $ "Couldn't determine host architecture from " ++ show spec 37 | 38 | getHostToolChain :: IO (Target, Action ToolChain) 39 | getHostToolChain = do 40 | t <- fmap target getHostArch 41 | return (t, return $ toolChain Generic) 42 | -------------------------------------------------------------------------------- /src/Development/Shake/Language/C/Label.hs: -------------------------------------------------------------------------------- 1 | -- Copyright 2012-2013 Samplecount S.L. 2 | -- 3 | -- Licensed under the Apache License, Version 2.0 (the "License"); 4 | -- you may not use this file except in compliance with the License. 5 | -- You may obtain a copy of the License at 6 | -- 7 | -- http://www.apache.org/licenses/LICENSE-2.0 8 | -- 9 | -- Unless required by applicable law or agreed to in writing, software 10 | -- distributed under the License is distributed on an "AS IS" BASIS, 11 | -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | -- See the License for the specific language governing permissions and 13 | -- limitations under the License. 14 | 15 | {-# LANGUAGE TypeOperators #-} 16 | 17 | {-| 18 | Description: Lens utilities 19 | 20 | This module provides some lens utilities and also re-exports "Data.Label", 21 | which can avoid a package dependency on @fclabels@ in some cases. 22 | -} 23 | module Development.Shake.Language.C.Label ( 24 | module Data.Label 25 | , append 26 | , prepend 27 | ) where 28 | 29 | import Data.Label 30 | import Data.Monoid (Monoid, mappend) 31 | 32 | -- | Append stuff to the 'Monoid' in record field specified by lens. 33 | append :: Monoid a => 34 | (f :-> a) -- ^ lens 35 | -> a -- ^ stuff to append 36 | -> f -- ^ original record 37 | -> f -- ^ modified record 38 | append l n = modify l (`mappend` n) 39 | 40 | -- | Prepend stuff to the 'Monoid' in record field specified by lens. 41 | prepend :: Monoid a => 42 | (f :-> a) -- ^ lens 43 | -> a -- ^ stuff to prepend 44 | -> f -- ^ original record 45 | -> f -- ^ modified record 46 | prepend l n = modify l (n `mappend`) 47 | -------------------------------------------------------------------------------- /src/Development/Shake/Language/C/Language.hs: -------------------------------------------------------------------------------- 1 | -- Copyright 2012-2014 Samplecount S.L. 2 | -- 3 | -- Licensed under the Apache License, Version 2.0 (the "License"); 4 | -- you may not use this file except in compliance with the License. 5 | -- You may obtain a copy of the License at 6 | -- 7 | -- http://www.apache.org/licenses/LICENSE-2.0 8 | -- 9 | -- Unless required by applicable law or agreed to in writing, software 10 | -- distributed under the License is distributed on an "AS IS" BASIS, 11 | -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | -- See the License for the specific language governing permissions and 13 | -- limitations under the License. 14 | 15 | {-# LANGUAGE TemplateHaskell #-} 16 | 17 | module Development.Shake.Language.C.Language ( 18 | Language(..) 19 | , defaultLanguageMap 20 | , languageOf 21 | ) where 22 | 23 | import Development.Shake.FilePath (takeExtension) 24 | 25 | -- | Source language. 26 | -- 27 | -- Currently something derived from @C@. 28 | data Language = 29 | C -- ^ Plain old C 30 | | Cpp -- ^ C++ 31 | | ObjC -- ^ Objective-C 32 | | ObjCpp -- ^ Objective-C with C++ (Apple extension) 33 | | Asm -- ^ Assembly 34 | deriving (Enum, Eq, Show) 35 | 36 | -- | Default mapping from file extension to source language. 37 | defaultLanguageMap :: [(String, Language)] 38 | defaultLanguageMap = concatMap f [ 39 | (C, [".c"]) 40 | , (Cpp, [".cc", ".CC", ".cpp", ".CPP", ".C", ".cxx", ".CXX"]) 41 | , (ObjC, [".m"]) 42 | , (ObjCpp, [".mm", ".M"]) 43 | , (Asm, [".s", ".S"]) 44 | ] 45 | where f (lang, exts) = map (\ext -> (ext, lang)) exts 46 | 47 | -- | Determine the source language of a file based on its extension. 48 | languageOf :: FilePath -> Maybe Language 49 | languageOf = flip lookup defaultLanguageMap . takeExtension 50 | -------------------------------------------------------------------------------- /src/Development/Shake/Language/C/PkgConfig.hs: -------------------------------------------------------------------------------- 1 | -- Copyright 2012-2013 Samplecount S.L. 2 | -- 3 | -- Licensed under the Apache License, Version 2.0 (the "License"); 4 | -- you may not use this file except in compliance with the License. 5 | -- You may obtain a copy of the License at 6 | -- 7 | -- http://www.apache.org/licenses/LICENSE-2.0 8 | -- 9 | -- Unless required by applicable law or agreed to in writing, software 10 | -- distributed under the License is distributed on an "AS IS" BASIS, 11 | -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | -- See the License for the specific language governing permissions and 13 | -- limitations under the License. 14 | 15 | {-| 16 | Description: Query build flags with @pkg-config@ 17 | 18 | This module provides utilities for querying 'BuildFlags' from the 19 | database, 20 | which is available on many Unix like operating systems. 21 | -} 22 | module Development.Shake.Language.C.PkgConfig ( 23 | Options(..) 24 | , defaultOptions 25 | , pkgConfig 26 | , fromConfig 27 | ) where 28 | 29 | import Data.Char (toLower) 30 | import Data.Default.Class (Default(..)) 31 | import Data.List (intercalate, isPrefixOf) 32 | import Development.Shake 33 | import Development.Shake.FilePath 34 | import Development.Shake.Language.C ( BuildFlags 35 | , compilerFlags 36 | , libraries 37 | , libraryPath 38 | , linkerFlags 39 | , systemIncludes 40 | , userIncludes ) 41 | import Development.Shake.Language.C.Label (append) 42 | import Development.Shake.Language.C.Util (words') 43 | 44 | -- ==================================================================== 45 | -- PkgConfig 46 | 47 | -- TODO: 48 | -- * Use parsec or attoparsec for more robust parser 49 | -- * Parse preprocessor defines 50 | -- * Parse framework path (-F) and -framework flags 51 | 52 | parseCflags :: [String] -> (BuildFlags -> BuildFlags) 53 | parseCflags [] = id 54 | parseCflags (x:xs) 55 | | isPrefixOf "-I" x = parseCflags xs . append systemIncludes [drop 2 x] 56 | | isPrefixOf "-i" x = parseCflags xs . append userIncludes [drop 2 x] 57 | | otherwise = parseCflags xs . append compilerFlags [(Nothing, [x])] 58 | 59 | parseLibs :: [String] -> (BuildFlags -> BuildFlags) 60 | parseLibs [] = id 61 | parseLibs (x:xs) 62 | | isPrefixOf "-l" x = parseLibs xs . append libraries [drop 2 x] 63 | | isPrefixOf "-L" x = parseLibs xs . append libraryPath [drop 2 x] 64 | | otherwise = parseLibs xs . append linkerFlags [x] 65 | 66 | parseFlags :: String -> [String] 67 | parseFlags = words' . head . lines 68 | 69 | -- | PkgConfig options. 70 | data Options = Options { 71 | searchPath :: Maybe [FilePath] -- ^ List of directories where @.pc@ files are searched, corresponding to the @PKG_CONFIG_PATH@ environment variable 72 | , static :: Bool -- ^ Return flags appropriate for static linking 73 | } deriving (Eq, Show) 74 | 75 | -- | Default @pkg-config@ options. 76 | -- 77 | -- This function is an alias for `def`. 78 | defaultOptions :: Options 79 | defaultOptions = Options{ 80 | searchPath = Nothing 81 | , static = False 82 | } 83 | 84 | instance Default Options where 85 | def = defaultOptions 86 | 87 | -- | Call @pkg-config@ with options and a package name and return a 'BuildFlags' modification function. 88 | -- 89 | -- The @pkg-config@ executable must be installed on the build host. 90 | pkgConfig :: Options -> String -> Action (BuildFlags -> BuildFlags) 91 | pkgConfig options pkg = do 92 | env <- case searchPath options of 93 | Nothing -> return [] 94 | Just path -> do 95 | env <- addEnv [("PKG_CONFIG_PATH", intercalate [searchPathSeparator] path)] 96 | return [env] 97 | let flags = if static options then ["--static"] else [] 98 | pkgconfig which = command ([Traced ""] ++ env) "pkg-config" (flags ++ ["--" ++ which, pkg]) 99 | Stdout cflags <- pkgconfig "cflags" 100 | Stdout libs <- pkgconfig "libs" 101 | return ( parseCflags (parseFlags cflags) 102 | . parseLibs (parseFlags libs) ) 103 | 104 | -- | 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. 105 | -- 106 | -- The following configuration variables are recognised: 107 | -- 108 | -- [@PkgConfig.packages@] List of package names for which build flags should be queried 109 | -- [@PkgConfig.options.searchPath@] Space-separated list of file paths, corresponds to the `searchPath` option 110 | -- [@PkgConfig.options.static@] @true@ or @false@, corresponds to the `static` option 111 | fromConfig :: Options -> (String -> Action (Maybe String)) -> Action (BuildFlags -> BuildFlags) 112 | fromConfig initialOptions cfg = do 113 | config_searchPath <- fmap words' <$> cfg "PkgConfig.options.searchPath" 114 | config_static <- fmap (bool . words) <$> cfg "PkgConfig.options.static" 115 | config_packages <- fmap words <$> cfg "PkgConfig.packages" 116 | let options = initialOptions { 117 | searchPath = maybe (searchPath initialOptions) Just config_searchPath, 118 | static = maybe (static initialOptions) id config_static 119 | } 120 | flags <- mapM (pkgConfig options) (maybe [] id config_packages) 121 | return $ foldl (.) id $ flags 122 | where 123 | bool (x:_) = map toLower x == "true" 124 | bool _ = False 125 | -------------------------------------------------------------------------------- /src/Development/Shake/Language/C/Rules.hs: -------------------------------------------------------------------------------- 1 | -- Copyright 2012-2014 Samplecount S.L. 2 | -- 3 | -- Licensed under the Apache License, Version 2.0 (the "License"); 4 | -- you may not use this file except in compliance with the License. 5 | -- You may obtain a copy of the License at 6 | -- 7 | -- http://www.apache.org/licenses/LICENSE-2.0 8 | -- 9 | -- Unless required by applicable law or agreed to in writing, software 10 | -- distributed under the License is distributed on an "AS IS" BASIS, 11 | -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | -- See the License for the specific language governing permissions and 13 | -- limitations under the License. 14 | 15 | {-| 16 | Description: High-level Shake rules 17 | 18 | This module provides a few high-level rules for building executables and 19 | libraries. Below is an example that builds both a static library and an executable. See 20 | "Development.Shake.Language.C.ToolChain" for examples of toolchain definitions. 21 | 22 | > let toolChain = ... 23 | > lib <- staticLibrary toolChain "libexample.a" (pure mempty) (pure ["example_lib.c"]) 24 | > exe <- staticLibrary toolChain ("example" <.> exe) (pure mempty) (pure ["example_exe.c"]) 25 | > want [lib, exe] 26 | 27 | Sometimes you want to structure your project in a set of static libraries that 28 | are later linked into one or more executables. For Shake to recognise the 29 | libraries as dependencies of the executable you need to add them to the 30 | `localLibraries` field of the `BuildFlags` record: 31 | 32 | > let toolChain = ... 33 | > buildFlags = ... 34 | > lib <- staticLibrary toolChain "libexample.a" 35 | > (pure buildFlags) 36 | > (pure ["example_lib.c"]) 37 | > exe <- executable toolChain ("example" <.> exe) 38 | > (pure $ buildFlags . append localLibraries [lib]) 39 | > (pure ["example_exe.c"]) 40 | > want [exe] 41 | 42 | The rule functions expect their arguments in the 'Action' monad in order to be 43 | able to derive them from side-effecting configuration actions. For example it 44 | can be useful to determine certain toolchain settings either from the 45 | environment, or from configuration files. Using "Control.Applicative" we could 46 | write: 47 | 48 | > Android.toolChain 49 | > <$> getEnvWithDefault 50 | > (error "ANDROID_NDK is undefined") 51 | > "ANDROID_NDK" 52 | > <*> pure (Android.sdkVersion 9) 53 | > <*> pure (LLVM, Version [3,4] []) 54 | > <*> pure (Android.target (Arm Armv7)) 55 | -} 56 | 57 | module Development.Shake.Language.C.Rules ( 58 | executable 59 | , staticLibrary 60 | , sharedLibrary 61 | , loadableLibrary 62 | ) where 63 | 64 | import Data.Monoid (mempty) 65 | import Development.Shake 66 | import Development.Shake.FilePath 67 | import Development.Shake.Language.C.BuildFlags as BuildFlags 68 | import Development.Shake.Language.C.ToolChain as ToolChain 69 | import Development.Shake.Language.C.Label (get) 70 | 71 | mkObjectsDir :: FilePath -> FilePath 72 | mkObjectsDir path = takeDirectory path map tr (takeFileName path) ++ "_obj" 73 | where tr '.' = '_' 74 | tr x = x 75 | 76 | buildProduct :: (ToolChain -> Linker) 77 | -> Action ToolChain 78 | -> FilePath 79 | -> Action (BuildFlags -> BuildFlags) 80 | -> Action [FilePath] 81 | -> Rules FilePath 82 | buildProduct getLinker getToolChain result getBuildFlags getSources = do 83 | let objectsDir = mkObjectsDir result 84 | cachedObjects <- newCache $ \() -> do 85 | sources <- getSources 86 | return $ [ objectsDir makeRelative "/" (src <.> if isAbsolute src then "abs.o" else "rel.o") 87 | | src <- sources ] 88 | cachedToolChain <- newCache $ \() -> getToolChain 89 | cachedBuildFlags <- newCache $ \() -> do 90 | tc <- cachedToolChain () 91 | f1 <- get ToolChain.defaultBuildFlags tc 92 | f2 <- getBuildFlags 93 | return $ f2 . f1 $ mempty 94 | result %> \_ -> do 95 | tc <- cachedToolChain () 96 | flags <- cachedBuildFlags () 97 | objs <- cachedObjects () 98 | need objs 99 | (getLinker tc) 100 | tc 101 | flags 102 | objs 103 | result 104 | (dropTrailingPathSeparator objectsDir ++ "//*.o") %> \obj -> do 105 | tc <- cachedToolChain () 106 | flags <- cachedBuildFlags () 107 | -- Compute source file name from object file name 108 | -- Using getSources here would result in a dependency of every object file on the list of sources, leading to unnecessary rebuilds. 109 | let src = case splitExtension $ dropExtension $ makeRelative objectsDir obj of 110 | (x, ".abs") -> "/" x -- Source file had absolute path 111 | (x, ".rel") -> x 112 | (_, ext) -> error $ "BUG: Unexpected object file extension " ++ ext 113 | (get compiler tc) 114 | tc 115 | flags 116 | src 117 | obj 118 | return result 119 | 120 | -- TODO: The following result type would be more composable, e.g. allowing for further build product processing: 121 | -- Rules (FilePath -> Action ()) 122 | -- See https://github.com/samplecount/shake-language-c/issues/15 123 | 124 | -- | Shake rule for building an executable. 125 | executable :: Action ToolChain -- ^ Action returning a target 'ToolChain' 126 | -> FilePath -- ^ Output file 127 | -> Action (BuildFlags -> BuildFlags) -- ^ Action returning a 'BuildFlags' modifier 128 | -> Action [FilePath] -- ^ Action returning a list of input source files 129 | -> Rules FilePath -- ^ Rule returning the output file path 130 | executable toolChain = buildProduct (flip (get linker) Executable) toolChain 131 | 132 | -- | Shake rule for building a static library. 133 | staticLibrary :: Action ToolChain -- ^ Action returning a target 'ToolChain' 134 | -> FilePath -- ^ Output file 135 | -> Action (BuildFlags -> BuildFlags) -- ^ Action returning a 'BuildFlags' modifier 136 | -> Action [FilePath] -- ^ Action returning a list of input source files 137 | -> Rules FilePath -- ^ Rule returning the output file path 138 | staticLibrary toolChain = buildProduct (get archiver) toolChain 139 | 140 | -- | Shake rule for building a shared (dynamically linked) library. 141 | sharedLibrary :: Action ToolChain -- ^ Action returning a target 'ToolChain' 142 | -> FilePath -- ^ Output file 143 | -> Action (BuildFlags -> BuildFlags) -- ^ Action returning a 'BuildFlags' modifier 144 | -> Action [FilePath] -- ^ Action returning a list of input source files 145 | -> Rules FilePath -- ^ Rule returning the output file path 146 | sharedLibrary toolChain = buildProduct (flip (get linker) SharedLibrary) toolChain 147 | 148 | -- | Shake rule for building a dynamically loadable library. 149 | loadableLibrary :: Action ToolChain -- ^ Action returning a target 'ToolChain' 150 | -> FilePath -- ^ Output file 151 | -> Action (BuildFlags -> BuildFlags) -- ^ Action returning a 'BuildFlags' modifier 152 | -> Action [FilePath] -- ^ Action returning a list of input source files 153 | -> Rules FilePath -- ^ Rule returning the output file path 154 | loadableLibrary toolChain = buildProduct (flip (get linker) LoadableLibrary) toolChain 155 | -------------------------------------------------------------------------------- /src/Development/Shake/Language/C/Target.hs: -------------------------------------------------------------------------------- 1 | -- Copyright 2012-2014 Samplecount S.L. 2 | -- 3 | -- Licensed under the Apache License, Version 2.0 (the "License"); 4 | -- you may not use this file except in compliance with the License. 5 | -- You may obtain a copy of the License at 6 | -- 7 | -- http://www.apache.org/licenses/LICENSE-2.0 8 | -- 9 | -- Unless required by applicable law or agreed to in writing, software 10 | -- distributed under the License is distributed on an "AS IS" BASIS, 11 | -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | -- See the License for the specific language governing permissions and 13 | -- limitations under the License. 14 | 15 | module Development.Shake.Language.C.Target ( 16 | OS(..) 17 | , Platform(..) 18 | , ArmVersion(..) 19 | , X86Version(..) 20 | , Arch(..) 21 | , Target(..) 22 | , ToBuildPrefix(..) 23 | ) where 24 | 25 | import Data.Char (toLower) 26 | import Development.Shake.FilePath 27 | 28 | -- $setup 29 | -- >>> :load Development.Shake.Language.C.Target.OSX 30 | -- >>> :module -Development.Shake.Language.C.Target.OSX 31 | -- >>> import Development.Shake.Language.C.Target 32 | 33 | -- | Target operating system. 34 | data OS = 35 | Android -- ^ Google Android 36 | | Linux -- ^ GNU Linux 37 | | OSX -- ^ Apple Mac OSX and iOS 38 | | Pepper -- ^ Google Portable Native Client (PNaCl) 39 | | Windows -- ^ Microsoft Windows 40 | | None -- ^ No OS (bare metal) 41 | deriving (Eq, Ord, Show) 42 | 43 | -- | Target platform. 44 | -- 45 | -- Basically just a platform identifier string. Use `toBuildPrefix` to convert a platform to a file path prefix that can be used in Shake rules. 46 | data Platform = Platform { 47 | platformName :: String 48 | } deriving (Eq, Show) 49 | 50 | -- | `X86` architecture version. 51 | data X86Version = 52 | I386 -- ^ @i386@, 32-bit architecture without @SSE@ 53 | | I686 -- ^ @i686@, 32-bit architecture with @SSE@ (/Pentium-Pro/) 54 | | X86_64 -- ^ @x86_64@, 64-bit architecture 55 | deriving (Eq, Show) 56 | 57 | -- | `Arm` architecture version. 58 | data ArmVersion = 59 | Armv5 60 | | Armv6 61 | | Armv7 62 | | Armv7s 63 | | Arm64 64 | deriving (Eq, Show) 65 | 66 | -- | Target architecture. 67 | -- 68 | -- Use `toBuildPrefix` to convert an architecture to a short, more or less canonical file path prefix that can be used in Shake rules. 69 | data Arch = 70 | X86 X86Version -- ^ Intel @x86@ architecture 71 | | Arm ArmVersion -- ^ Arm architecture 72 | | LLVM_IR -- ^ LLVM intermediate representation, used by `Pepper` (PNaCl) 73 | deriving (Eq, Show) 74 | 75 | -- | Compilation target triple consisting of operating system, platform and architecture. 76 | -- 77 | -- Use `toBuildPrefix` to convert a target to a file path prefix that can be used in Shake rules. The prefix is of the form 78 | -- 79 | -- > / 80 | -- 81 | -- For example: 82 | -- 83 | -- >>> import qualified Development.Shake.Language.C.Target.OSX as OSX 84 | -- >>> toBuildPrefix $ OSX.target OSX.iPhoneOS (Arm Armv7) 85 | -- "iphoneos/armv7" 86 | data Target = Target { 87 | targetOS :: OS -- ^ Target operating system 88 | , targetPlatform :: Platform -- ^ Target platform 89 | , targetArch :: Arch -- ^ Target architecture 90 | } deriving (Show) 91 | 92 | -- | Convert a value to a build directory prefix. 93 | -- 94 | -- 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. 95 | class ToBuildPrefix a where 96 | -- | Convert a value to a (unique) build directory prefix. 97 | toBuildPrefix :: a -> FilePath 98 | 99 | instance ToBuildPrefix Platform where 100 | toBuildPrefix = map toLower . platformName 101 | 102 | instance ToBuildPrefix Arch where 103 | toBuildPrefix arch = 104 | case arch of 105 | X86 version -> map toLower (show version) 106 | Arm version -> map toLower (show version) 107 | _ -> map toLower (show arch) 108 | 109 | instance ToBuildPrefix Target where 110 | toBuildPrefix target = 111 | toBuildPrefix (targetPlatform target) 112 | toBuildPrefix (targetArch target) 113 | -------------------------------------------------------------------------------- /src/Development/Shake/Language/C/Target/Android.hs: -------------------------------------------------------------------------------- 1 | -- Copyright 2012-2016 Samplecount S.L. 2 | -- 3 | -- Licensed under the Apache License, Version 2.0 (the "License"); 4 | -- you may not use this file except in compliance with the License. 5 | -- You may obtain a copy of the License at 6 | -- 7 | -- http://www.apache.org/licenses/LICENSE-2.0 8 | -- 9 | -- Unless required by applicable law or agreed to in writing, software 10 | -- distributed under the License is distributed on an "AS IS" BASIS, 11 | -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | -- See the License for the specific language governing permissions and 13 | -- limitations under the License. 14 | 15 | {-| 16 | Description: Toolchain definitions and utilities for Android 17 | 18 | This module provides toolchain definitions and utilities for targeting Android. 19 | See "Development.Shake.Language.C.Rules" for examples of how to use a target 20 | toolchain. 21 | 22 | The minimum required Android NDK revision is 11c. 23 | -} 24 | module Development.Shake.Language.C.Target.Android ( 25 | target 26 | , sdkVersion 27 | , toolChain 28 | , abiString 29 | , gnustl 30 | , libcxx 31 | , native_app_glue 32 | ) where 33 | 34 | import Control.Category ((>>>)) 35 | import Development.Shake.FilePath 36 | import Data.Version (Version(..), showVersion) 37 | import Development.Shake.Language.C.BuildFlags 38 | import Development.Shake.Language.C.Target 39 | import Development.Shake.Language.C.Label 40 | import Development.Shake.Language.C.ToolChain 41 | import qualified System.Info as System 42 | 43 | unsupportedArch :: Arch -> a 44 | unsupportedArch arch = error $ "Unsupported Android target architecture " ++ show arch 45 | 46 | toolChainPrefix :: Target -> String 47 | toolChainPrefix x = 48 | case targetArch x of 49 | X86 _ -> "x86-" 50 | Arm Arm64 -> "aarch64-linux-android-" 51 | Arm _ -> "arm-linux-androideabi-" 52 | arch -> unsupportedArch arch 53 | 54 | toolPrefix_ :: Target -> String 55 | toolPrefix_ x = 56 | case targetArch x of 57 | X86 _ -> "i686-linux-android-" 58 | _ -> toolChainPrefix x 59 | 60 | osPrefix :: String 61 | osPrefix = System.os ++ "-" ++ cpu 62 | where cpu = case System.arch of 63 | "i386" -> "x86" 64 | arch -> arch 65 | 66 | -- | Android target for architecture. 67 | target :: Arch -> Target 68 | target = Target Android (Platform "android") 69 | 70 | mkDefaultBuildFlags :: FilePath -> Version -> Arch -> BuildFlags -> BuildFlags 71 | mkDefaultBuildFlags ndk version arch = 72 | append compilerFlags [(Nothing, [sysroot, march])] 73 | >>> append compilerFlags (archCompilerFlags arch) 74 | >>> append compilerFlags [(Nothing, [ 75 | "-fpic" 76 | , "-ffunction-sections" 77 | , "-funwind-tables" 78 | , "-fstack-protector" 79 | , "-no-canonical-prefixes"])] 80 | >>> append linkerFlags [sysroot, march] 81 | >>> append linkerFlags (archLinkerFlags arch) 82 | >>> append linkerFlags ["-Wl,--no-undefined", "-Wl,-z,relro", "-Wl,-z,now"] 83 | >>> append linkerFlags ["-no-canonical-prefixes"] 84 | >>> append archiverFlags ["crs"] 85 | where 86 | sysroot = "--sysroot=" 87 | ++ ndk 88 | "platforms" 89 | "android-" ++ show (head (versionBranch version)) 90 | "arch-" ++ case arch of 91 | (X86 _) -> "x86" 92 | (Arm Arm64) -> "arm64" 93 | (Arm _) -> "arm" 94 | _ -> unsupportedArch arch 95 | march = "-march=" ++ case arch of 96 | X86 I386 -> "i386" 97 | X86 I686 -> "i686" 98 | X86 X86_64 -> "x86_64" 99 | Arm Armv5 -> "armv5te" 100 | Arm Armv6 -> "armv5te" 101 | Arm Armv7 -> "armv7-a" 102 | Arm Arm64 -> "armv8-a" 103 | _ -> unsupportedArch arch 104 | archCompilerFlags (Arm Armv7) = [(Nothing, ["-mfloat-abi=softfp", "-mfpu=neon"])] 105 | archCompilerFlags (Arm Arm64) = [] 106 | archCompilerFlags (Arm _) = [(Nothing, ["-mtune=xscale", "-msoft-float"])] 107 | archCompilerFlags _ = [] 108 | archLinkerFlags (Arm Armv7) = ["-Wl,--fix-cortex-a8"] 109 | archLinkerFlags _ = [] 110 | 111 | -- | Construct a version record from an integral Android SDK version. 112 | -- 113 | -- prop> sdkVersion 19 == Version [19] [] 114 | sdkVersion :: Int -> Version 115 | sdkVersion n = Version [n] [] 116 | 117 | gccToolChain :: FilePath -> Target -> FilePath 118 | gccToolChain ndk target = 119 | ndk "toolchains" 120 | toolChainPrefix target ++ showVersion (Version [4,9] []) 121 | "prebuilt" 122 | osPrefix 123 | 124 | -- | Construct an Android toolchain. 125 | toolChain :: FilePath -- ^ NDK source directory 126 | -> Version -- ^ SDK version, see `sdkVersion` 127 | -> ToolChainVariant -- ^ Toolchain variant 128 | -> Target -- ^ Build target, see `target` 129 | -> ToolChain -- ^ Resulting toolchain 130 | toolChain "" _ _ _ = error "Empty NDK directory" 131 | toolChain ndk version GCC t = 132 | set variant GCC 133 | $ set toolDirectory (Just (gccToolChain ndk t "bin")) 134 | $ set toolPrefix (toolPrefix_ t) 135 | $ set compilerCommand "gcc" 136 | $ set archiverCommand "ar" 137 | $ set linkerCommand "g++" 138 | $ set defaultBuildFlags (return $ mkDefaultBuildFlags ndk version (targetArch t)) 139 | $ defaultToolChain 140 | toolChain ndk version LLVM t = 141 | set variant LLVM 142 | $ set toolDirectory (Just (ndk "toolchains" 143 | "llvm" 144 | "prebuilt" 145 | osPrefix 146 | "bin")) 147 | $ set compilerCommand "clang" 148 | $ set archiverCommand (gccToolChain ndk t "bin" toolPrefix_ t ++ "ar") 149 | $ set linkerCommand "clang++" 150 | $ set defaultBuildFlags (return $ 151 | let flags = [ "-target", llvmTarget t 152 | , "-gcc-toolchain", gccToolChain ndk t ] 153 | in mkDefaultBuildFlags ndk version (targetArch t) 154 | . append compilerFlags [(Nothing, flags)] 155 | . append linkerFlags flags 156 | ) 157 | $ defaultToolChain 158 | where 159 | llvmTarget x = 160 | case targetArch x of 161 | Arm Armv5 -> "armv5te-none-linux-androideabi" 162 | Arm Armv7 -> "armv7-none-linux-androideabi" 163 | Arm Arm64 -> "aarch64-none-linux-android" 164 | X86 I386 -> "i686-none-linux-android" 165 | arch -> unsupportedArch arch 166 | toolChain _ _ tcVariant _ = 167 | error $ "Unsupported toolchain variant " ++ show tcVariant 168 | 169 | -- | Valid Android ABI identifier for the given architecture. 170 | abiString :: Arch -> String 171 | abiString (Arm Armv5) = "armeabi" 172 | abiString (Arm Armv6) = "armeabi" 173 | abiString (Arm Armv7) = "armeabi-v7a" 174 | abiString (Arm Arm64) = "arm64-v8a" 175 | abiString (X86 _) = "x86" 176 | abiString arch = unsupportedArch arch 177 | 178 | -- | Source paths and build flags for the @native_app_glue@ module. 179 | native_app_glue :: FilePath -- ^ NDK source directory 180 | -> ([FilePath], BuildFlags -> BuildFlags) 181 | native_app_glue ndk = 182 | ( [ndk "sources/android/native_app_glue/android_native_app_glue.c"] 183 | , append systemIncludes [ndk "sources/android/native_app_glue"] ) 184 | 185 | -- | Build flags for building with and linking against the GNU @gnustl@ standard C++ library. 186 | gnustl :: Version -- ^ GNU STL version 187 | -> Linkage -- ^ `Static` or `Shared` 188 | -> FilePath -- ^ NDK source directory 189 | -> Target -- ^ Build target, see `target` 190 | -> (BuildFlags -> BuildFlags) -- ^ 'BuildFlags' modification function 191 | gnustl version linkage ndk t = 192 | append systemIncludes [stlPath "include", stlPath "libs" abi "include"] 193 | . append libraryPath [stlPath "libs" abi] 194 | . append libraries [lib] 195 | where stlPath = ndk "sources/cxx-stl/gnu-libstdc++" showVersion version 196 | abi = abiString (targetArch t) 197 | lib = case linkage of 198 | Static -> "gnustl_static" 199 | Shared -> "gnustl_shared" 200 | 201 | -- | Build flags for building with and linking against the LLVM @libc++@ standard C++ library. 202 | libcxx :: Linkage -- ^ `Static` or `Shared` 203 | -> FilePath -- ^ NDK source directory 204 | -> Target -- ^ Build target, see `target` 205 | -> (BuildFlags -> BuildFlags) -- ^ 'BuildFlags' modification function 206 | libcxx linkage ndk t = 207 | append systemIncludes [ stl "llvm-libc++" "libcxx" "include" 208 | -- NOTE: libcxx needs to be first in include path! 209 | , stl "llvm-libc++abi" "libcxxabi" "include" 210 | , ndk "sources" "android" "support" "include" ] 211 | . append compilerFlags [(Just Cpp, ["-stdlib=libc++"])] 212 | . append libraryPath [stl "llvm-libc++" "libs" abi] 213 | . prepend libraries [lib] 214 | where stl = ndk "sources" "cxx-stl" 215 | abi = abiString (targetArch t) 216 | lib = case linkage of 217 | Static -> "c++_static" 218 | Shared -> "c++_shared" 219 | -------------------------------------------------------------------------------- /src/Development/Shake/Language/C/Target/Linux.hs: -------------------------------------------------------------------------------- 1 | -- Copyright 2012-2014 Samplecount S.L. 2 | -- 3 | -- Licensed under the Apache License, Version 2.0 (the "License"); 4 | -- you may not use this file except in compliance with the License. 5 | -- You may obtain a copy of the License at 6 | -- 7 | -- http://www.apache.org/licenses/LICENSE-2.0 8 | -- 9 | -- Unless required by applicable law or agreed to in writing, software 10 | -- distributed under the License is distributed on an "AS IS" BASIS, 11 | -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | -- See the License for the specific language governing permissions and 13 | -- limitations under the License. 14 | 15 | {-| 16 | Description: Toolchain definitions and utilities for Linux 17 | 18 | This module provides toolchain definitions and utilities for targeting Linux. 19 | See "Development.Shake.Language.C.Rules" for examples of how to use a target 20 | toolchain. 21 | 22 | Linux is also a supported host operating system, see 23 | "Development.Shake.Language.C.Host" for examples of how to target the host. 24 | -} 25 | 26 | module Development.Shake.Language.C.Target.Linux ( 27 | target 28 | , toolChain 29 | ) where 30 | 31 | import Data.Label (get, set) 32 | import Development.Shake 33 | import Development.Shake.Language.C.BuildFlags 34 | import Development.Shake.Language.C.Target 35 | import Development.Shake.Language.C.ToolChain 36 | 37 | -- | Build target given an architecture. 38 | target :: Arch -> Target 39 | target = Target Linux (Platform "linux") 40 | 41 | platformArchiver :: Archiver 42 | platformArchiver tc buildFlags inputs output = do 43 | need inputs 44 | command_ [] (tool tc archiverCommand) 45 | $ ["cr"] 46 | ++ get archiverFlags buildFlags 47 | ++ [output] 48 | ++ inputs 49 | command_ [] (toolFromString tc "ranlib") [output] 50 | 51 | -- | Linux toolchain. 52 | toolChain :: ToolChainVariant -> ToolChain 53 | toolChain GCC = 54 | set variant GCC 55 | $ set compilerCommand "gcc" 56 | $ set archiverCommand "ar" 57 | $ set archiver platformArchiver 58 | $ set linkerCommand "g++" 59 | $ defaultToolChain 60 | toolChain LLVM = 61 | set variant LLVM 62 | $ set compilerCommand "clang" 63 | $ set archiverCommand "ar" 64 | $ set archiver platformArchiver 65 | $ set linkerCommand "clang++" 66 | $ defaultToolChain 67 | toolChain Generic = toolChain GCC 68 | -------------------------------------------------------------------------------- /src/Development/Shake/Language/C/Target/NaCl.hs: -------------------------------------------------------------------------------- 1 | -- Copyright 2013 Samplecount S.L. 2 | -- 3 | -- Licensed under the Apache License, Version 2.0 (the "License"); 4 | -- you may not use this file except in compliance with the License. 5 | -- You may obtain a copy of the License at 6 | -- 7 | -- http://www.apache.org/licenses/LICENSE-2.0 8 | -- 9 | -- Unless required by applicable law or agreed to in writing, software 10 | -- distributed under the License is distributed on an "AS IS" BASIS, 11 | -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | -- See the License for the specific language governing permissions and 13 | -- limitations under the License. 14 | 15 | {-| 16 | Description: Toolchain definitions and utilities for Google Pepper and Portable Native Client 17 | 18 | This module provides toolchain definitions and utilities for targeting Google 19 | Pepper and Portable Native Client (PNaCl). Arguably it should be renamed 20 | appropriately. See "Development.Shake.Language.C.Rules" for examples of how to 21 | use a target toolchain. 22 | -} 23 | module Development.Shake.Language.C.Target.NaCl ( 24 | pepper 25 | , canary 26 | , target 27 | , Config(..) 28 | , toolChain 29 | , finalize 30 | , translate 31 | , Executable(..) 32 | , Program(..) 33 | , mk_nmf 34 | ) where 35 | 36 | import Development.Shake 37 | import Development.Shake.FilePath 38 | import Data.Version (Version(..)) 39 | import Development.Shake.Language.C hiding (Arch) 40 | import qualified Development.Shake.Language.C.Target as C 41 | import Development.Shake.Language.C.ToolChain 42 | import qualified Development.Shake.Language.C.Host as Host 43 | import Development.Shake.Language.C.Label 44 | 45 | -- | Stable /Pepper/ API version. 46 | pepper :: Int -> Version 47 | pepper apiVersion = Version [apiVersion] [] 48 | 49 | -- | Unstable /Pepper Canary/ API version. 50 | canary :: Version 51 | canary = Version [] ["canary"] 52 | 53 | -- | Pepper target. 54 | -- 55 | -- `LLVM_IR` (PNaCl) is the only supported target architecture at the moment. 56 | target :: Target 57 | target = Target Pepper (Platform "pepper") LLVM_IR 58 | 59 | hostString :: String 60 | hostString = 61 | case Host.os of 62 | Host.Linux -> "linux" 63 | Host.OSX -> "mac" 64 | Host.Windows -> "win" 65 | 66 | -- | Pepper build configuration. 67 | -- 68 | -- This is used to select the respective library versions when linking. 69 | data Config = Debug | Release deriving (Eq, Show) 70 | 71 | -- | Construct Pepper toolchain. 72 | toolChain :: FilePath -- ^ Pepper SDK directory (@nacl_sdk@) 73 | -> Version -- ^ Pepper API version, see `pepper` and `canary` 74 | -> Config -- ^ Build configuration for linked libraries 75 | -> Target -- ^ Target, see `target` 76 | -> ToolChain -- ^ Resulting toolchain 77 | toolChain sdk sdkVersion config t = 78 | set variant LLVM 79 | $ set toolDirectory (Just (platformDir "toolchain" hostString ++ "_" ++ "pnacl" "bin")) 80 | $ set toolPrefix "pnacl-" 81 | $ set compilerCommand "clang" 82 | $ set archiverCommand "ar" 83 | $ set archiver (\tc flags inputs output -> do 84 | need inputs 85 | command_ [] (tool tc archiverCommand) 86 | $ ["cr"] 87 | ++ get archiverFlags flags 88 | ++ [output] 89 | ++ inputs 90 | command_ [] (toolFromString tc "ranlib") [output] 91 | ) 92 | $ set linkerCommand "clang++" 93 | $ set defaultBuildFlags 94 | ( return $ 95 | append systemIncludes [includeDir] 96 | . append userIncludes [includeDir] 97 | . append systemIncludes [includeDir "pnacl"] 98 | . append libraryPath [platformDir "lib" "pnacl" show config] ) 99 | $ defaultToolChain 100 | where 101 | platformDir = 102 | sdk 103 | platformName (targetPlatform t) 104 | ++ "_" 105 | ++ case versionTags sdkVersion of 106 | ["canary"] -> "canary" 107 | _ -> show $ head (versionBranch sdkVersion) 108 | includeDir = platformDir "include" 109 | 110 | -- | Finalize a bit code executable. 111 | finalize :: ToolChain -- ^ Toolchain, see `toolChain` 112 | -> FilePath -- ^ Bit code input executable 113 | -> FilePath -- ^ Finalised bit code output executable 114 | -> Action () 115 | finalize tc input output = do 116 | need [input] 117 | command_ [] (toolFromString tc "finalize") 118 | ["-o", output, input] 119 | 120 | -- | Translate bit code to native code. 121 | translate :: ToolChain -> C.Arch -> FilePath -> FilePath -> Action () 122 | translate tc arch input output = do 123 | let archName = 124 | case arch of 125 | X86 I686 -> "i686" 126 | X86 X86_64 -> "x86-64" 127 | Arm Armv7 -> "armv7" 128 | _ -> error $ "Unsupported architecture: " ++ show arch 129 | need [input] 130 | command_ [] (toolFromString tc "finalize") 131 | ["-arch", archName, "-o", output, input] 132 | 133 | -- | Executable specification for Native Client Manifest (nmf) files. 134 | data Executable = Executable { 135 | executablePath :: FilePath -- ^ Relative path to executable. 136 | , optimizationLevel :: Maybe Int -- ^ Optional optimization level. 137 | } deriving (Eq, Show) 138 | 139 | -- | Program specification for Native Client Manifest (nmf) files. 140 | data Program = Program { 141 | pnaclTranslate :: Executable -- ^ Release executable (pexe) 142 | , pnaclDebug :: Maybe Executable -- ^ Executable used when debugging (bit code). 143 | } deriving (Eq, Show) 144 | 145 | -- | Create Native Client Manifest (nmf) files. 146 | -- 147 | -- This file is needed for serving PNaCl outside the Google Play store. See the native client for more information on the file format. 148 | mk_nmf :: Program -- ^ Program specification 149 | -> FilePath -- ^ Output file 150 | -> Action () 151 | mk_nmf program output = do 152 | need $ [executablePath (pnaclTranslate program)] 153 | ++ maybe [] ((:[]).executablePath) (pnaclDebug program) 154 | writeFileChanged output . unlines $ [ 155 | "{" 156 | , " \"program\": {" 157 | , " \"portable\": {" 158 | ] 159 | ++ entry "pnacl-translate" (pnaclTranslate program) 160 | ++ maybe [] (\e -> [","] ++ entry "pnacl-debug" e) (pnaclDebug program) 161 | ++ 162 | [ " }" 163 | , " }" 164 | , "}" 165 | ] 166 | where 167 | entry what prog = [ 168 | " \"" ++ what ++ "\": {" 169 | , " \"url\": \"" ++ makeRelative (takeDirectory output) (executablePath prog) ++ "\"" 170 | ] ++ maybe [] (\n -> 171 | [" , \"optlevel\": " ++ show n]) (optimizationLevel prog) 172 | ++ 173 | [ " }" ] 174 | -------------------------------------------------------------------------------- /src/Development/Shake/Language/C/Target/OSX.hs: -------------------------------------------------------------------------------- 1 | -- Copyright 2012-2014 Samplecount S.L. 2 | -- 3 | -- Licensed under the Apache License, Version 2.0 (the "License"); 4 | -- you may not use this file except in compliance with the License. 5 | -- You may obtain a copy of the License at 6 | -- 7 | -- http://www.apache.org/licenses/LICENSE-2.0 8 | -- 9 | -- Unless required by applicable law or agreed to in writing, software 10 | -- distributed under the License is distributed on an "AS IS" BASIS, 11 | -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | -- See the License for the specific language governing permissions and 13 | -- limitations under the License. 14 | 15 | {-| 16 | Description: Toolchain definitions and utilities for OSX and iOS 17 | 18 | This module provides toolchain definitions and utilities for targeting OSX 19 | and iOS. See "Development.Shake.Language.C.Rules" for examples of how to use a 20 | target toolchain. 21 | 22 | OSX is also a supported host operating system, see 23 | "Development.Shake.Language.C.Host" for examples of how to target the host. 24 | -} 25 | 26 | module Development.Shake.Language.C.Target.OSX ( 27 | DeveloperPath 28 | , getSDKRoot 29 | , macOSX 30 | , iPhoneOS 31 | , iPhoneSimulator 32 | , target 33 | , sdkVersion 34 | , toolChain 35 | , getPlatformVersions 36 | , macosx_version_min 37 | , iphoneos_version_min 38 | , universalBinary 39 | ) where 40 | 41 | import Data.List (stripPrefix) 42 | import Data.List.Split (splitOn) 43 | import Data.Maybe 44 | import Data.Version (Version(..), showVersion) 45 | import Development.Shake as Shake 46 | import Development.Shake.FilePath 47 | import Development.Shake.Language.C.BuildFlags 48 | import Development.Shake.Language.C.Target 49 | import Development.Shake.Language.C.Label 50 | import Development.Shake.Language.C.ToolChain 51 | import System.Process (readProcess) 52 | import Text.Read (readMaybe) 53 | 54 | archString :: Arch -> String 55 | archString arch = 56 | case arch of 57 | X86 I386 -> "i386" 58 | X86 I686 -> "i686" 59 | X86 X86_64 -> "x86_64" 60 | Arm Armv5 -> "armv5" 61 | Arm Armv6 -> "armv6" 62 | Arm Armv7 -> "armv7" 63 | Arm Armv7s -> "armv7s" 64 | Arm Arm64 -> "arm64" 65 | _ -> error $ "Unsupported OSX target architecture " ++ show arch 66 | 67 | archFlags :: Target -> [String] 68 | archFlags t = ["-arch", archString (targetArch t)] 69 | 70 | -- | Base path of development tools on OSX. 71 | newtype DeveloperPath = DeveloperPath FilePath 72 | deriving (Show) 73 | 74 | -- | Get base path of development tools on OSX. 75 | getSDKRoot :: Action DeveloperPath 76 | getSDKRoot = liftIO $ 77 | (DeveloperPath . head . splitOn "\n") 78 | <$> readProcess "xcode-select" ["--print-path"] "" 79 | 80 | -- | Mac OSX platform. 81 | macOSX :: Platform 82 | macOSX = Platform "MacOSX" 83 | 84 | -- | iOS platform. 85 | iPhoneOS :: Platform 86 | iPhoneOS = Platform "iPhoneOS" 87 | 88 | -- | iOS simulator platform. 89 | iPhoneSimulator :: Platform 90 | iPhoneSimulator = Platform "iPhoneSimulator" 91 | 92 | -- | Build target given a platform and an architecture. 93 | target :: Platform -> Arch -> Target 94 | target = Target OSX 95 | 96 | sdkDirectory :: FilePath -> Platform -> FilePath 97 | sdkDirectory sdkRoot platform = 98 | sdkRoot 99 | "Platforms" 100 | (platformName platform ++ ".platform") 101 | "Developer" 102 | "SDKs" 103 | 104 | platformSDKPath :: FilePath -> Platform -> Version -> FilePath 105 | platformSDKPath sdkRoot platform version = 106 | sdkDirectory sdkRoot platform 107 | platformName platform ++ showVersion version ++ ".sdk" 108 | 109 | getPlatformVersionsWithRoot :: Platform -> DeveloperPath -> Action [Version] 110 | getPlatformVersionsWithRoot platform (DeveloperPath sdkRoot) = do 111 | dirs <- getDirectoryDirs (sdkDirectory sdkRoot platform) 112 | case mapMaybe (\x -> parseVersion =<< stripPrefix name (dropExtension x)) dirs of 113 | [] -> error $ "No SDK found for " ++ name 114 | xs -> return xs 115 | where name = platformName platform 116 | parseVersion "" = Nothing 117 | parseVersion str = 118 | flip Version [] <$> mapM readMaybe (splitOn "." str) 119 | 120 | -- | Return a list of available platform SDK versions. 121 | -- 122 | -- For example in order to get the latest iOS SDK version: 123 | -- 124 | -- > maximum <$> getPlatformVersions iPhoneOS 125 | getPlatformVersions :: Platform -> Action [Version] 126 | getPlatformVersions platform = 127 | getPlatformVersionsWithRoot platform =<< getSDKRoot 128 | 129 | -- | SDK version given major and minor version numbers. 130 | sdkVersion :: Int -> Int -> Version 131 | sdkVersion major minor = Version [major, minor] [] 132 | 133 | -- | Construct an OSX or iOS toolchain. 134 | toolChain :: DeveloperPath -- ^ Developer tools base path, see `getSDKRoot` 135 | -> Version -- ^ Target SDK version 136 | -> Target -- ^ Build target, see `target` 137 | -> ToolChain -- ^ Resulting toolchain 138 | toolChain (DeveloperPath sdkRoot) version t = 139 | set variant LLVM 140 | $ set toolDirectory (Just (sdkRoot "Toolchains/XcodeDefault.xctoolchain/usr/bin")) 141 | $ set compilerCommand "clang" 142 | $ set archiverCommand "libtool" 143 | $ set archiver (\tc flags inputs output -> do 144 | need inputs 145 | command_ [] (tool tc archiverCommand) 146 | $ get archiverFlags flags 147 | ++ ["-static"] 148 | ++ ["-o", output] 149 | ++ inputs 150 | ) 151 | $ set linkerCommand "clang++" 152 | $ set linker (\lr tc -> 153 | case lr of 154 | Executable -> defaultLinker tc 155 | SharedLibrary -> defaultLinker tc . prepend linkerFlags ["-dynamiclib"] 156 | LoadableLibrary -> defaultLinker tc . prepend linkerFlags ["-bundle"] 157 | ) 158 | $ set defaultBuildFlags 159 | ( return $ 160 | append preprocessorFlags [ "-isysroot", sysRoot ] 161 | . append compilerFlags [(Nothing, archFlags t)] 162 | . append linkerFlags (archFlags t ++ [ "-isysroot", sysRoot ]) ) 163 | $ defaultToolChain 164 | where sysRoot = platformSDKPath sdkRoot (targetPlatform t) version 165 | 166 | -- | Specify the @-mmacosx-version-min@ compiler flag. 167 | macosx_version_min :: Version -> BuildFlags -> BuildFlags 168 | macosx_version_min version = 169 | append compilerFlags [(Nothing, ["-mmacosx-version-min=" ++ showVersion version])] 170 | 171 | -- | Specify the @-miphoneos-version-min@ compiler flag. 172 | iphoneos_version_min :: Version -> BuildFlags -> BuildFlags 173 | iphoneos_version_min version = 174 | append compilerFlags [(Nothing, ["-miphoneos-version-min=" ++ showVersion version])] 175 | 176 | -- | Create a universal binary a list of input files. 177 | -- 178 | -- Calls with the @-create@ option. 179 | universalBinary :: [FilePath] -- ^ Input files, can be executables, dynamic libraries or static archives but should be all of the same type 180 | -> FilePath -- ^ Output file path 181 | -> Action () 182 | universalBinary inputs output = do 183 | need inputs 184 | command_ [] "lipo" $ ["-create", "-output", output] ++ inputs 185 | -------------------------------------------------------------------------------- /src/Development/Shake/Language/C/Target/Windows.hs: -------------------------------------------------------------------------------- 1 | -- Copyright 2014 Samplecount S.L. 2 | -- 3 | -- Licensed under the Apache License, Version 2.0 (the "License"); 4 | -- you may not use this file except in compliance with the License. 5 | -- You may obtain a copy of the License at 6 | -- 7 | -- http://www.apache.org/licenses/LICENSE-2.0 8 | -- 9 | -- Unless required by applicable law or agreed to in writing, software 10 | -- distributed under the License is distributed on an "AS IS" BASIS, 11 | -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | -- See the License for the specific language governing permissions and 13 | -- limitations under the License. 14 | 15 | {-| 16 | Description: Toolchain definitions and utilities for Windows 17 | 18 | This module provides toolchain definitions and utilities for targeting Windows. 19 | See "Development.Shake.Language.C.Rules" for examples of how to use a target 20 | toolchain. 21 | 22 | Windows is also a supported host operating system, see 23 | "Development.Shake.Language.C.Host" for examples of how to target the host. 24 | 25 | On Windows currently only the toolchain is 26 | supported. 27 | -} 28 | 29 | module Development.Shake.Language.C.Target.Windows ( 30 | target 31 | , toolChain 32 | ) where 33 | 34 | import Data.Label (get, set) 35 | import Development.Shake 36 | import Development.Shake.Language.C.BuildFlags 37 | import Development.Shake.Language.C.Target 38 | import Development.Shake.Language.C.ToolChain 39 | 40 | -- | Build target given an architecture. 41 | target :: Arch -> Target 42 | target = Target Windows (Platform "windows") 43 | 44 | -- | Windows toolchain. 45 | toolChain :: ToolChainVariant -> ToolChain 46 | toolChain GCC = 47 | set variant GCC 48 | $ set compilerCommand "gcc" 49 | $ set archiverCommand "ar" 50 | $ set archiver (\tc flags inputs output -> do 51 | need inputs 52 | command_ [] (tool tc archiverCommand) 53 | $ ["cr"] 54 | ++ get archiverFlags flags 55 | ++ [output] 56 | ++ inputs 57 | command_ [] (toolFromString tc "ranlib") [output] 58 | ) 59 | $ set linkerCommand "g++" 60 | $ defaultToolChain 61 | toolChain LLVM = 62 | set variant LLVM 63 | $ set compilerCommand "gcc" 64 | $ set archiverCommand "ar" 65 | $ set linkerCommand "g++" 66 | $ defaultToolChain 67 | toolChain Generic = toolChain GCC 68 | -------------------------------------------------------------------------------- /src/Development/Shake/Language/C/ToolChain.hs: -------------------------------------------------------------------------------- 1 | -- Copyright 2012-2014 Samplecount S.L. 2 | -- 3 | -- Licensed under the Apache License, Version 2.0 (the "License"); 4 | -- you may not use this file except in compliance with the License. 5 | -- You may obtain a copy of the License at 6 | -- 7 | -- http://www.apache.org/licenses/LICENSE-2.0 8 | -- 9 | -- Unless required by applicable law or agreed to in writing, software 10 | -- distributed under the License is distributed on an "AS IS" BASIS, 11 | -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | -- See the License for the specific language governing permissions and 13 | -- limitations under the License. 14 | 15 | {-# LANGUAGE TemplateHaskell #-} 16 | {-# LANGUAGE TypeOperators #-} 17 | 18 | {-| 19 | Description: Types and functions for working with target tool chains 20 | -} 21 | 22 | module Development.Shake.Language.C.ToolChain ( 23 | Linkage(..) 24 | -- ** Working with toolchains 25 | , ToolChain 26 | , ToolChainVariant(..) 27 | , toolDirectory -- | Directory prefix for tools in a `ToolChain`, e.g. @\/usr\/local\/linux-armv5-eabi\/bin@. 28 | , toolPrefix -- | Prefix string for tools in a `ToolChain`, e.g. @"linux-armv5-eabi-"@. 29 | , variant -- | Toolchain variant. 30 | , compilerCommand -- | Compiler command, usually used in the `compiler` action. 31 | , Compiler 32 | , compiler -- | Compiler action for this `ToolChain`. 33 | , archiverCommand -- | Archiver command, usually used in the `archiver` action. 34 | , Archiver 35 | , archiver -- | Archiver action for this `ToolChain`. 36 | , linkerCommand -- | Linker command, usually used in the `linker` action. 37 | , Linker 38 | , LinkResult(..) 39 | , linker -- | Linker action for this `ToolChain`. 40 | , defaultBuildFlags -- | Action returning the default `BuildFlags` for this `ToolChain`. 41 | 42 | -- ** Interfacing with other build systems 43 | , applyEnv 44 | , toEnv 45 | -- ** Utilities for toolchain writers 46 | , defaultToolChain 47 | , defaultCompiler 48 | , defaultArchiver 49 | , defaultLinker 50 | , toolFromString 51 | , tool 52 | ) where 53 | 54 | import Control.Applicative 55 | import Data.Char (toLower) 56 | import Data.List (isInfixOf, isSuffixOf) 57 | import Data.Monoid (mempty) 58 | import Development.Shake 59 | import Development.Shake.FilePath 60 | import Development.Shake.Util (needMakefileDependencies) 61 | import Development.Shake.Language.C.Label 62 | import Development.Shake.Language.C.BuildFlags 63 | import Development.Shake.Language.C.Language (languageOf) 64 | import Development.Shake.Language.C.Util 65 | 66 | -- | Linkage type, static or shared. 67 | data Linkage = Static | Shared deriving (Enum, Eq, Show) 68 | 69 | -- | Link result type 70 | data LinkResult = 71 | Executable -- ^ Executable 72 | | SharedLibrary -- ^ Shared (dynamically linked) library 73 | | LoadableLibrary -- ^ Dynamically loadable library 74 | deriving (Enum, Eq, Show) 75 | 76 | -- | Toolchain variant. 77 | data ToolChainVariant = 78 | Generic -- ^ Unspecified toolchain 79 | | GCC -- ^ GNU Compiler Collection (gcc) toolchain 80 | | LLVM -- ^ Low-Level Virtual Machine (LLVM) toolchain 81 | deriving (Eq, Show) 82 | 83 | -- | `Action` type for producing an object file from a source file. 84 | type Compiler = 85 | ToolChain -- ^ Toolchain 86 | -> BuildFlags -- ^ Compiler flags 87 | -> FilePath -- ^ Input source file 88 | -> FilePath -- ^ Output object file 89 | -> Action () 90 | 91 | -- | `Action` type for linking object files into an executable or a library. 92 | type Linker = 93 | ToolChain -- ^ Toolchain 94 | -> BuildFlags -- ^ Linker flags 95 | -> [FilePath] -- ^ Input object files 96 | -> FilePath -- ^ Output link product 97 | -> Action () 98 | 99 | -- | `Action` type for archiving object files into a static library. 100 | type Archiver = 101 | ToolChain -- ^ Toolchain 102 | -> BuildFlags -- ^ Archiver flags 103 | -> [FilePath] -- ^ Input object files 104 | -> FilePath -- ^ Output object archive (static library) 105 | -> Action () 106 | 107 | data ToolChain = ToolChain { 108 | _variant :: ToolChainVariant 109 | , _toolDirectory :: Maybe FilePath 110 | , _toolPrefix :: String 111 | , _compilerCommand :: FilePath 112 | , _compiler :: Compiler 113 | , _archiverCommand :: FilePath 114 | , _archiver :: Archiver 115 | , _linkerCommand :: FilePath 116 | , _linker :: LinkResult -> Linker 117 | , _defaultBuildFlags :: Action (BuildFlags -> BuildFlags) 118 | } 119 | 120 | mkLabel ''ToolChain 121 | 122 | -- | Default compiler action. 123 | defaultCompiler :: Compiler 124 | defaultCompiler toolChain buildFlags input output = do 125 | need $ [input] 126 | let depFile = output <.> "d" 127 | command_ [] (tool toolChain compilerCommand) 128 | $ concatMapFlag "-isystem" (get systemIncludes buildFlags) 129 | ++ mapFlag "-I" (get userIncludes buildFlags) 130 | ++ defineFlags buildFlags 131 | ++ get preprocessorFlags buildFlags 132 | ++ compilerFlagsFor (languageOf input) buildFlags 133 | ++ ["-MD", "-MF", depFile] 134 | ++ ["-c", "-o", output, input] 135 | needMakefileDependencies depFile 136 | 137 | -- | Default archiver action. 138 | defaultArchiver :: Archiver 139 | defaultArchiver toolChain buildFlags inputs output = do 140 | need inputs 141 | command_ [] (tool toolChain archiverCommand) 142 | $ get archiverFlags buildFlags 143 | ++ [output] 144 | ++ inputs 145 | 146 | -- | Default linker action. 147 | defaultLinker :: Linker 148 | defaultLinker toolChain buildFlags inputs output = do 149 | let localLibs = get localLibraries buildFlags 150 | buildFlags' = append libraryPath (map takeDirectory localLibs) 151 | -- Local libraries must be passed to the linker before system libraries they depend on 152 | . prepend libraries (map (strip.dropExtension.takeFileName) localLibs) 153 | $ buildFlags 154 | need $ inputs ++ localLibs 155 | command_ [] (tool toolChain linkerCommand) 156 | $ inputs 157 | ++ get linkerFlags buildFlags' 158 | ++ concatMapFlag "-L" (get libraryPath buildFlags') 159 | ++ concatMapFlag "-l" (get libraries buildFlags') 160 | ++ ["-o", output] 161 | where 162 | strip ('l':'i':'b':rest) = rest 163 | strip x = x 164 | 165 | -- | Default toolchain. 166 | -- 167 | -- Probably not useful without modification. 168 | defaultToolChain :: ToolChain 169 | defaultToolChain = 170 | ToolChain { 171 | _variant = GCC 172 | , _toolDirectory = Nothing 173 | , _toolPrefix = "" 174 | , _compilerCommand = "gcc" 175 | , _compiler = defaultCompiler 176 | , _archiverCommand = "ar" 177 | , _archiver = defaultArchiver 178 | , _linkerCommand = "gcc" 179 | , _linker = \linkResult linkerCmd -> 180 | case linkResult of 181 | Executable -> defaultLinker linkerCmd 182 | _ -> defaultLinker linkerCmd . append linkerFlags ["-shared"] 183 | , _defaultBuildFlags = return id 184 | } 185 | 186 | -- | Given a tool chain command name, construct the command's full path, taking into account the toolchain's `toolPrefix`. 187 | toolFromString :: 188 | ToolChain -- ^ Toolchain 189 | -> String -- ^ Command name 190 | -> FilePath -- ^ Full command path 191 | toolFromString toolChain name = 192 | let c = _toolPrefix toolChain ++ name 193 | in maybe c ( c) (_toolDirectory toolChain) 194 | 195 | -- | Construct the full path of a predefined tool given a `ToolChain` accessor. 196 | tool :: 197 | ToolChain -- ^ Toolchain 198 | -> (ToolChain :-> String) -- ^ Toolchain accessor 199 | -> FilePath -- ^ Full command path 200 | tool toolChain getter = toolFromString toolChain (get getter toolChain) 201 | 202 | -- | Apply the current environment and return a modified toolchain. 203 | -- 204 | -- This function is experimental and subject to change! 205 | -- 206 | -- Currently recognised environment variables are 207 | -- 208 | -- [@CC@] Path to @C@ compiler. 209 | -- 210 | -- [@LD@] Path to linker. 211 | -- 212 | -- [@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. 213 | applyEnv :: ToolChain -> Action ToolChain 214 | applyEnv toolChain = do 215 | cc <- getEnv "CC" 216 | ld <- getEnv "LD" 217 | vendor <- getEnv "SHAKE_TOOLCHAIN_VARIANT" 218 | return $ maybe id (set compilerCommand) cc 219 | $ maybe id (set linkerCommand) ld 220 | . maybe id (set variant) ((vendor >>= parseVendor) <|> (cc >>= vendorFromCommand)) 221 | $ toolChain 222 | where 223 | parseVendor s = 224 | case map toLower s of 225 | "gcc" -> Just GCC 226 | "llvm" -> Just LLVM 227 | "clang" -> Just LLVM 228 | _ -> Nothing 229 | vendorFromCommand path = 230 | let x = takeFileName path 231 | in if "gcc" `isInfixOf` x || "g++" `isInfixOf` x 232 | then Just GCC 233 | else if "clang" `isInfixOf` x 234 | then Just LLVM 235 | else Just Generic 236 | 237 | -- | Export a 'ToolChain' definition to a list of environment variable mappings, suitable e.g. for calling third-party configure scripts in cross-compilation mode. 238 | -- 239 | -- Needs some fleshing out; currently only works for "standard" binutil toolchains. 240 | toEnv :: ToolChain -> Action [(String,String)] 241 | toEnv tc = do 242 | flags <- (\f -> f mempty) <$> get defaultBuildFlags tc 243 | let cflags = concatMapFlag "-I" (map escapeSpaces (get systemIncludes flags)) 244 | ++ concatMapFlag "-I" (map escapeSpaces (get userIncludes flags)) 245 | ++ defineFlags flags 246 | ++ get preprocessorFlags flags 247 | ++ compilerFlagsFor (Just C) flags 248 | cxxflags = cflags ++ compilerFlagsFor (Just Cpp) flags 249 | ldflags = get linkerFlags flags 250 | ++ concatMapFlag "-L" (get libraryPath flags) 251 | ++ concatMapFlag "-l" (get libraries flags) 252 | c2cxx path = let x = takeFileName path 253 | in if "gcc" `isSuffixOf` x 254 | then (++"g++") . reverse . drop 3 . reverse $ path 255 | else if "clang" `isSuffixOf` x 256 | then path ++ "++" 257 | else path 258 | let cc = tool tc compilerCommand 259 | cpp = toolFromString tc "cpp" 260 | cppExists <- doesFileExist cpp 261 | let cpp' = if cppExists then cpp else cc ++ " -E" 262 | return $ [ 263 | ("CPP", cpp') 264 | , ("AR", tool tc archiverCommand) 265 | , ("NM", toolFromString tc "nm") 266 | , ("CC", tool tc compilerCommand) 267 | -- FIXME: Configure toolchain with compiler command per language? 268 | , ("CXX", c2cxx $ tool tc compilerCommand) 269 | , ("LD", toolFromString tc "ld") 270 | , ("RANLIB", toolFromString tc "ranlib") 271 | , ("CFLAGS", unwords cflags) 272 | , ("CPPFLAGS", unwords cflags) 273 | , ("CXXFLAGS", unwords cxxflags) 274 | , ("LDFLAGS", unwords ldflags) 275 | ] 276 | -------------------------------------------------------------------------------- /src/Development/Shake/Language/C/Util.hs: -------------------------------------------------------------------------------- 1 | -- Copyright 2012-2014 Samplecount S.L. 2 | -- 3 | -- Licensed under the Apache License, Version 2.0 (the "License"); 4 | -- you may not use this file except in compliance with the License. 5 | -- You may obtain a copy of the License at 6 | -- 7 | -- http://www.apache.org/licenses/LICENSE-2.0 8 | -- 9 | -- Unless required by applicable law or agreed to in writing, software 10 | -- distributed under the License is distributed on an "AS IS" BASIS, 11 | -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | -- See the License for the specific language governing permissions and 13 | -- limitations under the License. 14 | 15 | module Development.Shake.Language.C.Util ( 16 | mapFlag 17 | , concatMapFlag 18 | , escapeSpaces 19 | , words' 20 | ) where 21 | 22 | import Data.List 23 | 24 | mapFlag :: String -> [String] -> [String] 25 | mapFlag f = concatMap (\x -> [f, x]) 26 | 27 | concatMapFlag :: String -> [String] -> [String] 28 | concatMapFlag f = map (f++) 29 | 30 | -- | Escape spaces with '\\' character. 31 | -- 32 | -- >>> escapeSpaces "string contains spaces" 33 | -- "string\\ contains\\ spaces" 34 | -- 35 | -- >>> escapeSpaces " leading and trailing spaces " 36 | -- "\\ leading\\ and\\ trailing\\ spaces\\ " 37 | -- 38 | -- >>> escapeSpaces "noSpaces" 39 | -- "noSpaces" 40 | -- 41 | escapeSpaces :: String -> String 42 | escapeSpaces [] = [] 43 | escapeSpaces (' ':xs) = '\\' : ' ' : escapeSpaces xs 44 | escapeSpaces ('\\':xs) = '\\' : '\\' : escapeSpaces xs 45 | escapeSpaces (x:xs) = x : escapeSpaces xs 46 | 47 | -- | Split a list of space separated strings. 48 | -- 49 | -- Spaces can be escaped by '\\'. 50 | -- 51 | -- >>> words' "word and word\\ with\\ spaces" 52 | -- ["word","and","word with spaces"] 53 | -- 54 | words' :: String -> [String] 55 | words' = unescape . words 56 | where 57 | escape = "\\" 58 | escapeLength = length escape 59 | isEscaped = isSuffixOf escape 60 | dropEscape = (++" ") . reverse . drop escapeLength . reverse 61 | unescape [] = [] 62 | unescape [x] = [if isEscaped x then dropEscape x else x] 63 | unescape (x1:x2:xs) 64 | | isEscaped x1 = unescape ((dropEscape x1 ++ x2):xs) 65 | | otherwise = [x1] ++ unescape (x2:xs) 66 | -------------------------------------------------------------------------------- /stack.yaml: -------------------------------------------------------------------------------- 1 | resolver: nightly-2019-04-14 2 | packages: 3 | - '.' 4 | extra-deps: [] 5 | flags: {} 6 | extra-package-dbs: [] 7 | -------------------------------------------------------------------------------- /tests/doctests.hs: -------------------------------------------------------------------------------- 1 | -- Copyright 2014 Samplecount S.L. 2 | -- 3 | -- Licensed under the Apache License, Version 2.0 (the "License"); 4 | -- you may not use this file except in compliance with the License. 5 | -- You may obtain a copy of the License at 6 | -- 7 | -- http://www.apache.org/licenses/LICENSE-2.0 8 | -- 9 | -- Unless required by applicable law or agreed to in writing, software 10 | -- distributed under the License is distributed on an "AS IS" BASIS, 11 | -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | -- See the License for the specific language governing permissions and 13 | -- limitations under the License. 14 | 15 | import Test.DocTest 16 | 17 | main = doctest [ 18 | "-isrc" 19 | , "src/Development/Shake/Language/C/BuildFlags.hs" 20 | , "src/Development/Shake/Language/C/Config.hs" 21 | , "src/Development/Shake/Language/C/Target.hs" 22 | , "src/Development/Shake/Language/C/Util.hs" 23 | ] 24 | -------------------------------------------------------------------------------- /tests/spectests.hs: -------------------------------------------------------------------------------- 1 | module Main where 2 | 3 | import Control.Applicative 4 | import Control.Monad 5 | import Data.IORef 6 | import Development.Shake 7 | import Development.Shake.FilePath 8 | import Development.Shake.Language.C 9 | import qualified Development.Shake.Language.C.Host as Host 10 | import qualified System.Directory as Dir 11 | import Test.Hspec 12 | 13 | buildDir :: FilePath 14 | buildDir = "tests/build" 15 | 16 | withShake :: String 17 | -> (FilePath 18 | -> (FilePath -> FilePath) 19 | -> (FilePath -> FilePath) 20 | -> Rules FilePath) 21 | -> IO String 22 | withShake name mkRules = do 23 | ref <- newIORef undefined 24 | shake shakeOptions { shakeFiles = addTrailingPathSeparator buildDir } $ do 25 | output <- mkRules name 26 | (\x -> buildDir name "input" x) 27 | (\x -> buildDir name "output" x) 28 | action $ do 29 | need [output] 30 | liftIO $ writeIORef ref output 31 | readFile =<< readIORef ref 32 | 33 | shouldBeBuiltBy :: String 34 | -> (FilePath 35 | -> (FilePath -> FilePath) 36 | -> (FilePath -> FilePath) 37 | -> Rules FilePath) 38 | -> Expectation 39 | shouldBeBuiltBy name mkRules = 40 | withShake name mkRules `shouldReturn` unlines [name] 41 | 42 | cstring :: String -> String 43 | cstring = show 44 | 45 | main :: IO () 46 | main = hspec $ do 47 | runIO $ do 48 | b <- Dir.doesDirectoryExist buildDir 49 | when b $ Dir.removeDirectoryRecursive buildDir 50 | describe "Host toolchain" $ do 51 | it "compiles a C file to an executable" $ do 52 | "host_toolchain_compile_c" `shouldBeBuiltBy` \name mkInput mkOutput -> do 53 | let (_, toolChain) = Host.defaultToolChain 54 | input = mkInput "source.c" 55 | outputGen = mkOutput $ "result" <.> exe 56 | output = mkOutput "result.txt" 57 | input %> \path -> do 58 | writeFileLines path [ 59 | "#include " 60 | , "int main(int argc, char** argv)" 61 | , "{" 62 | , " printf(\"%s\\n\", " ++ cstring name ++ ");" 63 | , " return 0;" 64 | , "}" 65 | ] 66 | _ <- executable toolChain outputGen (pure id) (pure [input]) 67 | output %> \path -> do 68 | need [outputGen] 69 | cmd Shell (outputGen ++ " > " ++ path) 70 | return output 71 | it "compiles a C++ file to an executable" $ do 72 | "host_toolchain_compile_cpp" `shouldBeBuiltBy` \name mkInput mkOutput -> do 73 | let (_, toolChain) = Host.defaultToolChain 74 | input = mkInput "source.cpp" 75 | outputGen = mkOutput $ "result" <.> exe 76 | output = mkOutput "result.txt" 77 | input %> \path -> do 78 | writeFileLines path [ 79 | "#include " 80 | , "int main(int argc, char** argv)" 81 | , "{" 82 | , " std::cout << " ++ cstring name ++ " << std::endl;" 83 | , " return 0;" 84 | , "}" 85 | ] 86 | _ <- executable toolChain outputGen (pure id) (pure [input]) 87 | output %> \path -> do 88 | need [outputGen] 89 | cmd Shell (outputGen ++ " > " ++ path) 90 | return output 91 | -------------------------------------------------------------------------------- /tools/git-hooks/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | out=`cabal check` 4 | if [ $? -ne 0 ]; then 5 | echo "cabal check failed:\n" 6 | echo $out 7 | exit 1 8 | fi 9 | 10 | --------------------------------------------------------------------------------