├── .ghci ├── .gitignore ├── .vimprj ├── LICENSE-2.0 ├── README.md ├── Setup.hs ├── cblrepo.cabal ├── data ├── ghc-7.6.3_pkgs └── ghc-7.8.2_pkgs ├── src ├── Add.hs ├── BuildPkgs.hs ├── BumpPkgs.hs ├── ConvertDB.hs ├── CreateConfig.hs ├── Extract.hs ├── ListPkgs.hs ├── Main.hs ├── OldPkgDB.hs ├── OldPkgTypes.hs ├── PkgBuild.hs ├── PkgDB.hs ├── PkgTypes.hs ├── Remove.hs ├── Update.hs ├── Upgrades.hs ├── Util │ ├── Cabal.hs │ ├── Cfg.hs │ ├── Dist.hs │ ├── HackageIndex.hs │ ├── Misc.hs │ └── Translation.hs └── Versions.hs └── tests ├── 00sync.test ├── add.test ├── build.test ├── bump.test ├── config.test ├── data ├── bar.cabal ├── baz.cabal ├── flags-file0.cabal ├── foo-self.cabal └── foo.cabal ├── extract.test ├── flags.test ├── list.test ├── pkgbuild.test └── self-referencing-cabal.test /.ghci: -------------------------------------------------------------------------------- 1 | :set -isrc:dist/build/autogen 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | tags 3 | TAGS 4 | cblrepo.db 5 | -------------------------------------------------------------------------------- /.vimprj: -------------------------------------------------------------------------------- 1 | set makeprg=./Setup.hs\ build 2 | 3 | " {{{1 plugin config 4 | " let g:hdevtools_options = '-g -isrc -g -idist/build/autogen -g -hide-package -g monads-tf' 5 | let g:ghcmod_ghc_options = ['-isrc', '-idist/build/autogen'] 6 | " let g:syntastic_haskell_ghc_mod_args = '-g -fno-warn-missing-signatures -g -fno-warn-orphans -g -fno-warn-name-shadowing' 7 | let g:syntastic_haskell_ghc_mod_args = '-g -fno-warn-orphans -g -fno-warn-name-shadowing' 8 | 9 | " vim: ft=vim : 10 | -------------------------------------------------------------------------------- /LICENSE-2.0: -------------------------------------------------------------------------------- 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 | # What is cblrepo? 2 | 3 | The goal of `cblrepo` is to aid in maintaining a consistent set of Haskell packages, e.g. for a Linux distribution. Currently it's heavily influenced by the work required to maintain Haskell packages for [Arch Linux](http://www.archlinux.org/), but it's proven useful also for other distributions. 4 | 5 | # Building it 6 | 7 | It uses CABAL, so as soon as its dependencies are satisfied it builds and installs with the familiar 3 steps: 8 | 9 | $ ./Setup.hs configure 10 | $ ./Setup.hs build 11 | $ ./Setup.hs install 12 | 13 | Alternatively one can use the `cabal` tool: 14 | 15 | $ cabal install 16 | 17 | # Using it 18 | 19 | The following sections cover some of the more used commands, but it's not an exhaustive description of the tool. One can list all commands can be accessed using 20 | 21 | $ cblrepo --help 22 | 23 | and the help for an individual command is accessed using 24 | 25 | $ cblrepo --help 26 | 27 | ## Syncing with Hackage 28 | 29 | The program can maintain a cache of all the packages on Hackage, this cache is used in some of the commands. The cache is updated using 30 | 31 | $ cblrepo update 32 | 33 | By default the cache is stored in `~/.cblrepo/`, the location can be controlled using `--appdir=`. 34 | 35 | ## Adding packages 36 | 37 | All added packages are kept in a database, named `cblrepo.db`, which should be in the current directory (unless the `--db=` argument is used). The database will be created on first add, if it doesn't exist already. 38 | 39 | To `cblrepo` there are three types of packages: 40 | 41 | *GHC package* -- A package provided by GHC, e.g. `base`. Added using the `-g` (or `--ghc-pkg=`) flag. Multiple package can be added at the same time by using the flag multiple times. For each package only the package name and version is recorded. Ex: 42 | 43 | $ cblrepo add -g base,4.3.1.0 -g array,0.3.0.2 44 | 45 | *Distro package* -- A package provided by the distribution. Added using the `-d` (or `--distro-pkg=`) flag. Multiple package can be added at the same time using the flag multiple times. For each package package name, version, xrevision, and release is recorded. Ex: 46 | 47 | $ cblrepo add -d xmonad-contrib,0.12,2,1 -d zlib,0.6.1.1,3,2 48 | 49 | *Repo package* -- A package maintained using `cblrepo`. Added by giving the package name and version on the command line (the details are then extracted from the cache). Ex: 50 | 51 | $ cblrepo add dataenc,0.14.0.3 52 | 53 | The release number after adding is set to 1. 54 | 55 | If there are any unsatisfiable dependencies they will be reported by `cblrepo` and no changes will be made to the database. 56 | 57 | ## Upgrading packages 58 | 59 | The *add* command is used to update packages as well. 60 | 61 | ## Generating PKGBUILDs for packages 62 | 63 | Use the *pkgbuild* command to generate the source Arch Linux package. 64 | 65 | $ cblrepo pkgbuild yesod 66 | 67 | ## Adding patches for packages 68 | 69 | In some, hopefully rare, cases the packages found on Hackage require patching in order to work properly. There are three types of patches used by `cblrepo` at the moment: 70 | 71 | *Cabal patches* -- A patch `/.cabal` is applied to the CABAL file before it's use. It's also included in the Arch Linux source package created with the `pkgbuild` command. 72 | 73 | *Pkgbuild patches* -- A patch `/.pkgbuild` is applied to the generated PKGBUILD when executing the `pkgbuild` command. 74 | 75 | *Source patches* -- A patch `/.source` is included in the Arch Linux source package created with the `pkgbuild` command. 76 | 77 | *Install patches* -- A patch `/.install` is applied to the generated install file when executing the `pkgbuild` command. 78 | 79 | The `` value is the exact name of the package as it appears in Hackage; e.g., `http://hackage.haskell.org/package/bindings-GLFW-3.0.3.2/bindings-GLFW.cabal` would have `bindings-GLFW` as the package name. 80 | The default location for patches is the dir `./patches`, but `cblrepo` can be told to look elsewhere by using the `--patchdir=` flag. 81 | 82 | ### Details of patches 83 | 84 | `cblrepo` uses the external tool `patch` to apply patches. There are a few technical details worth knowing to make sure that `cblrepo`, and the files it generates, can work with your patches. 85 | 86 | Patches for CABAL files and PKGBUILD files are applied using the pattern 87 | 88 | patch 89 | 90 | which means that the path depth in the patch is of no importance at all. It is however important that these patches contain diffs for a single file only. 91 | 92 | Patches for the source is only ever used in generated PKGBUILD files, and then it's applied using the pattern 93 | 94 | patch -p4 < 95 | 96 | from within the package source (i.e. `${srcdir}/`). The reason for this particular patch depth should be obvious after reading the following section. 97 | 98 | ### Example of working with patches 99 | 100 | Knowledge of the tool [`quilt`](http://savannah.nongnu.org/projects/quilt) is extremely useful when working with patches. In the following example we add the package DBus (version 0.4) and as you'll see it requires all three kind of patches. 101 | 102 | *patches/DBus.cabal* 103 | 104 | Extract the Cabal file for DBus: 105 | 106 | $ cblrepo extract DBus,0.4 107 | 108 | then create a new patch and add the Cabal file: 109 | 110 | $ quilt new DBus.cabal 111 | $ quilt add DBus.cabal 112 | 113 | Now go ahead and make the changes to the file and then record them in the patch: 114 | 115 | $ quilt refresh 116 | 117 | Once the changes have been recorded it's safe to remove the Cabal file, and if you want you can also remove all the `quilt` files: 118 | 119 | $ rm DBus.cabal 120 | $ rm -fr .pc patches/series 121 | 122 | It's now possible to add the package: 123 | 124 | $ cblrepo add DBus,0.4 125 | 126 | *patches/DBus.pkgbuild* 127 | 128 | DBus also requires some changes to the generated PKGBUILD, so generate the source package and then use `quilt` to record the necessary changes to it: 129 | 130 | $ cblrepo pkgbuild DBus 131 | $ quilt new DBus.pkgbuild 132 | $ quilt edit haskell-dbus/PKGBUILD 133 | 134 | $ quilt refresh 135 | 136 | (Here we use the `quilt` command `edit` to add the file and open an editor in a single command.) To verify that the patch we remove some files and re-generate the source package: 137 | 138 | $ rm -fr haskell-dbus .pc patches/series 139 | $ cblrepo pkgbuild DBus 140 | 141 | If we now inspect the generated PKGBUILD file it should contain the desired changes. 142 | 143 | *patches/DBus.source* 144 | 145 | Finally DBus requires some minor changes to its source. We start with moving into the directory containing the source package, download and extract all files, and create the source patch: 146 | 147 | $ cd haskell-dbus 148 | $ makepkg -o 149 | $ quilt new DBus.source 150 | 151 | Now we can move into the extracted source and `quilt edit` files to our hearts' content. Finally record the changes and clean up: 152 | 153 | $ quilt refresh 154 | $ cd 155 | $ rm -fr haskell-dbus .pc patches/series 156 | 157 | When we now re-generate the source package all our patches will be used: 158 | 159 | $ cblrepo pkgbuild DBus 160 | $ tree haskell 161 | haskell-dbus/ 162 | ├── cabal.patch 163 | ├── haskell-dbus.install 164 | ├── PKGBUILD 165 | ├── PKGBUILD.orig 166 | └── source.patch 167 | 168 | ### Modifying patches 169 | 170 | The command `quilt import` makes it easy to work with existing patches. Also remember that the `--patchdir=` flag for `cblrepo` can be used to *prevent* use of patches by e.g. pointing it to `/tmp`. 171 | 172 | # Contact 173 | 174 | Please report bugs and suggestions for improvements at [github](https://github.com/magthe/cblrepo). 175 | -------------------------------------------------------------------------------- /Setup.hs: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env runhaskell 2 | 3 | {- 4 | - Copyright 2011 Per Magnus Therning 5 | - 6 | - Licensed under the Apache License, Version 2.0 (the "License"); 7 | - you may not use this file except in compliance with the License. 8 | - You may obtain a copy of the License at 9 | - 10 | - http://www.apache.org/licenses/LICENSE-2.0 11 | - 12 | - Unless required by applicable law or agreed to in writing, software 13 | - distributed under the License is distributed on an "AS IS" BASIS, 14 | - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | - See the License for the specific language governing permissions and 16 | - limitations under the License. 17 | -} 18 | 19 | import Distribution.Simple 20 | 21 | main = defaultMain 22 | -------------------------------------------------------------------------------- /cblrepo.cabal: -------------------------------------------------------------------------------- 1 | name: cblrepo 2 | version: 99.999.999 3 | cabal-version: >= 1.23 4 | license: OtherLicense 5 | license-file: LICENSE-2.0 6 | copyright: Copyright (c) 2011 Magnus Therning 7 | author: Magnus Therning 8 | maintainer: Magnus Therning 9 | stability: experimental 10 | bug-reports: mailto:magnus@therning.org 11 | synopsis: Tool to maintain a database of CABAL packages and their dependencies 12 | description: 13 | Helper tool for people maintaining a set of CABAL packages for their 14 | distribution. It maintains a database of packages and verifies 15 | dependencies of the entire set as packages are added or updated. It 16 | also makes it trivial to track packages as new versions are released 17 | on Hackage. 18 | 19 | It can also be used to build source packages for the Arch Linux 20 | distribution. 21 | category: Utils, Distribution 22 | build-type: Custom 23 | 24 | custom-setup 25 | setup-depends: base, Cabal 26 | 27 | executable cblrepo 28 | hs-source-dirs: src 29 | main-is: Main.hs 30 | other-modules: Add 31 | BuildPkgs 32 | BumpPkgs 33 | ConvertDB 34 | CreateConfig 35 | Extract 36 | ListPkgs 37 | OldPkgDB 38 | OldPkgTypes 39 | PkgBuild 40 | PkgDB 41 | PkgTypes 42 | Remove 43 | Update 44 | Upgrades 45 | Util.Cabal 46 | Util.Cfg 47 | Util.Dist 48 | Util.HackageIndex 49 | Util.Misc 50 | Util.Translation 51 | Versions 52 | build-depends: base ==4.9.*, 53 | filepath ==1.4.*, 54 | directory ==1.3.*, 55 | Cabal ==1.24.*, 56 | transformers ==0.5.*, 57 | bytestring ==0.10.*, 58 | tar ==0.5.*, 59 | zlib ==0.6.*, 60 | mtl ==2.2.*, 61 | process ==1.4.*, 62 | Unixutils ==1.54.*, 63 | unix ==2.7.*, 64 | ansi-wl-pprint ==0.6.*, 65 | aeson >=1.0 && <1.3, 66 | stringsearch ==0.3.*, 67 | optparse-applicative ==0.13.*, 68 | safe ==0.3.*, 69 | containers ==0.5.*, 70 | utf8-string ==1.0.*, 71 | text, 72 | vector 73 | default-language: Haskell2010 74 | 75 | Source-Repository head 76 | Type: git 77 | Location: https://github.com/archhaskell/cblrepo.git 78 | 79 | -- vim: set tw=0 : 80 | -------------------------------------------------------------------------------- /data/ghc-7.6.3_pkgs: -------------------------------------------------------------------------------- 1 | -g Cabal,1.16.0 2 | -g array,0.4.0.1 3 | -g base,4.6.0.1 4 | -g bin-package-db,0.0.0.0 5 | -g binary,0.5.1.1 6 | -g bytestring,0.10.0.2 7 | -g containers,0.5.0.0 8 | -g deepseq,1.3.0.1 9 | -g directory,1.2.0.1 10 | -g filepath,1.3.0.1 11 | -g ghc-prim,0.3.0.0 12 | -g haskell2010,1.1.1.0 13 | -g haskell98,2.0.0.2 14 | -g hoopl,3.9.0.0 15 | -g hpc,0.6.0.0 16 | -g integer-gmp,0.5.0.0 17 | -g old-locale,1.0.0.5 18 | -g old-time,1.1.0.1 19 | -g pretty,1.1.1.0 20 | -g process,1.1.0.2 21 | -g template-haskell,2.8.0.0 22 | -g time,1.4.0.1 23 | -g unix,2.6.0.1 24 | -------------------------------------------------------------------------------- /data/ghc-7.8.2_pkgs: -------------------------------------------------------------------------------- 1 | -g Cabal,1.18.1.3 2 | -g array,0.5.0.0 3 | -g base,4.7.0.0 4 | -g bin-package-db,0.0.0.0 5 | -g binary,0.7.1.0 6 | -g bytestring,0.10.4.0 7 | -g containers,0.5.5.1 8 | -g deepseq,1.3.0.2 9 | -g directory,1.2.1.0 10 | -g filepath,1.3.0.2 11 | -g ghc-prim,0.3.1.0 12 | -g haskell2010,1.1.2.0 13 | -g haskell98,2.0.0.3 14 | -g hoopl,3.10.0.1 15 | -g hpc,0.6.0.1 16 | -g integer-gmp,0.5.1.0 17 | -g old-locale,1.0.0.6 18 | -g old-time,1.1.0.2 19 | -g pretty,1.1.1.1 20 | -g process,1.2.0.0 21 | -g rts,1.0 22 | -g template-haskell,2.9.0.0 23 | -g time,1.4.2 24 | -g transformers,0.3.0.0 25 | -g unix,2.7.0.1 26 | -------------------------------------------------------------------------------- /src/Add.hs: -------------------------------------------------------------------------------- 1 | {- 2 | - Copyright 2011-2014 Per Magnus Therning 3 | - 4 | - Licensed under the Apache License, Version 2.0 (the "License"); 5 | - you may not use this file except in compliance with the License. 6 | - You may obtain a copy of the License at 7 | - 8 | - http://www.apache.org/licenses/LICENSE-2.0 9 | - 10 | - Unless required by applicable law or agreed to in writing, software 11 | - distributed under the License is distributed on an "AS IS" BASIS, 12 | - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | - See the License for the specific language governing permissions and 14 | - limitations under the License. 15 | -} 16 | 17 | module Add where 18 | 19 | -- {{{1 imports 20 | -- {{{2 local 21 | import PkgDB 22 | import qualified Util.Cabal as Cbl 23 | import Util.Misc 24 | import Util.Dist 25 | import Util.Cfg 26 | 27 | -- {{{2 system 28 | import Control.Monad.Reader 29 | import Data.List 30 | import Data.Maybe 31 | import Distribution.PackageDescription 32 | import Distribution.Version 33 | import qualified Distribution.Package as P 34 | import Control.Arrow 35 | import Data.Monoid 36 | import Data.Either 37 | import System.Unix.Directory 38 | 39 | -- {{{1 types 40 | data PkgType 41 | = GhcType String Version 42 | | DistroType String Version Int Int 43 | | RepoType GenericPackageDescription 44 | deriving (Eq, Show) 45 | 46 | -- {{{1 add 47 | add :: Command () 48 | add = do 49 | dbFn <- asks $ dbFile . fst 50 | db <- liftIO $ readDb dbFn 51 | dr <- asks $ dryRun . fst 52 | ghcVersion <- asks $ ghcVer . optsCmd . fst 53 | filePkgs <- asks $ cmdAddFileCbls . optsCmd . fst 54 | idxPkgs <- asks $ cmdAddCbls . optsCmd . fst 55 | -- 56 | ghcPkgs <- asks $ map (uncurry GhcType) . cmdAddGhcPkgs . optsCmd . fst 57 | distroPkgs <- asks $ map (\ (n, v, x, r) -> DistroType n v x r) . cmdAddDistroPkgs . optsCmd . fst 58 | genFilePkgs <- mapM (runCabalParseWithTempDir . fmap snd . Cbl.readFromFile . fst) filePkgs 59 | genIdxPkgs <- mapM ((runCabalParseWithTempDir . fmap snd . Cbl.readFromIdx) . (\ (a, b, _) -> (a, b))) idxPkgs 60 | genPkgs <- liftM (map RepoType) $ exitOnAnyLefts (genFilePkgs ++ genIdxPkgs) 61 | -- 62 | let pkgs = ghcPkgs ++ distroPkgs ++ genPkgs 63 | pkgNames = map getName pkgs 64 | tmpDb = foldl delPkg db pkgNames 65 | oldFlags = map (maybe ([], []) (pkgName &&& pkgFlags) . lookupPkg db . getName) pkgs 66 | fileFlags = map (\ (pkg, (_, fa)) -> (pkgNameStr pkg, fa)) 67 | (zip (rights genFilePkgs) filePkgs) 68 | idxFlags = map (\ (a, _, b) -> (a, b)) idxPkgs 69 | flags = fileFlags `combineFlags` idxFlags `combineFlags` oldFlags 70 | case addPkgs ghcVersion tmpDb flags pkgs of 71 | Left (unsatisfiables, breaksOthers) -> liftIO (mapM_ printUnSat unsatisfiables >> mapM_ printBrksOth breaksOthers) 72 | Right newDb -> liftIO $ unless dr $ saveDb newDb dbFn 73 | 74 | runCabalParseWithTempDir :: Cbl.CabalParse a -> Command (Either String a) 75 | runCabalParseWithTempDir f = do 76 | aD <- asks $ appDir . fst 77 | pD <- asks $ patchDir . optsCmd . fst 78 | cfg <- asks snd 79 | liftIO $ withTemporaryDirectory "/tmp/cblrepo." $ \ destDir -> do 80 | let cpe = Cbl.CabalParseEnv aD pD destDir (getIndexFileName cfg) 81 | Cbl.runCabalParse cpe f 82 | 83 | getName (GhcType n _) = n 84 | getName (DistroType n _ _ _) = n 85 | getName (RepoType gpd) = pkgNameStr $ packageDescription gpd 86 | 87 | -- {{{1 addPkgs 88 | addPkgs :: Version -> CblDB -> [(String, FlagAssignment)] -> [PkgType] -> Either ([(String, [P.Dependency])], [((String, Version), [(String, Maybe P.Dependency)])]) CblDB 89 | addPkgs ghcVer db flags pkgs = let 90 | (succs, fails) = partition (canBeAdded ghcVer db flags) pkgs 91 | newDb = foldl addPkg2 db (map (pkgTypeToCblPkg ghcVer db flags) succs) 92 | unsatisfieds = mapMaybe (finalizeToUnsatisfiableDeps ghcVer db flags) fails 93 | breaksOthers = mapMaybe (findBreaking db) fails 94 | in case (succs, fails) of 95 | (_, []) -> Right newDb 96 | ([], _) -> Left (unsatisfieds, breaksOthers) 97 | (_, _) -> addPkgs ghcVer newDb flags fails 98 | 99 | canBeAdded :: Version -> CblDB -> [(String, FlagAssignment)] -> PkgType -> Bool 100 | canBeAdded _ db _ (GhcType n v) = null $ checkDependants db n v 101 | canBeAdded _ db _ (DistroType n v _ _) = null $ checkDependants db n v 102 | canBeAdded ghcVer db flags pkg@(RepoType gpd) = finable && depsOK 103 | where 104 | fa = fromMaybe [] $ lookup (getName pkg) flags 105 | finable = either (const False) (const True) (finalizePkg ghcVer db fa gpd) 106 | n = pkgNameStr (packageDescription gpd) 107 | v = P.pkgVersion $ package $ packageDescription gpd 108 | depsOK = null $ checkDependants db n v 109 | 110 | pkgTypeToCblPkg _ _ _ (GhcType n v) = createGhcPkg n v 111 | pkgTypeToCblPkg _ _ _ (DistroType n v x r) = createDistroPkg n v x r 112 | pkgTypeToCblPkg ghcVer db flags pkg@(RepoType gpd) = 113 | let fa = fromMaybe [] $ lookup (getName pkg) flags 114 | in fromJust $ case finalizePkg ghcVer db fa gpd of 115 | Right (pd, fa) -> Just $ createCblPkg pd fa 116 | Left _ -> Nothing 117 | 118 | finalizeToUnsatisfiableDeps ghcVer db flags pkg@(RepoType gpd) = 119 | let fa = fromMaybe [] $ lookup (getName pkg) flags 120 | in case finalizePkg ghcVer db fa gpd of 121 | Left ds -> Just (pkgNameStr (packageDescription gpd), ds) 122 | _ -> Nothing 123 | 124 | finalizeToUnsatisfiableDeps _ _ _ _ = Nothing 125 | 126 | findBreaking db (GhcType n v) = let 127 | d = checkDependants db n v 128 | in if null d 129 | then Nothing 130 | else Just ((n, v), d) 131 | findBreaking db (DistroType n v _ _) = let 132 | d = checkDependants db n v 133 | in if null d 134 | then Nothing 135 | else Just ((n, v), d) 136 | findBreaking db (RepoType gpd) = let 137 | n = pkgNameStr (packageDescription gpd) 138 | v = P.pkgVersion $ package $ packageDescription gpd 139 | d = checkDependants db n v 140 | in if null d 141 | then Nothing 142 | else Just ((n, v), d) 143 | 144 | combineFlags a b = zip keys $ mapMaybe (uncurry mappend . (\ k -> (lookup k a, lookup k b))) keys 145 | where 146 | keys = nub $ map fst (a ++ b) 147 | -------------------------------------------------------------------------------- /src/BuildPkgs.hs: -------------------------------------------------------------------------------- 1 | {- 2 | - Copyright 2011 Per Magnus Therning 3 | - 4 | - Licensed under the Apache License, Version 2.0 (the "License"); 5 | - you may not use this file except in compliance with the License. 6 | - You may obtain a copy of the License at 7 | - 8 | - http://www.apache.org/licenses/LICENSE-2.0 9 | - 10 | - Unless required by applicable law or agreed to in writing, software 11 | - distributed under the License is distributed on an "AS IS" BASIS, 12 | - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | - See the License for the specific language governing permissions and 14 | - limitations under the License. 15 | -} 16 | 17 | module BuildPkgs where 18 | 19 | import PkgDB 20 | import Util.Misc 21 | 22 | import Control.Monad.Reader 23 | 24 | buildPkgs :: Command () 25 | buildPkgs = do 26 | db <- asks (dbFile . fst) >>= liftIO . readDb 27 | pkgs <- asks $ pkgs . optsCmd . fst 28 | liftIO $ mapM_ putStrLn $ transitiveDependants db pkgs 29 | -------------------------------------------------------------------------------- /src/BumpPkgs.hs: -------------------------------------------------------------------------------- 1 | {- 2 | - Copyright 2011 Per Magnus Therning 3 | - 4 | - Licensed under the Apache License, Version 2.0 (the "License"); 5 | - you may not use this file except in compliance with the License. 6 | - You may obtain a copy of the License at 7 | - 8 | - http://www.apache.org/licenses/LICENSE-2.0 9 | - 10 | - Unless required by applicable law or agreed to in writing, software 11 | - distributed under the License is distributed on an "AS IS" BASIS, 12 | - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | - See the License for the specific language governing permissions and 14 | - limitations under the License. 15 | -} 16 | 17 | module BumpPkgs where 18 | 19 | import PkgDB 20 | import Util.Misc 21 | 22 | import Control.Monad.Reader 23 | 24 | bumpPkgs :: Command () 25 | bumpPkgs = do 26 | dbFn <- asks $ dbFile . fst 27 | db <- liftIO $ readDb dbFn 28 | dR <- asks (dryRun . fst) 29 | pkgs <- asks $ pkgs . optsCmd . fst 30 | incl <- asks $ inclusive . optsCmd . fst 31 | let bpkgs = transDependants db incl pkgs 32 | let newDb = foldl bumpRelease db bpkgs 33 | liftIO $ if dR 34 | then putStrLn "Would bump:" >> mapM_ putStrLn bpkgs 35 | else saveDb newDb dbFn 36 | 37 | transDependants db i pkgs = filter ((||) i . not . flip elem pkgs) $ transitiveDependants db pkgs 38 | -------------------------------------------------------------------------------- /src/ConvertDB.hs: -------------------------------------------------------------------------------- 1 | {- 2 | - Copyright 2011-2014 Per Magnus Therning 3 | - 4 | - Licensed under the Apache License, Version 2.0 (the "License"); 5 | - you may not use this file except in compliance with the License. 6 | - You may obtain a copy of the License at 7 | - 8 | - http://www.apache.org/licenses/LICENSE-2.0 9 | - 10 | - Unless required by applicable law or agreed to in writing, software 11 | - distributed under the License is distributed on an "AS IS" BASIS, 12 | - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | - See the License for the specific language governing permissions and 14 | - limitations under the License. 15 | -} 16 | 17 | module ConvertDB where 18 | 19 | import Util.Misc 20 | import qualified OldPkgDB as ODB 21 | import qualified PkgDB as NDB 22 | 23 | import Control.Monad.Reader 24 | import System.Directory 25 | 26 | convertDb :: Command () 27 | convertDb = do 28 | inDbFn <- asks $ inDbFile . optsCmd . fst 29 | outDbFn <- asks $ outDbFile . optsCmd . fst 30 | dbExist <- liftIO $ doesFileExist inDbFn 31 | when dbExist $ do 32 | newDb <- fmap doConvertDB (liftIO $ ODB.readDb inDbFn) 33 | liftIO $ NDB.saveDb newDb outDbFn 34 | 35 | doConvertDB :: ODB.CblDB -> NDB.CblDB 36 | doConvertDB = map doConvert 37 | where 38 | doConvert o 39 | | ODB.isGhcPkg o = NDB.createGhcPkg n v 40 | | ODB.isDistroPkg o = NDB.createDistroPkg n v x r 41 | | ODB.isRepoPkg o = NDB.createRepoPkg n v x d f r 42 | | otherwise = error "" 43 | where 44 | n = ODB.pkgName o 45 | v = ODB.pkgVersion o 46 | x = ODB.pkgXRev o 47 | d = ODB.pkgDeps o 48 | f = ODB.pkgFlags o 49 | r = ODB.pkgRelease o 50 | -------------------------------------------------------------------------------- /src/CreateConfig.hs: -------------------------------------------------------------------------------- 1 | {- 2 | - Copyright 2015 Per Magnus Therning 3 | - 4 | - Licensed under the Apache License, Version 2.0 (the "License"); 5 | - you may not use this file except in compliance with the License. 6 | - You may obtain a copy of the License at 7 | - 8 | - http://www.apache.org/licenses/LICENSE-2.0 9 | - 10 | - Unless required by applicable law or agreed to in writing, software 11 | - distributed under the License is distributed on an "AS IS" BASIS, 12 | - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | - See the License for the specific language governing permissions and 14 | - limitations under the License. 15 | -} 16 | 17 | module CreateConfig 18 | where 19 | 20 | import Util.Cfg 21 | import Util.Misc 22 | 23 | import Data.Aeson 24 | import Control.Monad 25 | import Control.Monad.IO.Class 26 | import System.Exit 27 | import System.Posix.Files 28 | 29 | createConfig :: Command () 30 | createConfig = liftIO $ do 31 | exists <- fileExist "cblrepo.cfg" 32 | if exists 33 | then putStrLn "Configuration file already exists" >> exitFailure 34 | else saveDefCfg "cblrepo.cfg" 35 | -------------------------------------------------------------------------------- /src/Extract.hs: -------------------------------------------------------------------------------- 1 | {- 2 | - Copyright 2014 - 2015 Per Magnus Therning 3 | - 4 | - Licensed under the Apache License, Version 2.0 (the "License"); 5 | - you may not use this file except in compliance with the License. 6 | - You may obtain a copy of the License at 7 | - 8 | - http://www.apache.org/licenses/LICENSE-2.0 9 | - 10 | - Unless required by applicable law or agreed to in writing, software 11 | - distributed under the License is distributed on an "AS IS" BASIS, 12 | - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | - See the License for the specific language governing permissions and 14 | - limitations under the License. 15 | -} 16 | 17 | {-# LANGUAGE FlexibleContexts #-} 18 | module Extract 19 | where 20 | 21 | import Util.HackageIndex 22 | import Util.Misc 23 | import Util.Cfg 24 | 25 | import Control.Monad.Trans.Except 26 | import Control.Monad.Reader 27 | import qualified Data.ByteString.Lazy as BSL 28 | import Data.Version 29 | import Distribution.Text (display) 30 | import System.FilePath 31 | 32 | extract :: Command () 33 | extract = do 34 | aD <- asks $ appDir . fst 35 | pkgsNVersions <- asks $ cmdExtractPkgs . optsCmd . fst 36 | cfg <- asks snd 37 | -- 38 | idx <- liftIO $ readIndexFile aD (getIndexFileName cfg) 39 | _ <- mapM (runExceptT . extractAndSave idx) pkgsNVersions >>= exitOnAnyLefts 40 | return () 41 | 42 | extractAndSave :: MonadIO m => BSL.ByteString -> (String, Version) -> ExceptT String m () 43 | extractAndSave idx (pkg, ver) = maybe (throwE errorMsg) (liftIO . BSL.writeFile destFn) (extractCabal idx pkg ver) 44 | where 45 | destFn = pkg <.> "cabal" 46 | errorMsg = "Failed to extract Cabal for " ++ pkg ++ " " ++ display ver 47 | -------------------------------------------------------------------------------- /src/ListPkgs.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE PatternGuards #-} 2 | {- 3 | - Copyright 2011 Per Magnus Therning 4 | - 5 | - Licensed under the Apache License, Version 2.0 (the "License"); 6 | - you may not use this file except in compliance with the License. 7 | - You may obtain a copy of the License at 8 | - 9 | - http://www.apache.org/licenses/LICENSE-2.0 10 | - 11 | - Unless required by applicable law or agreed to in writing, software 12 | - distributed under the License is distributed on an "AS IS" BASIS, 13 | - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | - See the License for the specific language governing permissions and 15 | - limitations under the License. 16 | -} 17 | 18 | module ListPkgs where 19 | 20 | import Util.Misc 21 | import PkgDB 22 | 23 | import Control.Monad.Reader 24 | import Distribution.Text 25 | import Distribution.PackageDescription 26 | 27 | listPkgs :: Command () 28 | listPkgs = do 29 | lG <- asks $ listGhc . optsCmd . fst 30 | lD <- asks $ listDistro . optsCmd . fst 31 | lR <- asks $ noListRepo . optsCmd . fst 32 | lF <- asks $ listFmt . optsCmd . fst 33 | ps <- asks $ pkgs . optsCmd . fst 34 | db <- asks (dbFile . fst) >>= liftIO . readDb 35 | let allPkgs = filter (pkgFilter lG lD lR) db 36 | let pkgsToList = if null ps 37 | then allPkgs 38 | else filter (\p -> pkgName p `elem` ps) allPkgs 39 | let printer = case lF of 40 | CmdListShortFmt -> printCblPkgShort 41 | CmdListNormalFmt -> printCblPkgNormal 42 | CmdListHackageFmt -> printCblPkgHackage 43 | liftIO $ mapM_ printer pkgsToList 44 | 45 | pkgFilter :: Bool -> Bool -> Bool -> CblPkg -> Bool 46 | pkgFilter g d r p = (g && isGhcPkg p) || (d && isDistroPkg p) || (not r && isRepoPkg p) 47 | 48 | printCblPkgShort :: CblPkg -> IO () 49 | printCblPkgShort p = putStrLn $ (pkgName p) ++ "," ++ (display $ pkgVersion p) 50 | 51 | printCblPkgNormal :: CblPkg -> IO () 52 | printCblPkgNormal p = 53 | putStrLn $ pkgName p ++ " " ++ v ++ "-" ++ r ++ showFlagsIfPresent p 54 | where 55 | v = display (pkgVersion p) ++ if (not $ isGhcPkg p) then (".x" ++ show (pkgXRev p)) else "" 56 | r = if isGhcPkg p then "xx" else show (pkgRelease p) 57 | showFlagsIfPresent _p 58 | | [] <- pkgFlags _p = "" 59 | | fa <- pkgFlags _p = " (" ++ unwords (map showSingleFlag fa) ++ ")" 60 | showSingleFlag (FlagName n, True) = n 61 | showSingleFlag (FlagName n, False) = '-' : n 62 | 63 | printCblPkgHackage :: CblPkg -> IO () 64 | printCblPkgHackage p = 65 | print (pkgName p, display $ pkgVersion p, Nothing :: Maybe String) 66 | -------------------------------------------------------------------------------- /src/Main.hs: -------------------------------------------------------------------------------- 1 | {- 2 | - Copyright 2011-2014 Per Magnus Therning 3 | - 4 | - Licensed under the Apache License, Version 2.0 (the "License"); 5 | - you may not use this file except in compliance with the License. 6 | - You may obtain a copy of the License at 7 | - 8 | - http://www.apache.org/licenses/LICENSE-2.0 9 | - 10 | - Unless required by applicable law or agreed to in writing, software 11 | - distributed under the License is distributed on an "AS IS" BASIS, 12 | - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | - See the License for the specific language governing permissions and 14 | - limitations under the License. 15 | -} 16 | 17 | module Main where 18 | 19 | -- {{{1 imports 20 | import Add 21 | import BuildPkgs 22 | import BumpPkgs 23 | import Update 24 | import Versions 25 | import ListPkgs 26 | import Upgrades 27 | import Util.Misc 28 | import PkgBuild 29 | import ConvertDB 30 | import Remove 31 | import Extract 32 | import CreateConfig 33 | import Util.Cfg 34 | 35 | import Paths_cblrepo 36 | 37 | import Data.Monoid 38 | import Distribution.Text 39 | import Options.Applicative as OA 40 | import System.Directory 41 | 42 | -- -- {{{1 command line arguments 43 | argAppDir, argDbFile :: Parser String 44 | argAppDir = strOption (long "appdir" <> value "" <> showDefault <> help "Path to application data directory") 45 | argDbFile = strOption (long "db" <> value "cblrepo.db" <> showDefault <> help "Path to package database") 46 | 47 | argDryRun :: Parser Bool 48 | argDryRun = switch (short 'n' <> help "Make no changes, (dry run)") 49 | 50 | cmdAddPkgOpts :: Parser Cmds 51 | cmdAddPkgOpts = CmdAdd 52 | <$> strOption (long "patchdir" <> value "patches" <> showDefault <> help "Location of patches") 53 | <*> option ghcVersionArgReader (long "ghc-version" <> value ghcDefVersion <> showDefault <> help "GHC version to use") 54 | <*> many (option ghcPkgArgReader (short 'g' <> long "ghc-pkg" <> metavar "PKG,VER" <> help "GHC base package (multiple)")) 55 | <*> many (option distroPkgArgReader (short 'd' <> long "distro-pkg" <> metavar "PKG,VER,XREV,REL" <> help "Distro package (multiple)")) 56 | <*> many (option strCblFileArgReader (short 'f' <> long "cbl-file" <> metavar "FILE[:flag,-flag]" <> help "CABAL file (multiple)")) 57 | <*> many (argument strCblPkgArgReader (metavar "PKGNAME,VERSION[:flag,-flag] ...")) 58 | 59 | cmdAddPkgCmd = command "add" (info (helper <*> cmdAddPkgOpts) (fullDesc <> progDesc "Add a package to the database")) 60 | 61 | cmdBumpPkgsCmd = command "bump" (info (helper <*> cmdBumpPkgsOpts) (fullDesc <> progDesc "Bump packages that need it after updating the named packages")) 62 | where 63 | cmdBumpPkgsOpts = CmdBumpPkgs 64 | <$> switch (long "inclusive" <> help "Include the listed packages") 65 | <*> some (strArgument (metavar "PKGNAME ...")) 66 | 67 | cmdBuildPkgsCmd = command "build" (info (helper <*> cmdBuildPkgsOpts) 68 | (fullDesc <> progDesc "Re-order packages into a good build order")) 69 | where 70 | cmdBuildPkgsOpts = CmdBuildPkgs <$> some (strArgument (metavar "PKGNAME ...")) 71 | 72 | cmdUpdateCmd = command "update" (info (helper <*> cmdUpdateOpts) (fullDesc <> progDesc "Update the index")) 73 | where 74 | cmdUpdateOpts = CmdUpdate <$> switch (internal <> hidden) 75 | 76 | cmdVersionsCmd = command "versions" (info (helper <*> cmdVersionsOpts) (fullDesc <> progDesc "List available versions of packages")) 77 | where 78 | cmdVersionsOpts = CmdVersions 79 | <$> switch (short 'l' <> long "latest" <> help "List only the latest version of packages") 80 | <*> some (strArgument (metavar "PKGNAME ...")) 81 | 82 | cmdUpgradesCmd = command "upgrades" (info (helper <*> cmdUpgradesOpts) (fullDesc <> progDesc "Check for packages that can be upgraded")) 83 | where 84 | cmdUpgradesOpts = CmdUpgrades 85 | <$> switch (short 's' <> help "A shorter output suitable for scripting") 86 | <*> switch (short 'x' <> help "Limit list to packages with new x-revision") 87 | 88 | cmdListPkgsCmd = command "list" (info (helper <*> cmdListPkgsOpts) (fullDesc <> progDesc "List packages in repo")) 89 | where 90 | cmdListPkgsOpts = CmdListPkgs 91 | <$> switch (short 'g' <> long "ghc" <> help "List ghc packages") 92 | <*> switch (short 'd' <> long "distro" <> help "List distro packages") 93 | <*> switch (long "no-repo" <> help "Do not list repo packages") 94 | <*> option listFormatReader (short 'f' <> long "format" <> value CmdListNormalFmt <> help "Output format: short, normal, hackage (default: normal)") 95 | <*> many (argument str (metavar "PKGNAME ...")) 96 | 97 | cmdPkgBuildCmd = command "pkgbuild" (info (helper <*> cmdPkgBuildOpts) (fullDesc <> progDesc "Create PKGBUILD other files necessary for an Arch package")) 98 | where 99 | cmdPkgBuildOpts = CmdPkgBuild 100 | <$> option ghcVersionArgReader (long "ghc-version" <> value ghcDefVersion <> help "GHC version to use in PKGBUILD (default: 8.0.1)") 101 | <*> option auto (long "ghc-release" <> value ghcDefRelease <> showDefault <> help "GHC release to use in PKGBUILD") 102 | <*> strOption (long "patchdir" <> value "patches" <> showDefault <> help "Location of patches") 103 | <*> some (strArgument (metavar "PKGNAME ...")) 104 | 105 | cmdConvertDbCmd = command "convertdb" (info (helper <*> cmdConvertDbOpts) (fullDesc <> progDesc "Convert an old database to the new format")) 106 | where 107 | cmdConvertDbOpts = CmdConvertDb 108 | <$> strOption (short 'i' <> long "indb" <> value "cblrepo.db" <> showDefault <> help "Old database") 109 | <*> strOption (short 'o' <> long "outdb" <> value "new-cblrepo.db" <> showDefault <> help "New database") 110 | 111 | cmdRemovePkgCmd = command "rm" (info (helper <*> cmdRemovePkgOpts) (fullDesc <> progDesc "Remove packages")) 112 | where 113 | cmdRemovePkgOpts = CmdRemovePkg <$> some (strArgument (metavar "PKGNAME ...")) 114 | 115 | cmdExtractCmd = command "extract" (info (helper <*> cmdExtractOpts) (fullDesc <> progDesc "Extract Cabal file from index")) 116 | where 117 | cmdExtractOpts = CmdExtract <$> many (argument pkgNVersionArgReader (metavar "PKGNAME,VERSION")) 118 | 119 | cmdCreateConfigCmd = command "create-config" (info (helper <*> cmdCreateConfigOpts) (fullDesc <> progDesc "Create configuration file with defaults")) 120 | where 121 | cmdCreateConfigOpts = pure CmdCreateConfig 122 | 123 | argParser = info (helper <*> opts) (fullDesc <> header (progName ++ " v" ++ display version) <> progDesc "Maintain a database of dependencies of CABAL packages") 124 | where 125 | opts = Opts 126 | <$> argAppDir <*> argDbFile <*> argDryRun 127 | <*> subparser (cmdAddPkgCmd <> cmdBumpPkgsCmd <> cmdBuildPkgsCmd <> cmdUpdateCmd <> cmdVersionsCmd <> cmdUpgradesCmd <> 128 | cmdListPkgsCmd <> cmdPkgBuildCmd <> cmdConvertDbCmd <> cmdRemovePkgCmd <> cmdExtractCmd <> cmdCreateConfigCmd) 129 | 130 | -- {{{1 main 131 | main :: IO () 132 | main = do 133 | defAppDir <- getAppUserDataDirectory progName 134 | execParser argParser >>= \ o -> do 135 | let aD = if null (appDir o) then defAppDir else appDir o 136 | createDirectoryIfMissing True aD 137 | cfg <- readCfg "cblrepo.cfg" 138 | let e = (o { appDir = aD }, cfg) 139 | case optsCmd o of 140 | CmdAdd {} -> runCommand e add 141 | CmdBuildPkgs {} -> runCommand e buildPkgs 142 | CmdBumpPkgs {} -> runCommand e bumpPkgs 143 | CmdUpdate {} -> runCommand e update 144 | CmdVersions {} -> runCommand e versions 145 | CmdListPkgs {} -> runCommand e listPkgs 146 | CmdUpgrades {} -> runCommand e upgrades 147 | CmdPkgBuild {} -> runCommand e pkgBuild 148 | CmdConvertDb {} -> runCommand e convertDb 149 | CmdRemovePkg {} -> runCommand e remove 150 | CmdExtract {} -> runCommand e extract 151 | CmdCreateConfig -> runCommand e createConfig 152 | -------------------------------------------------------------------------------- /src/OldPkgDB.hs: -------------------------------------------------------------------------------- 1 | {- 2 | - Copyright 2011-2014 Per Magnus Therning 3 | - 4 | - Licensed under the Apache License, Version 2.0 (the "License"); 5 | - you may not use this file except in compliance with the License. 6 | - You may obtain a copy of the License at 7 | - 8 | - http://www.apache.org/licenses/LICENSE-2.0 9 | - 10 | - Unless required by applicable law or agreed to in writing, software 11 | - distributed under the License is distributed on an "AS IS" BASIS, 12 | - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | - See the License for the specific language governing permissions and 14 | - limitations under the License. 15 | -} 16 | 17 | {-# LANGUAGE OverloadedStrings #-} 18 | {-# LANGUAGE ScopedTypeVariables #-} 19 | 20 | module OldPkgDB 21 | ( CblPkg 22 | , pkgName 23 | , pkgPkg 24 | , pkgVersion 25 | , pkgXRev 26 | , pkgDeps 27 | , pkgFlags 28 | , pkgRelease 29 | -- 30 | , isGhcPkg 31 | , isDistroPkg 32 | , isRepoPkg 33 | , isBasePkg 34 | -- 35 | , createGhcPkg 36 | , createDistroPkg 37 | , createRepoPkg 38 | , createCblPkg 39 | -- 40 | , CblDB 41 | , addPkg 42 | , addPkg2 43 | , delPkg 44 | , bumpRelease 45 | , lookupPkg 46 | , transitiveDependants 47 | , checkDependants 48 | , checkAgainstDb 49 | , saveDb 50 | , readDb 51 | ) where 52 | 53 | -- {{{1 imports 54 | import Control.Arrow 55 | import Control.Exception as CE 56 | import Control.Monad 57 | import Data.Aeson 58 | import qualified Data.ByteString.Lazy.Char8 as C 59 | import Data.List 60 | import Data.Maybe 61 | import qualified Distribution.Package as P 62 | import Distribution.PackageDescription 63 | import qualified Distribution.Version as V 64 | import System.IO.Error 65 | 66 | import qualified Util.Dist 67 | import OldPkgTypes 68 | 69 | -- {{{1 packages 70 | pkgName :: CblPkg -> String 71 | pkgName (CP n _) = n 72 | 73 | pkgPkg :: CblPkg -> Pkg 74 | pkgPkg (CP _ p) = p 75 | 76 | pkgVersion :: CblPkg -> V.Version 77 | pkgVersion (CP _ (GhcPkg d)) = gpVersion d 78 | pkgVersion (CP _ (DistroPkg d)) = dpVersion d 79 | pkgVersion (CP _ (RepoPkg d)) = rpVersion d 80 | 81 | pkgXRev :: CblPkg -> Int 82 | pkgXRev (CP _ (DistroPkg d)) = dpXrev d 83 | pkgXRev (CP _ (RepoPkg d)) = rpXrev d 84 | pkgXRev _ = 0 85 | 86 | pkgDeps :: CblPkg -> [P.Dependency] 87 | pkgDeps (CP _ (RepoPkg d)) = rpDeps d 88 | pkgDeps _ = [] 89 | 90 | pkgFlags :: CblPkg -> FlagAssignment 91 | pkgFlags (CP _ (RepoPkg d)) = rpFlags d 92 | pkgFlags _ = [] 93 | 94 | pkgRelease :: CblPkg -> Int 95 | pkgRelease (CP _ (GhcPkg _)) = -1 96 | pkgRelease (CP _ (DistroPkg d)) = dpRelease d 97 | pkgRelease (CP _ (RepoPkg d)) = rpRelease d 98 | 99 | createGhcPkg :: String -> V.Version -> CblPkg 100 | createGhcPkg n v = CP n (GhcPkg $ GhcPkgD v) 101 | 102 | createDistroPkg :: String -> V.Version -> Int -> Int -> CblPkg 103 | createDistroPkg n v x r = CP n (DistroPkg (DistroPkgD v x r)) 104 | 105 | createRepoPkg :: String -> V.Version -> Int -> [P.Dependency] -> FlagAssignment -> Int -> CblPkg 106 | createRepoPkg n v x d fa r = CP n (RepoPkg $ RepoPkgD v x d fa r) 107 | 108 | createCblPkg :: PackageDescription -> FlagAssignment -> CblPkg 109 | createCblPkg pd fa = createRepoPkg name version xrev deps fa 1 110 | where 111 | name = Util.Dist.pkgNameStr pd 112 | version = P.pkgVersion $ package pd 113 | xrev = Util.Dist.pkgXRev pd 114 | deps = buildDepends pd 115 | 116 | getDependencyOn :: String -> CblPkg -> Maybe P.Dependency 117 | getDependencyOn n p = find (\ d -> Util.Dist.depName d == n) (pkgDeps p) 118 | 119 | isGhcPkg :: CblPkg -> Bool 120 | isGhcPkg (CP _ GhcPkg {}) = True 121 | isGhcPkg _ = False 122 | 123 | isDistroPkg :: CblPkg -> Bool 124 | isDistroPkg (CP _ DistroPkg {}) = True 125 | isDistroPkg _ = False 126 | 127 | isRepoPkg :: CblPkg -> Bool 128 | isRepoPkg (CP _ RepoPkg {}) = True 129 | isRepoPkg _ = False 130 | 131 | isBasePkg :: CblPkg -> Bool 132 | isBasePkg = not . isRepoPkg 133 | 134 | -- {{{1 database 135 | emptyPkgDB :: CblDB 136 | emptyPkgDB = [] 137 | 138 | addPkg :: CblDB -> String -> Pkg -> CblDB 139 | addPkg db n p = nubBy cmp newdb 140 | where 141 | cmp (CP n1 _) (CP n2 _) = n1 == n2 142 | newdb = CP n p:db 143 | 144 | addPkg2 :: CblDB -> CblPkg -> CblDB 145 | addPkg2 db (CP n p) = addPkg db n p 146 | 147 | delPkg :: CblDB -> String -> CblDB 148 | delPkg db n = filter (\ p -> n /= pkgName p) db 149 | 150 | bumpRelease :: CblDB -> String -> CblDB 151 | bumpRelease db n = maybe db (addPkg2 db . doBump) (lookupPkg db n) 152 | where 153 | doBump (CP n' (RepoPkg d)) = CP n' (RepoPkg d { rpRelease = rpRelease d + 1 }) 154 | doBump p = p 155 | 156 | lookupPkg :: CblDB -> String -> Maybe CblPkg 157 | lookupPkg [] _ = Nothing 158 | lookupPkg (p:db) n 159 | | n == pkgName p = Just p 160 | | otherwise = lookupPkg db n 161 | 162 | lookupDependants :: [CblPkg] -> String -> [String] 163 | lookupDependants db n = filter (/= n) $ map pkgName $ filter (`doesDependOn` n) db 164 | where 165 | doesDependOn p n' = n' `elem` map Util.Dist.depName (pkgDeps p) 166 | 167 | transitiveDependants :: CblDB -> [String] -> [String] 168 | transitiveDependants db names = keepLast $ concatMap transUsersOfOne names 169 | where 170 | transUsersOfOne n = n : transitiveDependants db (lookupDependants db n) 171 | keepLast = reverse . nub . reverse 172 | 173 | checkDependants :: CblDB -> String -> V.Version -> [(String, Maybe P.Dependency)] 174 | checkDependants db n v = 175 | let d1 = mapMaybe (lookupPkg db) (lookupDependants db n) 176 | -- d2 = map (\ p -> (pkgName p, getDependencyOn n p)) d1 177 | d2 = map (pkgName &&& getDependencyOn n ) d1 178 | fails = filter (not . V.withinRange v . Util.Dist.depVersionRange . fromJust . snd) d2 179 | in fails 180 | 181 | checkAgainstDb :: CblDB -> String -> P.Dependency -> Bool 182 | checkAgainstDb db name dep = 183 | let dN = Util.Dist.depName dep 184 | dVR = Util.Dist.depVersionRange dep 185 | in (dN == name) || 186 | (case lookupPkg db dN of 187 | Nothing -> False 188 | Just p -> V.withinRange (pkgVersion p) dVR) 189 | 190 | readDb :: FilePath -> IO CblDB 191 | readDb fp = handle 192 | (\ e -> if isDoesNotExistError e 193 | then return emptyPkgDB 194 | else throwIO e) 195 | $ do r <- (mapM decode . C.lines) `liftM` C.readFile fp 196 | case r of 197 | Just a -> return a 198 | Nothing -> fail "JSON parsing failed" 199 | 200 | saveDb :: CblDB -> FilePath -> IO () 201 | saveDb db fp = C.writeFile fp s 202 | where 203 | s = C.unlines $ map encode $ sort db 204 | -------------------------------------------------------------------------------- /src/OldPkgTypes.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE TemplateHaskell #-} 2 | {-# LANGUAGE OverloadedStrings #-} 3 | 4 | {- 5 | - Copyright 2011-2015 Per Magnus Therning 6 | - 7 | - Licensed under the Apache License, Version 2.0 (the "License"); 8 | - you may not use this file except in compliance with the License. 9 | - You may obtain a copy of the License at 10 | - 11 | - http://www.apache.org/licenses/LICENSE-2.0 12 | - 13 | - Unless required by applicable law or agreed to in writing, software 14 | - distributed under the License is distributed on an "AS IS" BASIS, 15 | - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | - See the License for the specific language governing permissions and 17 | - limitations under the License. 18 | -} 19 | 20 | module OldPkgTypes where 21 | 22 | import Control.Applicative 23 | import Data.Aeson 24 | import Data.Aeson.Types 25 | import Data.Aeson.TH (deriveJSON) 26 | import Data.Text (unpack) 27 | import qualified Data.Version as DV 28 | import qualified Distribution.Package as P 29 | import Distribution.PackageDescription 30 | import qualified Distribution.Version as V 31 | import Text.ParserCombinators.ReadP (readP_to_S) 32 | import qualified Data.Vector as Vec 33 | 34 | data Pkg = GhcPkg GhcPkgD 35 | | DistroPkg DistroPkgD 36 | | RepoPkg RepoPkgD 37 | deriving (Eq, Show) 38 | 39 | data GhcPkgD = GhcPkgD { gpVersion :: V.Version } 40 | deriving (Eq, Show) 41 | 42 | data DistroPkgD = DistroPkgD 43 | { dpVersion :: V.Version 44 | , dpXrev :: Int 45 | , dpRelease :: Int 46 | } deriving (Eq, Show) 47 | 48 | data RepoPkgD = RepoPkgD 49 | { rpVersion :: V.Version 50 | , rpXrev :: Int 51 | , rpDeps :: [P.Dependency] 52 | , rpFlags :: FlagAssignment 53 | , rpRelease :: Int 54 | } deriving (Eq, Show) 55 | 56 | data CblPkg = CP String Pkg 57 | deriving (Eq, Show) 58 | 59 | type CblDB = [CblPkg] 60 | 61 | instance Ord CblPkg where 62 | compare (CP n1 (GhcPkg d1)) (CP n2 (GhcPkg d2)) = 63 | compare (n1, gpVersion d1) (n2, gpVersion d2) 64 | compare (CP _ GhcPkg {}) _ = LT 65 | compare _ (CP _ GhcPkg {}) = GT 66 | 67 | compare (CP n1 (DistroPkg d1)) (CP n2 (DistroPkg d2)) = 68 | compare (n1, dpVersion d1, dpRelease d1) (n2, dpVersion d2, dpRelease d2) 69 | compare (CP _ DistroPkg {}) _ = LT 70 | compare _ (CP _ DistroPkg {}) = GT 71 | 72 | compare (CP n1 (RepoPkg d1)) (CP n2 (RepoPkg d2)) = 73 | compare (n1, rpVersion d1, rpRelease d1) (n2, rpVersion d2, rpRelease d2) 74 | 75 | -- JSON instances 76 | version2Json :: V.Version -> Value 77 | version2Json = toJSON . DV.showVersion 78 | 79 | json2Version :: Value -> Parser V.Version 80 | json2Version = withText "Version" $ go . readP_to_S DV.parseVersion . unpack 81 | where 82 | go [(v,[])] = return v 83 | go (_ : xs) = go xs 84 | go _ = fail "could not parse Version" 85 | 86 | dependencyList2Json :: [P.Dependency] -> Value 87 | dependencyList2Json = toJSON . map convDep 88 | where 89 | convDep (P.Dependency (P.PackageName n) vr)= (n, versionRange2Json vr) 90 | 91 | json2DependencyList :: Value -> Parser [P.Dependency] 92 | json2DependencyList = withArray "DependencyList" parseList 93 | where 94 | parseList = mapM (withArray "Dependency" parseDep) . Vec.toList 95 | 96 | parseDep a = do 97 | n <- withText "PackageName" (return . unpack) (a Vec.! 0) 98 | vr <- json2VersionRange (a Vec.! 1) 99 | return $ P.Dependency (P.PackageName n) vr 100 | 101 | versionRange2Json :: V.VersionRange -> Value 102 | versionRange2Json = V.foldVersionRange 103 | (object ["AnyVersion" .= ([]::[(Int,Int)])]) 104 | (\ v -> object ["ThisVersion" .= version2Json v]) 105 | (\ v -> object ["LaterVersion" .= version2Json v]) 106 | (\ v -> object ["EarlierVersion" .= version2Json v]) 107 | (\ vr0 vr1 -> object ["UnionVersionRanges" .= [vr0, vr1]]) 108 | (\ vr0 vr1 -> object ["IntersectVersionRanges" .= [vr0, vr1]]) 109 | 110 | json2VersionRange :: Value -> Parser V.VersionRange 111 | json2VersionRange = withObject "VersionRange" go 112 | where 113 | go :: Object -> Parser V.VersionRange 114 | go o = 115 | V.thisVersion <$> (o .: "ThisVersion" >>= json2Version) <|> 116 | V.laterVersion <$> (o .: "LaterVersion" >>= json2Version) <|> 117 | V.earlierVersion <$> (o .: "EarlierVersion" >>= json2Version) <|> 118 | V.WildcardVersion <$> (o .: "WildcardVersion" >>= json2Version) <|> 119 | nullaryOp V.anyVersion <$> o .: "AnyVersion" <|> 120 | (o .: "UnionVersionRanges" >>= parserPair V.unionVersionRanges) <|> 121 | (o .: "IntersectVersionRanges" >>= parserPair V.intersectVersionRanges) <|> 122 | V.VersionRangeParens <$> (o .: "VersionRangeParens" >>= json2VersionRange) 123 | 124 | nullaryOp :: a -> Value -> a 125 | nullaryOp = const 126 | 127 | parserPair :: (V.VersionRange -> V.VersionRange -> V.VersionRange) -> Value -> Parser V.VersionRange 128 | parserPair f = withArray "parserPair" p 129 | where 130 | p a = do 131 | v1 <- json2VersionRange (a Vec.! 0) 132 | v2 <- json2VersionRange (a Vec.! 1) 133 | return $ f v1 v2 134 | 135 | flagAssignment2Json :: FlagAssignment -> Value 136 | flagAssignment2Json = toJSON . map convFlag 137 | where 138 | convFlag (FlagName s, b) = (s, b) 139 | 140 | json2FlagAssignment :: Value -> Parser FlagAssignment 141 | json2FlagAssignment = withArray "FlagAssignment" parseList 142 | where 143 | parseList = mapM (withArray "SingleFlag" parseFlag) . Vec.toList 144 | 145 | parseFlag a = do 146 | n <- withText "FlagName" (return . unpack) (a Vec.! 0) 147 | b <- withBool "FlagBool" return (a Vec.! 1) 148 | return (FlagName n, b) 149 | 150 | instance ToJSON GhcPkgD where 151 | toJSON (GhcPkgD v) = object ["gpVersion" .= version2Json v] 152 | 153 | instance FromJSON GhcPkgD where 154 | parseJSON = withObject "GhcPkgD" (\ o -> GhcPkgD <$> (o .: "gpVersion" >>= json2Version)) 155 | 156 | instance ToJSON DistroPkgD where 157 | toJSON (DistroPkgD v x r)= object [ "dpVersion" .= version2Json v 158 | , "dpXrev" .= x 159 | , "dpRelease" .= r 160 | ] 161 | 162 | instance FromJSON DistroPkgD where 163 | parseJSON = withObject "DistroPkgD" go 164 | where 165 | go o = do 166 | v <- o .: "dpVersion" >>= json2Version 167 | x <- o .: "dpXrev" 168 | r <- o .: "dpRelease" 169 | return $ DistroPkgD v x r 170 | 171 | instance ToJSON RepoPkgD where 172 | toJSON (RepoPkgD v x ds fs r)= object [ "rpVersion" .= version2Json v 173 | , "rpXrev" .= x 174 | , "rpDeps" .= dependencyList2Json ds 175 | , "rpFlags" .= flagAssignment2Json fs 176 | , "rpRelease" .= r 177 | ] 178 | 179 | instance FromJSON RepoPkgD where 180 | parseJSON = withObject "RepoPkgD" go 181 | where 182 | go o = do 183 | v <- o .: "rpVersion" >>= json2Version 184 | x <- o .: "rpXrev" 185 | ds <- o .: "rpDeps" >>= json2DependencyList 186 | fs <- o .: "rpFlags" >>= json2FlagAssignment 187 | r <- o .: "rpRelease" 188 | return $ RepoPkgD v x ds fs r 189 | 190 | $(deriveJSON defaultOptions { sumEncoding = ObjectWithSingleField, allNullaryToStringTag = False } ''Pkg) 191 | $(deriveJSON defaultOptions { sumEncoding = ObjectWithSingleField, allNullaryToStringTag = False } ''CblPkg) 192 | -------------------------------------------------------------------------------- /src/PkgBuild.hs: -------------------------------------------------------------------------------- 1 | {- 2 | - Copyright 2011-2014 Per Magnus Therning 3 | - 4 | - Licensed under the Apache License, Version 2.0 (the "License"); 5 | - you may not use this file except in compliance with the License. 6 | - You may obtain a copy of the License at 7 | - 8 | - http://www.apache.org/licenses/LICENSE-2.0 9 | - 10 | - Unless required by applicable law or agreed to in writing, software 11 | - distributed under the License is distributed on an "AS IS" BASIS, 12 | - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | - See the License for the specific language governing permissions and 14 | - limitations under the License. 15 | -} 16 | 17 | module PkgBuild where 18 | 19 | import PkgDB 20 | import Util.Misc 21 | import Util.Translation 22 | import qualified Util.Cabal as Cbl 23 | import Util.Cfg 24 | 25 | import Control.Arrow 26 | import Control.Monad.Reader 27 | import Control.Monad.Trans.Except 28 | import qualified Data.ByteString.Lazy as BSL (writeFile) 29 | import System.Directory 30 | import System.FilePath 31 | import System.IO 32 | import System.Unix.Directory 33 | import Text.PrettyPrint.ANSI.Leijen 34 | 35 | pkgBuild :: Command () 36 | pkgBuild = do 37 | pkgs <- asks $ pkgs . optsCmd . fst 38 | void $ mapM (runExceptT . generatePkgBuild) pkgs >>= exitOnAnyLefts 39 | 40 | generatePkgBuild :: String -> ExceptT String Command () 41 | generatePkgBuild pkg = do 42 | db <- asks (dbFile . fst) >>= liftIO . readDb 43 | patchDir <- asks $ patchDir . optsCmd . fst 44 | ghcVer <- asks $ ghcVer . optsCmd . fst 45 | ghcRel <- asks $ ghcRel . optsCmd . fst 46 | (ver, fa) <- maybe (throwE $ "Unknown package: " ++ pkg) (return . (pkgVersion &&& pkgFlags)) $ lookupPkg db pkg 47 | --- 48 | (cblFile, genericPkgDesc) <- runCabalParseWithTempDir $ Cbl.readFromIdx (pkg, ver) 49 | pkgDescAndFlags <- either (const $ throwE ("Failed to finalize package: " ++ pkg)) return 50 | (finalizePkg ghcVer db fa genericPkgDesc) 51 | let archPkg = translate ghcVer ghcRel db (snd pkgDescAndFlags) (fst pkgDescAndFlags) 52 | archPkgWPatches <- liftIO $ addPatches patchDir archPkg 53 | archPkgWHash <- withTempDirExceptT "/tmp/cblrepo." (addHashes archPkgWPatches cblFile) 54 | liftIO $ createDirectoryIfMissing False (apPkgName archPkgWHash) 55 | liftIO $ withWorkingDirectory (apPkgName archPkgWHash) $ do 56 | copyPatches "." archPkgWHash 57 | hPKGBUILD <- openFile "PKGBUILD" WriteMode 58 | hPutDoc hPKGBUILD $ pretty archPkgWHash 59 | hClose hPKGBUILD 60 | maybe (return ()) (void . runExceptT . applyPatch "PKGBUILD") 61 | (apPkgbuildPatch archPkgWHash) 62 | when (apHasLibrary archPkgWHash) $ do 63 | hInstall <- openFile (apPkgName archPkgWHash <.> "install") WriteMode 64 | let archInstall = aiFromAP archPkgWHash 65 | hPutDoc hInstall $ pretty archInstall 66 | hClose hInstall 67 | maybe (return ()) (void . runExceptT . applyPatch (apPkgName archPkgWHash <.> "install")) 68 | (apInstallPatch archPkgWHash) 69 | BSL.writeFile ("original.cabal") cblFile 70 | 71 | runCabalParseWithTempDir :: Cbl.CabalParse a -> ExceptT String Command a 72 | runCabalParseWithTempDir f = do 73 | aD <- asks (appDir . fst) 74 | pD <- asks $ patchDir . optsCmd . fst 75 | cfg <- asks snd 76 | r <- liftIO $ withTemporaryDirectory "/tmp/cblrepo." $ \ destDir -> do 77 | let cpe = Cbl.CabalParseEnv aD pD destDir (getIndexFileName cfg) 78 | Cbl.runCabalParse cpe f 79 | reThrowE r 80 | -------------------------------------------------------------------------------- /src/PkgDB.hs: -------------------------------------------------------------------------------- 1 | {- 2 | - Copyright 2011-2014 Per Magnus Therning 3 | - 4 | - Licensed under the Apache License, Version 2.0 (the "License"); 5 | - you may not use this file except in compliance with the License. 6 | - You may obtain a copy of the License at 7 | - 8 | - http://www.apache.org/licenses/LICENSE-2.0 9 | - 10 | - Unless required by applicable law or agreed to in writing, software 11 | - distributed under the License is distributed on an "AS IS" BASIS, 12 | - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | - See the License for the specific language governing permissions and 14 | - limitations under the License. 15 | -} 16 | 17 | {-# LANGUAGE OverloadedStrings #-} 18 | {-# LANGUAGE ScopedTypeVariables #-} 19 | 20 | module PkgDB 21 | ( CblPkg 22 | , pkgName 23 | , pkgPkg 24 | , pkgVersion 25 | , pkgXRev 26 | , pkgDeps 27 | , pkgFlags 28 | , pkgRelease 29 | -- 30 | , isGhcPkg 31 | , isDistroPkg 32 | , isRepoPkg 33 | , isBasePkg 34 | -- 35 | , createGhcPkg 36 | , createDistroPkg 37 | , createRepoPkg 38 | , createCblPkg 39 | -- 40 | , CblDB 41 | , addPkg 42 | , addPkg2 43 | , delPkg 44 | , bumpRelease 45 | , lookupPkg 46 | , transitiveDependants 47 | , checkDependants 48 | , checkAgainstDb 49 | , saveDb 50 | , readDb 51 | ) where 52 | 53 | -- {{{1 imports 54 | import Control.Arrow 55 | import Control.Exception as CE 56 | import Control.Monad 57 | import Data.Aeson 58 | import qualified Data.ByteString.Lazy.Char8 as C 59 | import Data.List 60 | import Data.Maybe 61 | import qualified Distribution.Package as P 62 | import Distribution.PackageDescription 63 | import qualified Distribution.Version as V 64 | import System.IO.Error 65 | 66 | import qualified Util.Dist 67 | import PkgTypes 68 | 69 | -- {{{1 packages 70 | pkgName :: CblPkg -> String 71 | pkgName (CP n _) = n 72 | 73 | pkgPkg :: CblPkg -> Pkg 74 | pkgPkg (CP _ p) = p 75 | 76 | pkgVersion :: CblPkg -> V.Version 77 | pkgVersion (CP _ (GhcPkg d)) = gpVersion d 78 | pkgVersion (CP _ (DistroPkg d)) = dpVersion d 79 | pkgVersion (CP _ (RepoPkg d)) = rpVersion d 80 | 81 | pkgXRev :: CblPkg -> Int 82 | pkgXRev (CP _ (DistroPkg d)) = dpXrev d 83 | pkgXRev (CP _ (RepoPkg d)) = rpXrev d 84 | pkgXRev _ = 0 85 | 86 | pkgDeps :: CblPkg -> [P.Dependency] 87 | pkgDeps (CP _ (RepoPkg d)) = rpDeps d 88 | pkgDeps _ = [] 89 | 90 | pkgFlags :: CblPkg -> FlagAssignment 91 | pkgFlags (CP _ (RepoPkg d)) = rpFlags d 92 | pkgFlags _ = [] 93 | 94 | pkgRelease :: CblPkg -> Int 95 | pkgRelease (CP _ (GhcPkg _)) = -1 96 | pkgRelease (CP _ (DistroPkg d)) = dpRelease d 97 | pkgRelease (CP _ (RepoPkg d)) = rpRelease d 98 | 99 | createGhcPkg :: String -> V.Version -> CblPkg 100 | createGhcPkg n v = CP n (GhcPkg $ GhcPkgD v) 101 | 102 | createDistroPkg :: String -> V.Version -> Int -> Int -> CblPkg 103 | createDistroPkg n v x r = CP n (DistroPkg (DistroPkgD v x r)) 104 | 105 | createRepoPkg :: String -> V.Version -> Int -> [P.Dependency] -> FlagAssignment -> Int -> CblPkg 106 | createRepoPkg n v x d fa r = CP n (RepoPkg $ RepoPkgD v x d fa r) 107 | 108 | createCblPkg :: PackageDescription -> FlagAssignment -> CblPkg 109 | createCblPkg pd fa = createRepoPkg name version xrev deps fa 1 110 | where 111 | name = Util.Dist.pkgNameStr pd 112 | version = P.pkgVersion $ package pd 113 | xrev = Util.Dist.pkgXRev pd 114 | deps = buildDepends pd 115 | 116 | getDependencyOn :: String -> CblPkg -> Maybe P.Dependency 117 | getDependencyOn n p = find (\ d -> Util.Dist.depName d == n) (pkgDeps p) 118 | 119 | isGhcPkg :: CblPkg -> Bool 120 | isGhcPkg (CP _ GhcPkg {}) = True 121 | isGhcPkg _ = False 122 | 123 | isDistroPkg :: CblPkg -> Bool 124 | isDistroPkg (CP _ DistroPkg {}) = True 125 | isDistroPkg _ = False 126 | 127 | isRepoPkg :: CblPkg -> Bool 128 | isRepoPkg (CP _ RepoPkg {}) = True 129 | isRepoPkg _ = False 130 | 131 | isBasePkg :: CblPkg -> Bool 132 | isBasePkg = not . isRepoPkg 133 | 134 | -- {{{1 database 135 | emptyPkgDB :: CblDB 136 | emptyPkgDB = [] 137 | 138 | addPkg :: CblDB -> String -> Pkg -> CblDB 139 | addPkg db n p = nubBy cmp newdb 140 | where 141 | cmp (CP n1 _) (CP n2 _) = n1 == n2 142 | newdb = CP n p:db 143 | 144 | addPkg2 :: CblDB -> CblPkg -> CblDB 145 | addPkg2 db (CP n p) = addPkg db n p 146 | 147 | delPkg :: CblDB -> String -> CblDB 148 | delPkg db n = filter (\ p -> n /= pkgName p) db 149 | 150 | bumpRelease :: CblDB -> String -> CblDB 151 | bumpRelease db n = maybe db (addPkg2 db . doBump) (lookupPkg db n) 152 | where 153 | doBump (CP n' (RepoPkg d)) = CP n' (RepoPkg d { rpRelease = rpRelease d + 1 }) 154 | doBump p = p 155 | 156 | lookupPkg :: CblDB -> String -> Maybe CblPkg 157 | lookupPkg [] _ = Nothing 158 | lookupPkg (p:db) n 159 | | n == pkgName p = Just p 160 | | otherwise = lookupPkg db n 161 | 162 | lookupDependants :: [CblPkg] -> String -> [String] 163 | lookupDependants db n = filter (/= n) $ map pkgName $ filter (`doesDependOn` n) db 164 | where 165 | doesDependOn p n' = n' `elem` map Util.Dist.depName (pkgDeps p) 166 | 167 | transitiveDependants :: CblDB -> [String] -> [String] 168 | transitiveDependants db names = keepLast $ concatMap transUsersOfOne names 169 | where 170 | transUsersOfOne n = n : transitiveDependants db (lookupDependants db n) 171 | keepLast = reverse . nub . reverse 172 | 173 | checkDependants :: CblDB -> String -> V.Version -> [(String, Maybe P.Dependency)] 174 | checkDependants db n v = 175 | let d1 = mapMaybe (lookupPkg db) (lookupDependants db n) 176 | -- d2 = map (\ p -> (pkgName p, getDependencyOn n p)) d1 177 | d2 = map (pkgName &&& getDependencyOn n ) d1 178 | fails = filter (not . V.withinRange v . Util.Dist.depVersionRange . fromJust . snd) d2 179 | in fails 180 | 181 | checkAgainstDb :: CblDB -> String -> P.Dependency -> Bool 182 | checkAgainstDb db name dep = 183 | let dN = Util.Dist.depName dep 184 | dVR = Util.Dist.depVersionRange dep 185 | in (dN == name) || 186 | (case lookupPkg db dN of 187 | Nothing -> False 188 | Just p -> V.withinRange (pkgVersion p) dVR) 189 | 190 | readDb :: FilePath -> IO CblDB 191 | readDb fp = handle 192 | (\ e -> if isDoesNotExistError e 193 | then return emptyPkgDB 194 | else throwIO e) 195 | $ do r <- (mapM decode . C.lines) `liftM` C.readFile fp 196 | case r of 197 | Just a -> return a 198 | Nothing -> fail "JSON parsing failed" 199 | 200 | saveDb :: CblDB -> FilePath -> IO () 201 | saveDb db fp = C.writeFile fp s 202 | where 203 | s = C.unlines $ map encode $ sort db 204 | -------------------------------------------------------------------------------- /src/PkgTypes.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE TemplateHaskell #-} 2 | {-# LANGUAGE OverloadedStrings #-} 3 | 4 | {- 5 | - Copyright 2011-2015 Per Magnus Therning 6 | - 7 | - Licensed under the Apache License, Version 2.0 (the "License"); 8 | - you may not use this file except in compliance with the License. 9 | - You may obtain a copy of the License at 10 | - 11 | - http://www.apache.org/licenses/LICENSE-2.0 12 | - 13 | - Unless required by applicable law or agreed to in writing, software 14 | - distributed under the License is distributed on an "AS IS" BASIS, 15 | - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | - See the License for the specific language governing permissions and 17 | - limitations under the License. 18 | -} 19 | 20 | module PkgTypes where 21 | 22 | import Control.Applicative 23 | import Data.Aeson 24 | import Data.Aeson.Types 25 | import Data.Aeson.TH (deriveJSON) 26 | import Data.Text (unpack) 27 | import qualified Data.Version as DV 28 | import qualified Distribution.Package as P 29 | import Distribution.PackageDescription 30 | import qualified Distribution.Version as V 31 | import Text.ParserCombinators.ReadP (readP_to_S) 32 | import qualified Data.Vector as Vec 33 | 34 | data Pkg = GhcPkg GhcPkgD 35 | | DistroPkg DistroPkgD 36 | | RepoPkg RepoPkgD 37 | deriving (Eq, Show) 38 | 39 | data GhcPkgD = GhcPkgD { gpVersion :: V.Version } 40 | deriving (Eq, Show) 41 | 42 | data DistroPkgD = DistroPkgD 43 | { dpVersion :: V.Version 44 | , dpXrev :: Int 45 | , dpRelease :: Int 46 | } deriving (Eq, Show) 47 | 48 | data RepoPkgD = RepoPkgD 49 | { rpVersion :: V.Version 50 | , rpXrev :: Int 51 | , rpDeps :: [P.Dependency] 52 | , rpFlags :: FlagAssignment 53 | , rpRelease :: Int 54 | } deriving (Eq, Show) 55 | 56 | data CblPkg = CP String Pkg 57 | deriving (Eq, Show) 58 | 59 | type CblDB = [CblPkg] 60 | 61 | instance Ord CblPkg where 62 | compare (CP n1 (GhcPkg d1)) (CP n2 (GhcPkg d2)) = 63 | compare (n1, gpVersion d1) (n2, gpVersion d2) 64 | compare (CP _ GhcPkg {}) _ = LT 65 | compare _ (CP _ GhcPkg {}) = GT 66 | 67 | compare (CP n1 (DistroPkg d1)) (CP n2 (DistroPkg d2)) = 68 | compare (n1, dpVersion d1, dpRelease d1) (n2, dpVersion d2, dpRelease d2) 69 | compare (CP _ DistroPkg {}) _ = LT 70 | compare _ (CP _ DistroPkg {}) = GT 71 | 72 | compare (CP n1 (RepoPkg d1)) (CP n2 (RepoPkg d2)) = 73 | compare (n1, rpVersion d1, rpRelease d1) (n2, rpVersion d2, rpRelease d2) 74 | 75 | -- JSON instances 76 | version2Json :: V.Version -> Value 77 | version2Json = toJSON . DV.showVersion 78 | 79 | json2Version :: Value -> Parser V.Version 80 | json2Version = withText "Version" $ go . readP_to_S DV.parseVersion . unpack 81 | where 82 | go [(v,[])] = return v 83 | go (_ : xs) = go xs 84 | go _ = fail "could not parse Version" 85 | 86 | dependencyList2Json :: [P.Dependency] -> Value 87 | dependencyList2Json = toJSON . map convDep 88 | where 89 | convDep (P.Dependency (P.PackageName n) vr)= (n, versionRange2Json vr) 90 | 91 | json2DependencyList :: Value -> Parser [P.Dependency] 92 | json2DependencyList = withArray "DependencyList" parseList 93 | where 94 | parseList = mapM (withArray "Dependency" parseDep) . Vec.toList 95 | 96 | parseDep a = do 97 | n <- withText "PackageName" (return . unpack) (a Vec.! 0) 98 | vr <- json2VersionRange (a Vec.! 1) 99 | return $ P.Dependency (P.PackageName n) vr 100 | 101 | versionRange2Json :: V.VersionRange -> Value 102 | versionRange2Json = V.foldVersionRange 103 | (object ["AnyVersion" .= ([]::[(Int,Int)])]) 104 | (\ v -> object ["ThisVersion" .= version2Json v]) 105 | (\ v -> object ["LaterVersion" .= version2Json v]) 106 | (\ v -> object ["EarlierVersion" .= version2Json v]) 107 | (\ vr0 vr1 -> object ["UnionVersionRanges" .= [vr0, vr1]]) 108 | (\ vr0 vr1 -> object ["IntersectVersionRanges" .= [vr0, vr1]]) 109 | 110 | json2VersionRange :: Value -> Parser V.VersionRange 111 | json2VersionRange = withObject "VersionRange" go 112 | where 113 | go :: Object -> Parser V.VersionRange 114 | go o = 115 | V.thisVersion <$> (o .: "ThisVersion" >>= json2Version) <|> 116 | V.laterVersion <$> (o .: "LaterVersion" >>= json2Version) <|> 117 | V.earlierVersion <$> (o .: "EarlierVersion" >>= json2Version) <|> 118 | V.WildcardVersion <$> (o .: "WildcardVersion" >>= json2Version) <|> 119 | nullaryOp V.anyVersion <$> o .: "AnyVersion" <|> 120 | (o .: "UnionVersionRanges" >>= parserPair V.unionVersionRanges) <|> 121 | (o .: "IntersectVersionRanges" >>= parserPair V.intersectVersionRanges) <|> 122 | V.VersionRangeParens <$> (o .: "VersionRangeParens" >>= json2VersionRange) 123 | 124 | nullaryOp :: a -> Value -> a 125 | nullaryOp = const 126 | 127 | parserPair :: (V.VersionRange -> V.VersionRange -> V.VersionRange) -> Value -> Parser V.VersionRange 128 | parserPair f = withArray "parserPair" p 129 | where 130 | p a = do 131 | v1 <- json2VersionRange (a Vec.! 0) 132 | v2 <- json2VersionRange (a Vec.! 1) 133 | return $ f v1 v2 134 | 135 | flagAssignment2Json :: FlagAssignment -> Value 136 | flagAssignment2Json = toJSON . map convFlag 137 | where 138 | convFlag (FlagName s, b) = (s, b) 139 | 140 | json2FlagAssignment :: Value -> Parser FlagAssignment 141 | json2FlagAssignment = withArray "FlagAssignment" parseList 142 | where 143 | parseList = mapM (withArray "SingleFlag" parseFlag) . Vec.toList 144 | 145 | parseFlag a = do 146 | n <- withText "FlagName" (return . unpack) (a Vec.! 0) 147 | b <- withBool "FlagBool" return (a Vec.! 1) 148 | return (FlagName n, b) 149 | 150 | instance ToJSON GhcPkgD where 151 | toJSON (GhcPkgD v) = object ["gpVersion" .= version2Json v] 152 | 153 | instance FromJSON GhcPkgD where 154 | parseJSON = withObject "GhcPkgD" (\ o -> GhcPkgD <$> (o .: "gpVersion" >>= json2Version)) 155 | 156 | instance ToJSON DistroPkgD where 157 | toJSON (DistroPkgD v x r)= object [ "dpVersion" .= version2Json v 158 | , "dpXrev" .= x 159 | , "dpRelease" .= r 160 | ] 161 | 162 | instance FromJSON DistroPkgD where 163 | parseJSON = withObject "DistroPkgD" go 164 | where 165 | go o = do 166 | v <- o .: "dpVersion" >>= json2Version 167 | x <- o .: "dpXrev" 168 | r <- o .: "dpRelease" 169 | return $ DistroPkgD v x r 170 | 171 | instance ToJSON RepoPkgD where 172 | toJSON (RepoPkgD v x ds fs r)= object [ "rpVersion" .= version2Json v 173 | , "rpXrev" .= x 174 | , "rpDeps" .= dependencyList2Json ds 175 | , "rpFlags" .= flagAssignment2Json fs 176 | , "rpRelease" .= r 177 | ] 178 | 179 | instance FromJSON RepoPkgD where 180 | parseJSON = withObject "RepoPkgD" go 181 | where 182 | go o = do 183 | v <- o .: "rpVersion" >>= json2Version 184 | x <- o .: "rpXrev" 185 | ds <- o .: "rpDeps" >>= json2DependencyList 186 | fs <- o .: "rpFlags" >>= json2FlagAssignment 187 | r <- o .: "rpRelease" 188 | return $ RepoPkgD v x ds fs r 189 | 190 | $(deriveJSON defaultOptions { sumEncoding = ObjectWithSingleField, allNullaryToStringTag = False } ''Pkg) 191 | $(deriveJSON defaultOptions { sumEncoding = ObjectWithSingleField, allNullaryToStringTag = False } ''CblPkg) 192 | -------------------------------------------------------------------------------- /src/Remove.hs: -------------------------------------------------------------------------------- 1 | {- 2 | - Copyright 2011 Per Magnus Therning 3 | - 4 | - Licensed under the Apache License, Version 2.0 (the "License"); 5 | - you may not use this file except in compliance with the License. 6 | - You may obtain a copy of the License at 7 | - 8 | - http://www.apache.org/licenses/LICENSE-2.0 9 | - 10 | - Unless required by applicable law or agreed to in writing, software 11 | - distributed under the License is distributed on an "AS IS" BASIS, 12 | - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | - See the License for the specific language governing permissions and 14 | - limitations under the License. 15 | -} 16 | 17 | module Remove where 18 | 19 | -- {{{1 imports 20 | -- {{{1 local 21 | import PkgDB 22 | import Util.Misc 23 | 24 | -- {{{1 system 25 | import Control.Monad 26 | import Control.Monad.Trans 27 | import Control.Monad.Reader (asks) 28 | import System.Exit 29 | 30 | -- {{{1 remove 31 | remove :: Command () 32 | remove = do 33 | dbFn <- asks (dbFile . fst) 34 | db <- liftIO $ readDb dbFn 35 | pkgs <- asks $ pkgs . optsCmd . fst 36 | dR <- asks (dryRun . fst) 37 | liftIO $ either 38 | (\ s -> putStrLn s >> exitFailure) 39 | (\ newDb -> unless dR $ saveDb newDb dbFn) 40 | (foldM removeOne db pkgs) 41 | 42 | removeOne :: CblDB -> String -> Either String CblDB 43 | removeOne db pkg = let 44 | deps = tail $ transitiveDependants db [pkg] 45 | depsString = foldr (\ s t -> " " ++ s ++ "\n" ++ t) "" deps 46 | in if null deps 47 | then return (delPkg db pkg) 48 | else fail ("Can't delete package " ++ pkg ++ ", dependants:\n" ++ depsString) 49 | -------------------------------------------------------------------------------- /src/Update.hs: -------------------------------------------------------------------------------- 1 | {- 2 | - Copyright 2011-2013 Per Magnus Therning 3 | - 4 | - Licensed under the Apache License, Version 2.0 (the "License"); 5 | - you may not use this file except in compliance with the License. 6 | - You may obtain a copy of the License at 7 | - 8 | - http://www.apache.org/licenses/LICENSE-2.0 9 | - 10 | - Unless required by applicable law or agreed to in writing, software 11 | - distributed under the License is distributed on an "AS IS" BASIS, 12 | - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | - See the License for the specific language governing permissions and 14 | - limitations under the License. 15 | -} 16 | 17 | module Update where 18 | 19 | import Util.Misc 20 | import Util.Cfg 21 | 22 | import Control.Monad.Reader 23 | import System.FilePath 24 | 25 | update :: Command () 26 | update = do 27 | aD <- asks $ appDir . fst 28 | cfg <- asks snd 29 | let idxFn = getIndexFileName cfg 30 | liftIO $ getFromURL (cfgIdxUrl cfg) (aD idxFn) 31 | -------------------------------------------------------------------------------- /src/Upgrades.hs: -------------------------------------------------------------------------------- 1 | {- 2 | - Copyright 2011-2014 Per Magnus Therning 3 | - 4 | - Licensed under the Apache License, Version 2.0 (the "License"); 5 | - you may not use this file except in compliance with the License. 6 | - You may obtain a copy of the License at 7 | - 8 | - http://www.apache.org/licenses/LICENSE-2.0 9 | - 10 | - Unless required by applicable law or agreed to in writing, software 11 | - distributed under the License is distributed on an "AS IS" BASIS, 12 | - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | - See the License for the specific language governing permissions and 14 | - limitations under the License. 15 | -} 16 | 17 | module Upgrades 18 | ( upgrades 19 | ) where 20 | 21 | import PkgDB 22 | import Util.Misc 23 | import Util.HackageIndex 24 | import Util.Cfg 25 | 26 | import Control.Applicative 27 | import Control.Arrow 28 | import Control.Monad.Reader 29 | import Data.Maybe 30 | import Distribution.Text 31 | import Distribution.Version hiding (thisVersion) 32 | 33 | upgrades :: Command () 34 | upgrades = do 35 | db <- asks (dbFile . fst) >>= liftIO . readDb 36 | aD <- asks $ appDir . fst 37 | aCS <- asks $ idxStyle .optsCmd . fst 38 | xrevsOnly <- asks $ xrevs . optsCmd . fst 39 | cfg <- asks snd 40 | availPkgsNVers <- liftIO $ buildPkgVersions <$> readIndexFile aD (getIndexFileName cfg) 41 | let nonBasePkgs = filter (not . isBasePkg) db 42 | pkgsNVers = map (pkgName &&& pkgVersion &&& pkgXRev) nonBasePkgs 43 | filterFunc = if xrevsOnly 44 | then (\ (p, (v, x)) -> maybe False (> x) (snd <$> thisVersion availPkgsNVers p v)) 45 | else (\ (p, vx) -> maybe False (> vx) (latestVersion availPkgsNVers p undefined)) 46 | outdated = filter filterFunc pkgsNVers 47 | printer = if aCS 48 | then printNewShort 49 | else printUpgrades 50 | if xrevsOnly 51 | then liftIO $ mapM_ (printer thisVersion availPkgsNVers) outdated 52 | else liftIO $ mapM_ (printer latestVersion availPkgsNVers) outdated 53 | 54 | printUpgrades verFunc avail (pkgName, (pkgVer, pkgXRev)) = putStrLn $ 55 | pkgName ++ ": " ++ display pkgVer ++ ":x" ++ show pkgXRev ++ " (" ++ display lv ++ ":x" ++ show lx ++ ")" 56 | where 57 | (lv, lx) = fromJust $ verFunc avail pkgName pkgVer 58 | 59 | printNewShort verFunc avail (pkgName, (pkgVer, _)) = putStrLn $ 60 | pkgName ++ "," ++ display l 61 | where 62 | l = fst $ fromJust $ verFunc avail pkgName pkgVer 63 | -------------------------------------------------------------------------------- /src/Util/Cabal.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE FlexibleContexts #-} 2 | {- 3 | - Copyright 2014 Per Magnus Therning 4 | - 5 | - Licensed under the Apache License, Version 2.0 (the "License"); 6 | - you may not use this file except in compliance with the License. 7 | - You may obtain a copy of the License at 8 | - 9 | - http://www.apache.org/licenses/LICENSE-2.0 10 | - 11 | - Unless required by applicable law or agreed to in writing, software 12 | - distributed under the License is distributed on an "AS IS" BASIS, 13 | - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | - See the License for the specific language governing permissions and 15 | - limitations under the License. 16 | -} 17 | 18 | module Util.Cabal 19 | where 20 | 21 | import Util.HackageIndex 22 | 23 | import Control.Monad.Trans.Except 24 | import Control.Monad.Reader 25 | import Distribution.Package 26 | import Distribution.PackageDescription 27 | import Distribution.PackageDescription.Parse 28 | import Distribution.Text 29 | import Distribution.Verbosity 30 | import Distribution.Version 31 | import System.Directory 32 | import System.Exit 33 | import System.FilePath 34 | import System.Posix 35 | import System.Process 36 | 37 | import qualified Data.ByteString.Lazy as BSL 38 | 39 | data CabalParseEnv = CabalParseEnv 40 | { cpeAppDir :: FilePath 41 | , cpePatchDir :: FilePath 42 | , cpeDestDir :: FilePath 43 | , cpeIdxFn :: FilePath 44 | } deriving (Eq, Show) 45 | 46 | -- | Type used for reading Cabal files 47 | type CabalParse a = ReaderT CabalParseEnv (ExceptT String IO) a 48 | 49 | -- | Run a Cabal parse action in the provided environment. 50 | runCabalParse :: CabalParseEnv -> CabalParse a -> IO (Either String a) 51 | runCabalParse cpe f = runExceptT $ runReaderT f cpe 52 | 53 | runCabalParseE :: CabalParseEnv -> CabalParse a -> (ExceptT String IO) a 54 | runCabalParseE cpe f = runReaderT f cpe 55 | 56 | readFromFile :: FilePath -- ^ file name 57 | -> CabalParse (BSL.ByteString, GenericPackageDescription) 58 | readFromFile fn = do 59 | destDir <- asks cpeDestDir 60 | let destFn = destDir takeFileName fn 61 | liftIO $ copyFile fn destFn 62 | cblFile <- patch destFn 63 | gpd <- liftIO $ readPackageDescription silent destFn 64 | return (cblFile, gpd) 65 | 66 | readFromIdx :: (String, Version) -- ^ package name and version 67 | -> CabalParse (BSL.ByteString, GenericPackageDescription) 68 | readFromIdx (pN, pV) = do 69 | destDir <- asks cpeDestDir 70 | appDir <- asks cpeAppDir 71 | idxFn <- asks cpeIdxFn 72 | let destFn = destDir pN <.> ".cabal" 73 | lift $ copyCabal appDir idxFn destFn 74 | cblFile <- patch destFn 75 | gpd <- liftIO $ readPackageDescription silent destFn 76 | return (cblFile, gpd) 77 | 78 | where 79 | copyCabal appDir idxFn destFn = do 80 | idx <- liftIO $ readIndexFile appDir idxFn 81 | cbl <- maybe (throwE $ "Failed to extract contents for " ++ pN ++ " " ++ display pV) return 82 | (extractCabal idx pN pV) 83 | liftIO $ BSL.writeFile destFn cbl 84 | 85 | -- | Patch a Cabal file. 86 | -- 87 | -- The name of the patch file is based on the name of the Cabal package: @.patch@. The file is patched only if a patch with the correct name 89 | -- is found in the patch directory (provided via 'CabalParseEnv'). 90 | patch :: FilePath -- ^ file to patch 91 | -> CabalParse BSL.ByteString 92 | patch fn = do 93 | pkgName <- liftIO $ extractName fn 94 | patchDir <- asks cpePatchDir 95 | let patchFn = patchDir pkgName <.> "cabal" 96 | lift $ applyPatchIfExist fn patchFn 97 | liftIO $ BSL.readFile fn 98 | 99 | where 100 | extractName fn = liftM name $ readPackageDescription silent fn 101 | name = display . pkgName . package . packageDescription 102 | 103 | applyPatchIfExist origFn patchFn = 104 | liftIO (fileExist patchFn) >>= flip when (applyPatch origFn patchFn) 105 | 106 | applyPatch origFn patchFn = do 107 | (ec, _, _) <- liftIO $ readProcessWithExitCode "patch" [origFn, patchFn] "" 108 | case ec of 109 | ExitSuccess -> return () 110 | ExitFailure _ -> 111 | throwE ("Failed patching " ++ origFn ++ " with " ++ patchFn) 112 | -------------------------------------------------------------------------------- /src/Util/Cfg.hs: -------------------------------------------------------------------------------- 1 | {- 2 | - Copyright 2015 Per Magnus Therning 3 | - 4 | - Licensed under the Apache License, Version 2.0 (the "License"); 5 | - you may not use this file except in compliance with the License. 6 | - You may obtain a copy of the License at 7 | - 8 | - http://www.apache.org/licenses/LICENSE-2.0 9 | - 10 | - Unless required by applicable law or agreed to in writing, software 11 | - distributed under the License is distributed on an "AS IS" BASIS, 12 | - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | - See the License for the specific language governing permissions and 14 | - limitations under the License. 15 | -} 16 | 17 | {-# LANGUAGE TemplateHaskell #-} 18 | 19 | module Util.Cfg 20 | ( Cfg(..) 21 | , readCfg 22 | , saveDefCfg 23 | , getIndexFileName 24 | ) where 25 | 26 | import Control.Monad 27 | import Data.Aeson 28 | import Data.Aeson.TH (deriveJSON, defaultOptions, Options(..), SumEncoding(..)) 29 | import qualified Data.ByteString.Lazy as BSL 30 | import Data.Maybe 31 | import System.Posix.Files 32 | 33 | data Cfg = Cfg { cfgIdxUrl :: String } 34 | deriving (Show) 35 | 36 | defCfg = Cfg "http://hackage.fpcomplete.com/00-index.tar.gz" 37 | 38 | readCfg :: FilePath -> IO Cfg 39 | readCfg fn = do 40 | exists <- fileExist fn 41 | if exists 42 | then (fromMaybe defCfg . decode) `fmap` BSL.readFile fn 43 | else return defCfg 44 | 45 | saveDefCfg :: FilePath -> IO () 46 | saveDefCfg fn = BSL.writeFile fn (encode defCfg) 47 | 48 | getIndexFileName :: Cfg -> String 49 | getIndexFileName cfg = map repSlash $ drop 7 $ cfgIdxUrl cfg 50 | where 51 | repSlash c = if c == '/' then '_' else c 52 | 53 | $(deriveJSON defaultOptions { sumEncoding = ObjectWithSingleField, allNullaryToStringTag = False } ''Cfg) 54 | -------------------------------------------------------------------------------- /src/Util/Dist.hs: -------------------------------------------------------------------------------- 1 | {- 2 | - Copyright 2014 Per Magnus Therning 3 | - 4 | - Licensed under the Apache License, Version 2.0 (the "License"); 5 | - you may not use this file except in compliance with the License. 6 | - You may obtain a copy of the License at 7 | - 8 | - http://www.apache.org/licenses/LICENSE-2.0 9 | - 10 | - Unless required by applicable law or agreed to in writing, software 11 | - distributed under the License is distributed on an "AS IS" BASIS, 12 | - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | - See the License for the specific language governing permissions and 14 | - limitations under the License. 15 | -} 16 | 17 | module Util.Dist 18 | ( depName 19 | , depVersionRange 20 | , pkgNameStr 21 | , pkgXRev 22 | ) where 23 | 24 | import Distribution.Package 25 | import Distribution.PackageDescription 26 | import Distribution.Version 27 | 28 | -- | Extract the name of a 'Dependency'. 29 | depName :: Dependency -> String 30 | depName (Dependency (PackageName n) _) = n 31 | 32 | -- | Extract the version range from a 'Dependency'. 33 | depVersionRange :: Dependency -> VersionRange 34 | depVersionRange (Dependency _ vr) = vr 35 | 36 | -- | Get the name from a 'Package', i.e. various variants of package 37 | -- descriptions. 38 | pkgNameStr :: Package pkg => pkg -> String 39 | pkgNameStr p = n 40 | where 41 | (PackageName n) = packageName p 42 | 43 | -- | Get the "x-revision" from a 'PackageDescription'. 44 | pkgXRev :: PackageDescription -> Int 45 | pkgXRev p = maybe 0 read $ lookup "x-revision" (customFieldsPD p) 46 | -------------------------------------------------------------------------------- /src/Util/HackageIndex.hs: -------------------------------------------------------------------------------- 1 | {- 2 | - Copyright 2014 Per Magnus Therning 3 | - 4 | - Licensed under the Apache License, Version 2.0 (the "License"); 5 | - you may not use this file except in compliance with the License. 6 | - You may obtain a copy of the License at 7 | - 8 | - http://www.apache.org/licenses/LICENSE-2.0 9 | - 10 | - Unless required by applicable law or agreed to in writing, software 11 | - distributed under the License is distributed on an "AS IS" BASIS, 12 | - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | - See the License for the specific language governing permissions and 14 | - limitations under the License. 15 | -} 16 | 17 | module Util.HackageIndex 18 | where 19 | 20 | import Util.Misc 21 | import Util.Dist 22 | import Util.Cfg 23 | 24 | import qualified Codec.Archive.Tar as Tar 25 | import qualified Codec.Compression.GZip as GZip 26 | import Control.Applicative 27 | import qualified Data.ByteString as BS 28 | import qualified Data.ByteString.Lazy as BSL 29 | import qualified Data.ByteString.Lazy.Search as BSLS 30 | import qualified Data.ByteString.Lazy.UTF8 as BSLU 31 | import Data.List 32 | import qualified Data.Map as M 33 | import Data.Maybe 34 | import Data.Version 35 | import Distribution.PackageDescription 36 | import Distribution.PackageDescription.Parse 37 | import Distribution.Text 38 | import System.FilePath 39 | import Safe (atMay) 40 | 41 | readIndexFile :: FilePath -> FilePath -> IO BSL.ByteString 42 | readIndexFile indexLocation indexFilename = exitOnException 43 | "Cannot open index file, have you run the 'update' command?" 44 | (BSL.readFile $ indexLocation indexFilename) 45 | 46 | type PkgVersions = M.Map String [(Version, Int)] 47 | 48 | -- | Build up a map of packages and their versions. The versions are listed in 49 | -- descending order, i.e. `head` contains the latest version. 50 | buildPkgVersions :: BSL.ByteString -- ^ the index 51 | -> PkgVersions 52 | buildPkgVersions idx = createPkgVerMap M.empty entries 53 | where 54 | entries = Tar.read $ GZip.decompress idx 55 | 56 | createPkgVerMap acc (Tar.Next e es) = case ver of 57 | Nothing -> createPkgVerMap acc es 58 | Just ver -> createPkgVerMap (M.insertWith (++) (head parts) [(ver, xrev)] acc) es 59 | where 60 | parts = splitDirectories (Tar.entryPath e) 61 | ver = if length parts == 3 then parts `atMay` 1 >>= simpleParse 62 | else Nothing 63 | content = case Tar.entryContent e of 64 | Tar.NormalFile c _ -> Just $ BSLU.toString c 65 | _ -> Nothing 66 | xrev = case parsePackageDescription <$> content of 67 | Just (ParseOk _ gpd) -> pkgXRev (packageDescription gpd) 68 | _ -> 0 69 | 70 | createPkgVerMap acc Tar.Done = acc 71 | createPkgVerMap _ (Tar.Fail _) = undefined 72 | 73 | latestVersion :: PkgVersions 74 | -> String -- ^ package name 75 | -> Version 76 | -> Maybe (Version, Int) 77 | latestVersion pnv pkg _ = last . sort <$> M.lookup pkg pnv 78 | 79 | thisVersion :: PkgVersions 80 | -> String -- ^ package name 81 | -> Version 82 | -> Maybe (Version, Int) -- ^ xrev 83 | thisVersion pnvs pkgName pkgVer = do 84 | vers <- M.lookup pkgName pnvs 85 | find (\ (v, _) -> v == pkgVer) vers 86 | 87 | extractCabal :: BSL.ByteString -- ^ the index 88 | -> String -- ^ package name 89 | -> Version -- ^ package version 90 | -> Maybe BSL.ByteString 91 | extractCabal idx pkg ver = fmap dosToUnix $ getContent entries 92 | where 93 | entries = Tar.read $ GZip.decompress idx 94 | pkgPath = pkg display ver pkg <.> "cabal" 95 | 96 | dosToUnix bs = BSLS.replace (BS.pack [0xd, 0xa]) (BSL.pack [0xa]) bs 97 | 98 | getContent (Tar.Next e es) 99 | | pkgPath == Tar.entryPath e = 100 | case Tar.entryContent e of 101 | Tar.NormalFile c _ -> Just c 102 | _ -> Nothing 103 | | otherwise = getContent es 104 | 105 | getContent _ = Nothing 106 | 107 | getRevision :: BSL.ByteString -- ^ the index 108 | -> String -- ^ package name 109 | -> Version -- ^ package version 110 | -> Maybe Int 111 | getRevision idx pkg ver = do 112 | cblStr <- BSLU.toString <$> extractCabal idx pkg ver 113 | case parsePackageDescription cblStr of 114 | ParseOk _ gpd -> maybe (return 0) (return . read) $ lookup "x-revision" (customFieldsPD $ packageDescription gpd) 115 | _ -> fail "no good" 116 | -------------------------------------------------------------------------------- /src/Util/Misc.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE FlexibleContexts #-} 2 | {- 3 | - Copyright 2011-2015 Per Magnus Therning 4 | - 5 | - Licensed under the Apache License, Version 2.0 (the "License"); 6 | - you may not use this file except in compliance with the License. 7 | - You may obtain a copy of the License at 8 | - 9 | - http://www.apache.org/licenses/LICENSE-2.0 10 | - 11 | - Unless required by applicable law or agreed to in writing, software 12 | - distributed under the License is distributed on an "AS IS" BASIS, 13 | - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | - See the License for the specific language governing permissions and 15 | - limitations under the License. 16 | -} 17 | 18 | module Util.Misc where 19 | 20 | -- {{{1 imports 21 | import qualified PkgDB as DB 22 | import Util.Dist 23 | import Util.Cfg 24 | 25 | import Control.Exception (onException) 26 | import Control.Monad 27 | import Control.Monad.Trans.Except 28 | import Control.Monad.Reader 29 | import Data.Either 30 | import Data.Version 31 | import Distribution.Compiler 32 | import Distribution.PackageDescription 33 | import Distribution.PackageDescription.Configuration 34 | import Distribution.System 35 | import Distribution.Text 36 | import Options.Applicative 37 | import Options.Applicative.Types 38 | import Safe (lastMay) 39 | import System.Exit 40 | import System.IO 41 | import System.Posix.Files 42 | import System.Process 43 | import System.Unix.Directory 44 | import Text.ParserCombinators.ReadP hiding (many) 45 | 46 | -- {{{ print functions 47 | printUnSat (n, ds) = do 48 | putStrLn $ "Failed to satisfy the following dependencies for " ++ n ++ ":" 49 | mapM_ (putStrLn . (" " ++) . display) ds 50 | 51 | printBrksOth ((n, v), brks) = do 52 | putStrLn $ "Adding " ++ n ++ " " ++ display v ++ " would break:" 53 | mapM_ (\ (bN, Just bD) -> putStrLn $ " " ++ bN ++ " : " ++ display bD) brks 54 | 55 | -- {{{1 program variables 56 | progName = "cblrepo" 57 | dbName = progName ++ ".db" 58 | 59 | ghcDefVersion = Version [8, 0, 2] [] 60 | ghcDefRelease = 1 :: Int 61 | ghcVersionDep :: Version -> Int -> String 62 | ghcVersionDep ghcVer ghcRel = "ghc=" ++ display ghcVer ++ "-" ++ show ghcRel 63 | 64 | -- {{{1 command line parser helpers 65 | readPkgNVersion :: ReadP (String, Version) 66 | readPkgNVersion = do 67 | n <- many (satisfy (/= ',')) 68 | char ',' 69 | v <- parseVersion 70 | return (n, v) 71 | 72 | readFlag :: ReadP (FlagName, Bool) 73 | readFlag = readNegFlag <++ readPosFlag 74 | where 75 | readNegFlag = do 76 | char '-' 77 | n <- many (satisfy (/= ',')) 78 | return (FlagName n, False) 79 | 80 | readPosFlag = do 81 | n0 <- get 82 | n <- many (satisfy (/= ',')) 83 | return (FlagName (n0 : n), True) 84 | 85 | ghcVersionArgReader :: ReadM Version 86 | ghcVersionArgReader = do 87 | arg <- readerAsk 88 | case lastMay $ readP_to_S parseVersion arg of 89 | Just (v, "") -> return v 90 | _ -> fail $ "cannot parse value `" ++ arg ++ "`" 91 | 92 | pkgNVersionArgReader :: ReadM (String, Version) 93 | pkgNVersionArgReader = do 94 | s <- readerAsk 95 | case lastMay (readP_to_S readPkgNVersion s) of 96 | Just (r, "") -> return r 97 | _ -> fail $ "Cannot parse '" ++ s ++ "' as PKG,VER" 98 | 99 | ghcPkgArgReader :: ReadM (String, Version) 100 | ghcPkgArgReader = pkgNVersionArgReader 101 | 102 | distroPkgArgReader :: ReadM (String, Version, Int, Int) 103 | distroPkgArgReader = let 104 | readDistroPkg = do 105 | (n, v) <- readPkgNVersion 106 | char ',' 107 | x <- many (satisfy (/= ',')) 108 | char ',' 109 | r <- many (satisfy (/= ',')) 110 | return (n, v, read x, read r) 111 | 112 | in do 113 | s <- readerAsk 114 | case lastMay (readP_to_S readDistroPkg s) of 115 | Just (r, "") -> return r 116 | _ -> fail $ "Cannot parse '" ++ s ++ "' as PKG,VER,XREV,REL" 117 | 118 | strCblFileArgReader :: ReadM (FilePath, FlagAssignment) 119 | strCblFileArgReader = let 120 | readWithFlags = do 121 | fn <- many $ satisfy (/= ':') 122 | char ':' 123 | fas <- sepBy readFlag (char ',') 124 | return (fn, fas) 125 | 126 | readWithoutFlags = do 127 | fn <- many $ satisfy (/= ':') 128 | return (fn, []) 129 | 130 | in do 131 | s <-readerAsk 132 | case lastMay (readP_to_S (readWithFlags <++ readWithoutFlags) s) of 133 | Just (r, "") -> return r 134 | _ -> fail $ "Cannot parse '" ++ s ++ "' as FILE[:FLAG,-FLAG,..]" 135 | 136 | strCblPkgArgReader :: ReadM (String, Version, FlagAssignment) 137 | strCblPkgArgReader = let 138 | readWithFlags = do 139 | (n, v) <- readPkgNVersion 140 | char ':' 141 | fas <- sepBy readFlag (char ',') 142 | return (n, v, fas) 143 | 144 | readWithoutFlags = do 145 | (n, v) <- readPkgNVersion 146 | return (n, v, []) 147 | 148 | in do 149 | s <- readerAsk 150 | case lastMay (readP_to_S (readWithFlags <++ readWithoutFlags) s) of 151 | Just (r, "") -> return r 152 | _ -> fail $ "Cannot parse: " ++ s 153 | 154 | listFormatReader :: ReadM CmdListFormat 155 | listFormatReader = do 156 | s <- readerAsk 157 | case s of 158 | "normal" -> return CmdListNormalFmt 159 | "short" -> return CmdListShortFmt 160 | "hackage" -> return CmdListHackageFmt 161 | _ -> fail $ "Cannot parse: " ++ s 162 | 163 | -- {{{1 command line argument type 164 | data CmdListFormat = CmdListNormalFmt | CmdListShortFmt | CmdListHackageFmt 165 | deriving (Eq, Show) 166 | 167 | data Cmds 168 | = CmdAdd 169 | { patchDir :: FilePath, ghcVer :: Version, cmdAddGhcPkgs :: [(String, Version)] 170 | , cmdAddDistroPkgs :: [(String, Version, Int, Int)], cmdAddFileCbls :: [(FilePath, FlagAssignment)] 171 | , cmdAddCbls :: [(String, Version, FlagAssignment)] } 172 | | CmdBuildPkgs { pkgs :: [String] } 173 | | CmdBumpPkgs { inclusive :: Bool, pkgs :: [String] } 174 | | CmdUpdate { unused :: Bool } 175 | | CmdVersions { latest :: Bool, pkgs :: [String] } 176 | | CmdListPkgs 177 | { listGhc :: Bool, listDistro :: Bool, noListRepo :: Bool 178 | , listFmt :: CmdListFormat, pkgs :: [String] } 179 | | CmdUpgrades { idxStyle :: Bool, xrevs :: Bool } 180 | | CmdPkgBuild { ghcVer :: Version, ghcRel :: Int, patchDir :: FilePath, pkgs :: [String] } 181 | | CmdConvertDb { inDbFile :: FilePath, outDbFile :: FilePath } 182 | | CmdRemovePkg { pkgs :: [String] } 183 | | CmdExtract { cmdExtractPkgs :: [(String, Version)] } 184 | | CmdCreateConfig 185 | deriving (Show) 186 | 187 | data Opts = Opts 188 | { appDir :: FilePath 189 | , dbFile :: FilePath 190 | , dryRun :: Bool 191 | , optsCmd :: Cmds 192 | } deriving (Show) 193 | 194 | -- {{{1 getFromURL 195 | getFromURL url fn = do 196 | (ec, _, er) <- readProcessWithExitCode "curl" ["-f", "-L", "-o", fn, url] "" 197 | case ec of 198 | ExitSuccess -> return () 199 | ExitFailure _ -> do 200 | hPutStrLn stderr ("Failed downloading " ++ url) 201 | hPutStrLn stderr er 202 | exitFailure 203 | 204 | -- {{{1 applyPatchIfExist 205 | applyPatch origFilename patchFilename = do 206 | (ec, _, _) <- liftIO $ readProcessWithExitCode "patch" [origFilename, patchFilename] "" 207 | case ec of 208 | ExitSuccess -> return () 209 | ExitFailure _ -> 210 | throwE ("Failed patching " ++ origFilename ++ " with " ++ patchFilename) 211 | 212 | applyPatchIfExist origFilename patchFilename = 213 | liftIO (fileExist patchFilename) >>= flip when (applyPatch origFilename patchFilename) 214 | 215 | -- {{{1 finalising package descriptions 216 | finalizePkg ghcVersion db fa gpd = finalizePackageDescription 217 | fa 218 | (DB.checkAgainstDb db n) 219 | (Platform X86_64 buildOS) -- platform 220 | (unknownCompilerInfo (CompilerId GHC ghcVersion) NoAbiTag) -- compiler version 221 | [] -- no additional constraints 222 | gpd 223 | where 224 | n = pkgNameStr gpd 225 | 226 | -- {{{1 Command type 227 | type Command = ReaderT (Opts, Cfg) IO 228 | 229 | runCommand cmds func = runReaderT func cmds 230 | 231 | -- {{{1 ExceptT 232 | reThrowE :: Monad m => Either a b -> ExceptT a m b 233 | reThrowE = either throwE return 234 | 235 | withTempDirExceptT :: MonadIO m => FilePath -> (FilePath -> ExceptT e IO b) -> ExceptT e m b 236 | withTempDirExceptT fp func = do 237 | r <- liftIO $ withTemporaryDirectory fp (runExceptT . func) 238 | reThrowE r 239 | 240 | exitOnAnyLefts vs = let 241 | es = lefts vs 242 | in 243 | if not $ null $ lefts vs 244 | then liftIO $ mapM_ (hPutStrLn stderr) es >> exitFailure 245 | else return (rights vs) 246 | 247 | exitOnException msg a = onException a $ hPutStrLn stderr msg >> exitFailure 248 | 249 | -- {{{1 shell escaping 250 | shEsc :: String -> String 251 | shEsc s = concatMap escChar s 252 | where 253 | escChar '\"' = "\\\"" 254 | escChar '`' = "\\`" 255 | escChar '\'' = "\\\'" 256 | escChar c = [c] 257 | -------------------------------------------------------------------------------- /src/Util/Translation.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE FlexibleContexts #-} 2 | {-# LANGUAGE RecordWildCards #-} 3 | {- 4 | - Copyright 2011 Per Magnus Therning 5 | - 6 | - Licensed under the Apache License, Version 2.0 (the "License"); 7 | - you may not use this file except in compliance with the License. 8 | - You may obtain a copy of the License at 9 | - 10 | - http://www.apache.org/licenses/LICENSE-2.0 11 | - 12 | - Unless required by applicable law or agreed to in writing, software 13 | - distributed under the License is distributed on an "AS IS" BASIS, 14 | - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | - See the License for the specific language governing permissions and 16 | - limitations under the License. 17 | -} 18 | 19 | module Util.Translation where 20 | 21 | import PkgDB as DB 22 | import Util.Misc 23 | import Util.Dist 24 | 25 | import Control.Monad 26 | import Control.Monad.Trans 27 | import Control.Monad.Trans.Except 28 | import qualified Data.ByteString.Lazy as BSL (writeFile) 29 | import Data.Char 30 | import Data.List 31 | import Data.Maybe 32 | import Data.Monoid hiding ((<>)) 33 | import Data.Version 34 | import Distribution.Package as P 35 | import Distribution.PackageDescription as PD 36 | import Distribution.Text 37 | import Prelude hiding ( (<$>) ) 38 | import System.Directory 39 | import System.Exit 40 | import System.FilePath 41 | import System.IO 42 | import System.Process 43 | import System.Unix.Directory 44 | import Text.PrettyPrint.ANSI.Leijen hiding(()) 45 | 46 | -- {{{1 ShQuotedString 47 | newtype ShQuotedString = ShQuotedString String 48 | deriving (Eq, Show) 49 | 50 | instance Pretty ShQuotedString where 51 | pretty (ShQuotedString s) = char '"' <> text quotStr <> char '"' 52 | where 53 | quotStr = shEsc $ unwords $ lines s 54 | 55 | -- {{{1 ShArray 56 | newtype ShArray = ShArray [String] 57 | deriving (Eq, Show) 58 | 59 | instance Monoid ShArray where 60 | mempty = ShArray [] 61 | (ShArray a) `mappend` (ShArray b) = ShArray $ a ++ b 62 | 63 | instance Pretty ShArray where 64 | pretty (ShArray a) = encloseSep (char '(') (char ')') (char ' ') (map (\ t -> char '"' <> text t <> char '"') a) 65 | 66 | -- {{{1 ShVar 67 | data ShVar a = ShVar String a 68 | deriving (Eq, Show) 69 | 70 | {-# ANN shVarNewValue "HLint: ignore Eta reduce" #-} 71 | shVarNewValue (ShVar n _) v = ShVar n v 72 | shVarAppendValue (ShVar n v1) v2 = ShVar n (v1 `mappend` v2) 73 | shVarValue (ShVar _ v) = v 74 | shVarName (ShVar n _) = n 75 | shVarRefName (ShVar n _) = "${" ++ n ++ "}" 76 | 77 | instance Pretty a => Pretty (ShVar a) where 78 | pretty (ShVar n v) = text n <> char '=' <> pretty v 79 | 80 | -- {{{1 ArchPkg 81 | data ArchPkg = ArchPkg 82 | { apPkgName :: String 83 | , apHkgName :: String 84 | , apHasLibrary :: Bool 85 | , apLicenseFile :: Maybe FilePath 86 | , apCabalPatch :: Maybe FilePath 87 | , apPkgbuildPatch :: Maybe FilePath 88 | , apInstallPatch :: Maybe FilePath 89 | , apBuildPatch :: Maybe FilePath 90 | -- shell bits 91 | , apShHkgName :: ShVar String 92 | , apShPkgName :: ShVar String 93 | , apShPkgVer :: ShVar Version 94 | , apShPkgRel :: ShVar Int 95 | , apShXRev :: ShVar String 96 | , apShPkgDesc :: ShVar ShQuotedString 97 | , apShUrl :: ShVar ShQuotedString 98 | , apShLicence :: ShVar ShArray 99 | , apShMakeDepends :: ShVar ShArray 100 | , apShDepends :: ShVar ShArray 101 | , apShSource :: ShVar ShArray 102 | , apShInstall :: Maybe (ShVar ShQuotedString) 103 | , apShSha256Sums :: ShVar ShArray 104 | , apFlags :: FlagAssignment 105 | } deriving (Eq, Show) 106 | 107 | -- {{{2 baseArchPkg 108 | baseArchPkg = ArchPkg 109 | { apPkgName = "" 110 | , apHkgName = "" 111 | , apHasLibrary = False 112 | , apLicenseFile = Nothing 113 | , apCabalPatch = Nothing 114 | , apPkgbuildPatch = Nothing 115 | , apInstallPatch = Nothing 116 | , apBuildPatch = Nothing 117 | , apShHkgName = ShVar "_hkgname" "" 118 | , apShPkgName = ShVar "pkgname" "" 119 | , apShPkgVer = ShVar "_ver" (Version [] []) 120 | , apShPkgRel = ShVar "pkgrel" 0 121 | , apShXRev = ShVar "_xrev" "0" 122 | , apShPkgDesc = ShVar "pkgdesc" (ShQuotedString "") 123 | , apShUrl = ShVar "url" (ShQuotedString "http://hackage.haskell.org/package/${_hkgname}") 124 | , apShLicence = ShVar "license" (ShArray []) 125 | , apShMakeDepends = ShVar "makedepends" (ShArray []) 126 | , apShDepends = ShVar "depends" (ShArray []) 127 | , apShSource = ShVar "source" (ShArray ["http://hackage.haskell.org/packages/archive/${_hkgname}/${_ver}/${_hkgname}-${_ver}.tar.gz", "original.cabal"]) 128 | , apShInstall = Just $ ShVar "install" (ShQuotedString "${pkgname}.install") 129 | -- this is a trick to make sure that the user's setting for integrity 130 | -- checking in makepkg.conf isn't used, as long as this array contains 131 | -- something non-empty it will overrule 132 | , apShSha256Sums = ShVar "sha256sums" (ShArray ["0"]) 133 | , apFlags = [] 134 | } 135 | 136 | -- {{{2 Pretty instance 137 | instance Pretty ArchPkg where 138 | pretty (ArchPkg {..}) 139 | = vsep [ text "# custom variables" 140 | , pretty apShHkgName 141 | , maybe empty (pretty . ShVar "_licensefile") apLicenseFile 142 | , pretty apShPkgVer 143 | , pretty apShXRev 144 | , empty, text "# PKGBUILD options/directives" 145 | , pretty apShPkgName 146 | , text "pkgver=${_ver}.x${_xrev}" 147 | , pretty apShPkgRel 148 | , pretty apShPkgDesc 149 | , pretty apShUrl 150 | , pretty apLicenseFile 151 | , text "arch=('i686' 'x86_64')" 152 | , pretty apShMakeDepends 153 | , pretty apShDepends 154 | , text "options=('strip' 'staticlibs')" 155 | , pretty apShSource 156 | , maybe empty pretty apInstallPatch 157 | , pretty apShSha256Sums 158 | , empty, text "# PKGBUILD functions" 159 | , empty 160 | , prepareFunction 161 | , empty 162 | , if apHasLibrary then libBuildFunction else exeBuildFunction 163 | , empty 164 | , if apHasLibrary then libPackageFunction else exePackageFunction 165 | , empty 166 | ] 167 | where 168 | prepareFunction = text "prepare() {" <> 169 | nest 4 (empty <$> 170 | text "cd \"${srcdir}/${_hkgname}-${_ver}\"" <$> 171 | text "cp \"${srcdir}/original.cabal\" \"${srcdir}/${_hkgname}-${_ver}/${_hkgname}.cabal\"" <$> 172 | empty <$> 173 | maybe (text "# no source patch") (\ _ -> 174 | text "patch -p4 < \"${srcdir}/source.patch\"") 175 | apBuildPatch 176 | ) <$> 177 | char '}' 178 | libBuildFunction = text "build() {" <> 179 | nest 4 (empty <$> 180 | text "cd \"${srcdir}/${_hkgname}-${_ver}\"" <$> 181 | empty <$> 182 | nest 4 (text "runhaskell Setup configure -O --enable-library-profiling --enable-shared \\" <$> 183 | text "--prefix=/usr --docdir=\"/usr/share/doc/${pkgname}\" \\" <$> 184 | text "--libsubdir=\\$compiler/site-local/\\$pkgid" <> confFlags) <$> 185 | text "runhaskell Setup build" <$> 186 | text "runhaskell Setup haddock --hoogle --html" <$> 187 | text "runhaskell Setup register --gen-script" <$> 188 | text "runhaskell Setup unregister --gen-script" <$> 189 | text "sed -i -r -e \"s|ghc-pkg.*unregister[^ ]* |&'--force' |\" unregister.sh" 190 | ) <$> 191 | char '}' 192 | 193 | exeBuildFunction = text "build() {" <> 194 | nest 4 (empty <$> text "cd \"${srcdir}/${_hkgname}-${_ver}\"" <$> 195 | empty <$> 196 | text "runhaskell Setup configure -O --prefix=/usr --docdir=\"/usr/share/doc/${pkgname}\"" <> confFlags <$> 197 | text "runhaskell Setup build" 198 | ) <$> 199 | char '}' 200 | 201 | confFlags = if null apFlags 202 | then empty 203 | else text " \\" <> 204 | nest 4 (empty <$> (hsep . map (uncurry (<>)) $ zip (repeat $ text "-f") (map prettyFlag apFlags))) 205 | 206 | libPackageFunction = text "package() {" <> 207 | nest 4 (empty <$> 208 | text "cd \"${srcdir}/${_hkgname}-${_ver}\"" <$> 209 | empty <$> 210 | text "install -D -m744 register.sh \"${pkgdir}/usr/share/haskell/${pkgname}/register.sh\"" <$> 211 | text "install -m744 unregister.sh \"${pkgdir}/usr/share/haskell/${pkgname}/unregister.sh\"" <$> 212 | text "install -d -m755 \"${pkgdir}/usr/share/doc/ghc/html/libraries\"" <$> 213 | text "ln -s \"/usr/share/doc/${pkgname}/html\" \"${pkgdir}/usr/share/doc/ghc/html/libraries/${_hkgname}\"" <$> 214 | text "runhaskell Setup copy --destdir=\"${pkgdir}\"" <$> 215 | maybe empty (\ _ -> text "install -D -m644 \"${_licensefile}\" \"${pkgdir}/usr/share/licenses/${pkgname}/LICENSE\"" <$> 216 | text "rm -f \"${pkgdir}/usr/share/doc/${pkgname}/${_licensefile}\"") apLicenseFile 217 | ) <$> 218 | char '}' 219 | 220 | exePackageFunction = text "package() {" <> 221 | nest 4 (empty <$> text "cd \"${srcdir}/${_hkgname}-${_ver}\"" <$> 222 | text "runhaskell Setup copy --destdir=\"${pkgdir}\"" 223 | ) <$> 224 | char '}' 225 | 226 | -- {{{1 ArchInstall 227 | data ArchInstall = ArchInstall 228 | { aiShPkgName :: ShVar String 229 | } deriving (Eq, Show) 230 | 231 | -- {{{2 baseArchInstall 232 | baseArchInstall = ArchInstall 233 | { aiShPkgName = ShVar "pkgname" "" 234 | } 235 | 236 | -- {{{2 ArchInstall from ArchPackage 237 | aiFromAP (ArchPkg { apShPkgName = pkgName }) = 238 | ArchInstall { aiShPkgName = pkgName } 239 | 240 | -- {{{2 Pretty instance 241 | instance Pretty ArchInstall where 242 | pretty (ArchInstall 243 | { aiShPkgName = pkgName 244 | }) = vsep 245 | [ text "# custom variables" 246 | , pretty pkgName 247 | , pretty (ShVar "HS_DIR" "usr/share/haskell/${pkgname}") 248 | , empty, text "# functions" 249 | , postInstallFunction 250 | , empty, preUpgradeFunction 251 | , empty, postUpgradeFunction 252 | , empty, preRemoveFunction 253 | , empty 254 | ] 255 | where 256 | postInstallFunction = text "post_install() {" <> 257 | nest 4 (empty <$> text "${HS_DIR}/register.sh --verbose=0") <$> 258 | char '}' 259 | preUpgradeFunction = text "pre_upgrade() {" <> 260 | nest 4 (empty <$> text "${HS_DIR}/unregister.sh --verbose=0") <$> 261 | char '}' 262 | postUpgradeFunction = text "post_upgrade() {" <> 263 | nest 4 (empty <$> text "${HS_DIR}/register.sh --verbose=0") <$> 264 | char '}' 265 | preRemoveFunction = text "pre_remove() {" <> 266 | nest 4 (empty <$> text "${HS_DIR}/unregister.sh --verbose=0") <$> 267 | char '}' 268 | 269 | -- {{{1 extra instances 270 | instance Pretty Version where 271 | pretty (Version b _) = encloseSep empty empty (char '.') (map pretty b) 272 | 273 | prettyFlag (FlagName n, True) = text n 274 | prettyFlag (FlagName n, False) = text $ '-' : n 275 | 276 | -- {{{1 translate 277 | -- TODO: 278 | -- • translation of extraLibDepends-libs to Arch packages 279 | translate :: Version -> Int -> CblDB -> FlagAssignment -> PackageDescription -> ArchPkg 280 | translate ghcVer ghcRel db fa pd = let 281 | ap = baseArchPkg 282 | (PackageName hkgName) = packageName pd 283 | pkgVer = packageVersion pd 284 | pkgRel = maybe 1 pkgRelease (lookupPkg db hkgName) 285 | xrev = show $ Util.Dist.pkgXRev pd 286 | hasLib = isJust (library pd) 287 | licFn = let l = licenseFiles pd in if null l then Nothing else Just (head l) 288 | archName = (if hasLib then "haskell-" else "") ++ map toLower hkgName 289 | pkgDesc = synopsis pd 290 | url = if null (homepage pd) then "http://hackage.haskell.org/package/${_hkgname}" else homepage pd 291 | lic = display (license pd) 292 | makeDepends = if hasLib then [] else ghcVersionDep ghcVer ghcRel : calcExactDeps db pd 293 | depends = if hasLib then ghcVersionDep ghcVer ghcRel : calcExactDeps db pd else [] 294 | extraLibDepends = maybe [] (extraLibs . libBuildInfo) (library pd) 295 | install = if hasLib then apShInstall ap else Nothing 296 | in ap 297 | { apPkgName = archName 298 | , apHkgName = hkgName 299 | , apHasLibrary = hasLib 300 | , apLicenseFile = licFn 301 | , apShHkgName = shVarNewValue (apShHkgName ap) hkgName 302 | , apShPkgName = shVarNewValue (apShPkgName ap) archName 303 | , apShPkgVer = shVarNewValue (apShPkgVer ap) pkgVer 304 | , apShPkgRel = shVarNewValue (apShPkgRel ap) pkgRel 305 | , apShXRev = shVarNewValue (apShXRev ap) xrev 306 | , apShPkgDesc = shVarNewValue (apShPkgDesc ap) (ShQuotedString pkgDesc) 307 | , apShUrl = shVarNewValue (apShUrl ap) (ShQuotedString url) 308 | , apShLicence = shVarNewValue (apShLicence ap) (ShArray [lic]) 309 | , apShMakeDepends = shVarNewValue (apShMakeDepends ap) (ShArray makeDepends) 310 | , apShDepends = shVarNewValue (apShDepends ap) (ShArray $ depends ++ extraLibDepends) 311 | , apShInstall = install 312 | , apFlags = fa 313 | } 314 | 315 | -- Calculate exact dependencies based on the package in a CblDB. We assume the 316 | -- same db has been used to finalise the package so it's all right to use 317 | -- fromJust. 318 | -- TODO: 319 | -- • this is most likely too simplistic to create the Arch package names 320 | -- correctly for all possible dependencies 321 | calcExactDeps db pd = map depString deps 322 | where 323 | remPkgs = map DB.pkgName (filter isGhcPkg db) ++ [pkgNameStr pd] 324 | deps = filter (not . (`elem` remPkgs)) (map depName (buildDepends pd)) 325 | 326 | depString n = "haskell-" ++ name ++ "=" ++ ver ++ ".x" ++ xrev ++ "-" ++ rel 327 | where 328 | pkg = fromJust $ lookupPkg db n 329 | name = map toLower $ DB.pkgName pkg 330 | ver = display $ DB.pkgVersion pkg 331 | xrev = show $ DB.pkgXRev pkg 332 | rel = show $ pkgRelease pkg 333 | 334 | -- {{{1 stuff with patches 335 | -- {{{2 addPatches 336 | addPatches patchDir ap = let 337 | hkgName = apHkgName ap 338 | sources = apShSource ap 339 | fi tF fF v = if v then tF else fF 340 | cabalPatchFn = patchDir hkgName <.> "cabal" 341 | pkgbuildPatchFn = patchDir hkgName <.> "pkgbuild" 342 | installPatchFn = patchDir hkgName <.> "install" 343 | buildPatchFn = patchDir hkgName <.> "source" 344 | in do 345 | cabalPatch <- doesFileExist cabalPatchFn >>= fi (liftM Just $ canonicalizePath cabalPatchFn) (return Nothing) 346 | pkgBuildPatch <- doesFileExist pkgbuildPatchFn >>= fi (liftM Just $ canonicalizePath pkgbuildPatchFn) (return Nothing) 347 | installPatch <- doesFileExist installPatchFn >>= fi (liftM Just $ canonicalizePath installPatchFn) (return Nothing) 348 | buildPatch <- doesFileExist buildPatchFn >>= fi (liftM Just $ canonicalizePath buildPatchFn) (return Nothing) 349 | let sources' = shVarAppendValue sources 350 | (ShArray $ catMaybes [maybe Nothing (const $ Just "source.patch") buildPatch]) 351 | return ap 352 | { apCabalPatch = cabalPatch 353 | , apPkgbuildPatch = pkgBuildPatch 354 | , apInstallPatch = installPatch 355 | , apBuildPatch = buildPatch 356 | , apShSource = sources' 357 | } 358 | 359 | -- {{{2 copyPatches 360 | copyPatches destDir ap = let 361 | cabalPatch = apCabalPatch ap 362 | buildPatch = apBuildPatch ap 363 | in do 364 | maybe (return ()) (\ fn -> copyFile fn (destDir "cabal.patch")) cabalPatch 365 | maybe (return ()) (\ fn -> copyFile fn (destDir "source.patch")) buildPatch 366 | 367 | -- {{{1 addHashes 368 | addHashes ap cbl tmpDir = let 369 | hashes = map (filter (`elem` "1234567890abcdef")) . lines . drop 11 370 | pkgbuildFn = tmpDir "PKGBUILD" 371 | installFn = tmpDir apPkgName ap <.> "install" 372 | cblFn = tmpDir "original.cabal" 373 | pkgbuildPatch = apPkgbuildPatch ap 374 | in do 375 | liftIO $ copyPatches tmpDir ap 376 | liftIO $ withFile pkgbuildFn WriteMode (`hPutDoc` pretty ap) 377 | liftIO $ withFile installFn WriteMode (`hPutDoc` (pretty $ aiFromAP ap)) 378 | maybe (return ()) (void . applyPatch pkgbuildFn) pkgbuildPatch 379 | liftIO $ BSL.writeFile cblFn cbl 380 | (ec, out, _) <- liftIO $ withWorkingDirectory tmpDir (readProcessWithExitCode "makepkg" ["-g"] "") 381 | case ec of 382 | ExitFailure _ -> 383 | throwE $ "makepkg: error while calculating the source hashes for " ++ apHkgName ap 384 | ExitSuccess -> 385 | return (if "sha256sums=(" `isPrefixOf` out then replaced else ap) 386 | where 387 | replaced = ap { apShSha256Sums = shVarNewValue (apShSha256Sums ap) (ShArray $ hashes out) } 388 | -------------------------------------------------------------------------------- /src/Versions.hs: -------------------------------------------------------------------------------- 1 | {- 2 | - Copyright 2011-2013 Per Magnus Therning 3 | - 4 | - Licensed under the Apache License, Version 2.0 (the "License"); 5 | - you may not use this file except in compliance with the License. 6 | - You may obtain a copy of the License at 7 | - 8 | - http://www.apache.org/licenses/LICENSE-2.0 9 | - 10 | - Unless required by applicable law or agreed to in writing, software 11 | - distributed under the License is distributed on an "AS IS" BASIS, 12 | - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | - See the License for the specific language governing permissions and 14 | - limitations under the License. 15 | -} 16 | 17 | module Versions where 18 | 19 | import Util.HackageIndex 20 | import Util.Misc 21 | import Util.Cfg 22 | 23 | import Control.Applicative 24 | import Control.Monad.Reader 25 | import Data.Map as M 26 | import Distribution.Text 27 | import Distribution.Version 28 | import Data.List 29 | 30 | versions :: Command () 31 | versions = do 32 | aD <- asks (appDir . fst) 33 | l <- asks $ latest . optsCmd . fst 34 | pkgs <- asks $ pkgs . optsCmd . fst 35 | cfg <- asks snd 36 | let printFunc = if l then printLatestVersion else printAllVersions 37 | liftIO $ do 38 | pkgsNVers <- buildPkgVersions <$> readIndexFile aD (getIndexFileName cfg) 39 | mapM_ (\ pkg -> printFunc (pkg, reverse . sort <$> M.lookup pkg pkgsNVers)) pkgs 40 | 41 | printAllVersions :: (String, Maybe [(Version, Int)]) -> IO () 42 | printAllVersions (p, Nothing) = putStrLn $ p ++ ": No such package" 43 | printAllVersions (p, Just vs) = putStrLn $ p ++ ": " ++ versions 44 | where 45 | versions = unwords $ display . fst <$> vs 46 | 47 | printLatestVersion :: (String, Maybe [(Version, Int)]) -> IO () 48 | printLatestVersion (p, Nothing) = putStrLn $ p ++ ": No such package" 49 | printLatestVersion (p, Just vs) = putStrLn $ p ++ "," ++ display (fst $ head vs) 50 | -------------------------------------------------------------------------------- /tests/00sync.test: -------------------------------------------------------------------------------- 1 | # a test to ensure that there is an up-to-date index of packages 2 | 3 | cblrepo update 4 | >>>= 0 5 | -------------------------------------------------------------------------------- /tests/add.test: -------------------------------------------------------------------------------- 1 | # a few tests to verify the add command, run with `shelltest -w` 2 | 3 | # {{{1 set-up 4 | rm -f cblrepo.db 5 | >>>= 0 6 | 7 | cblrepo list 8 | >>> 9 | >>>= 0 10 | 11 | # {{{1 adding ghc and distro packages 12 | cblrepo add -g foo,1.1 13 | >>>= 0 14 | 15 | ls cblrepo.db 16 | >>> 17 | cblrepo.db 18 | >>>= 0 19 | 20 | # ghc packages aren't listed by default 21 | cblrepo list 22 | >>> 23 | >>>= 0 24 | 25 | cblrepo list -g 26 | >>> 27 | foo 1.1-xx 28 | >>>= 0 29 | 30 | cblrepo add -d bar,1.2,0,1 31 | >>> 32 | >>>= 0 33 | 34 | # distro packages aren't listed by default 35 | cblrepo list 36 | >>> 37 | >>>= 0 38 | 39 | cblrepo list -d 40 | >>> 41 | bar 1.2.x0-1 42 | >>>= 0 43 | 44 | # {{{1 add from hackage 45 | # add a non-existing package from hackage 46 | cblrepo add dataenc,0.13.1 47 | >>> 48 | >>>2 49 | Failed to extract contents for dataenc 0.13.1 50 | >>>= 1 51 | 52 | # add a package from hackage (without its deps) 53 | # **** this really should be fixed!!!! 54 | cblrepo add dataenc,0.14.0.3 55 | >>> 56 | Failed to satisfy the following dependencies for dataenc: 57 | array >=0.1.0 && <0.5 58 | base >=3.0.0 && <4.6 59 | containers >=0.1.0 && <0.5 60 | >>>2 61 | >>>= 0 62 | 63 | # add a package from hackage (with its deps) 64 | cblrepo add -g array,0.3 -g base,4.0 -g containers,0.4 dataenc,0.14.0.3 65 | >>> 66 | >>>2 67 | >>>= 0 68 | 69 | cblrepo list 70 | >>> 71 | dataenc 0.14.0.3.x0-1 (-buildtests) 72 | >>>= 0 73 | 74 | # {{{1 add from file 75 | # add a package from a file 76 | cblrepo add -f tests/data/foo.cabal 77 | >>> 78 | >>>= 0 79 | 80 | cblrepo list 81 | >>> 82 | dataenc 0.14.0.3.x0-1 (-buildtests) 83 | foo 1.2.3.x3-1 84 | >>>= 0 85 | 86 | cblrepo add -f tests/data/bar.cabal:foo,-bar 87 | >>> 88 | >>>= 0 89 | 90 | cblrepo list 91 | >>> 92 | bar 0.x0-1 93 | dataenc 0.14.0.3.x0-1 (-buildtests) 94 | foo 1.2.3.x3-1 95 | >>>= 0 96 | 97 | # {{{1 check ordering of db 98 | cblrepo list -g -d 99 | >>> 100 | array 0.3-xx 101 | base 4.0-xx 102 | containers 0.4-xx 103 | bar 0.x0-1 104 | dataenc 0.14.0.3.x0-1 (-buildtests) 105 | foo 1.2.3.x3-1 106 | >>>= 0 107 | 108 | # {{{1 clean-up 109 | # a bit of cleanup at the end 110 | rm cblrepo.db 111 | >>>= 0 112 | -------------------------------------------------------------------------------- /tests/build.test: -------------------------------------------------------------------------------- 1 | # a few tests to verify the build command, run with shelltestrunner 2 | 3 | # ensure things are clean 4 | rm -f cblrepo.db 5 | >>>= 0 6 | 7 | cblrepo add -f tests/data/foo.cabal -f tests/data/bar.cabal -f tests/data/baz.cabal 8 | >>>= 0 9 | 10 | cblrepo build baz 11 | >>> 12 | baz 13 | >>>= 0 14 | 15 | cblrepo build bar 16 | >>> 17 | bar 18 | baz 19 | >>>= 0 20 | 21 | cblrepo build foo 22 | >>> 23 | foo 24 | bar 25 | baz 26 | >>>= 0 27 | 28 | # clean up 29 | rm cblrepo.db 30 | >>>= 0 31 | -------------------------------------------------------------------------------- /tests/bump.test: -------------------------------------------------------------------------------- 1 | # a few tests to verify the bump command, run with shelltestrunner 2 | 3 | # {{{1 set up 4 | rm -f cblrepo.db 5 | >>>= 0 6 | 7 | cblrepo add -f tests/data/foo.cabal -f tests/data/bar.cabal -f tests/data/baz.cabal 8 | >>>= 0 9 | 10 | cblrepo list 11 | >>> 12 | bar 0.x0-1 13 | baz 1.x0-1 14 | foo 1.2.3.x3-1 15 | >>>= 0 16 | 17 | # {{{1 bump single 18 | cblrepo bump baz 19 | >>> 20 | >>>= 0 21 | 22 | cblrepo list 23 | >>> 24 | bar 0.x0-1 25 | baz 1.x0-1 26 | foo 1.2.3.x3-1 27 | >>>= 0 28 | 29 | cblrepo bump --inclusive baz 30 | >>> 31 | >>>= 0 32 | 33 | cblrepo list 34 | >>> 35 | bar 0.x0-1 36 | baz 1.x0-2 37 | foo 1.2.3.x3-1 38 | >>>= 0 39 | 40 | # {{{1 bump multiple 41 | cblrepo bump foo 42 | >>> 43 | >>>= 0 44 | 45 | cblrepo list 46 | >>> 47 | bar 0.x0-2 48 | baz 1.x0-3 49 | foo 1.2.3.x3-1 50 | >>>= 0 51 | 52 | cblrepo bump --inclusive foo 53 | >>> 54 | >>>= 0 55 | 56 | cblrepo list 57 | >>> 58 | bar 0.x0-3 59 | baz 1.x0-4 60 | foo 1.2.3.x3-2 61 | >>>= 0 62 | 63 | # {{{1 clean up 64 | rm -f cblrepo.db 65 | >>>= 0 66 | -------------------------------------------------------------------------------- /tests/config.test: -------------------------------------------------------------------------------- 1 | # tests for the configuration 2 | 3 | rm -f cblrepo.cfg 4 | >>>= 0 5 | 6 | cblrepo create-config 7 | >>>= 0 8 | 9 | md5sum cblrepo.cfg 10 | >>> 11 | 12db6b8b91c0ac81fa1743f7f9ddf3ef cblrepo.cfg 12 | >>>= 0 13 | 14 | cblrepo create-config 15 | >>> 16 | Configuration file already exists 17 | >>>= 1 18 | 19 | rm cblrepo.cfg 20 | >>>= 0 21 | -------------------------------------------------------------------------------- /tests/data/bar.cabal: -------------------------------------------------------------------------------- 1 | name: bar 2 | version: 0 3 | 4 | library 5 | build-depends: foo 6 | -------------------------------------------------------------------------------- /tests/data/baz.cabal: -------------------------------------------------------------------------------- 1 | name: baz 2 | version: 1 3 | 4 | library 5 | build-depends: bar ==0 6 | -------------------------------------------------------------------------------- /tests/data/flags-file0.cabal: -------------------------------------------------------------------------------- 1 | name: FlagsSimple0 2 | version: 1.2.3 3 | 4 | flag flag0 5 | default: True 6 | manual: False 7 | 8 | flag flag1 9 | default: False 10 | manual: False 11 | 12 | flag flag2 13 | default: False 14 | manual: True 15 | 16 | flag flag3 17 | default: True 18 | manual: True 19 | -------------------------------------------------------------------------------- /tests/data/foo-self.cabal: -------------------------------------------------------------------------------- 1 | name: foo-self 2 | version: 0 3 | 4 | library 5 | 6 | executable foo-self-exe 7 | build-depends: foo-self 8 | -------------------------------------------------------------------------------- /tests/data/foo.cabal: -------------------------------------------------------------------------------- 1 | name: foo 2 | version: 1.2.3 3 | x-revision: 3 4 | -------------------------------------------------------------------------------- /tests/extract.test: -------------------------------------------------------------------------------- 1 | # a few tests to verify the extract command, run with `shelltestrunner -w` 2 | 3 | rm -fr ,,test_appdir 4 | >>>= 0 5 | mkdir ,,test_appdir 6 | >>>= 0 7 | 8 | cblrepo --appdir=,,test_appdir update 9 | >>>= 0 10 | 11 | cblrepo --appdir=,,test_appdir extract dataenc,0.14.0.7 12 | >>>= 0 13 | 14 | md5sum dataenc.cabal 15 | >>> 16 | 2f606651e462a0f0b0b8ac61ff29861b dataenc.cabal 17 | >>>= 0 18 | 19 | cblrepo --appdir=,,test_appdir extract dataenc,0.13.1 20 | >>> 21 | >>>2 22 | Failed to extract Cabal for dataenc 0.13.1 23 | >>>= 1 24 | 25 | rm -f dataenc.cabal 26 | >>>= 0 27 | 28 | rm -fr ,,test_appdir 29 | >>>= 0 30 | -------------------------------------------------------------------------------- /tests/flags.test: -------------------------------------------------------------------------------- 1 | # a few tests of flags 2 | 3 | # {{{1 set-up 4 | rm -f cblrepo.db 5 | >>>= 0 6 | 7 | cblrepo list 8 | >>> 9 | >>>= 0 10 | 11 | # {{{1 adding simple 12 | cblrepo add -f tests/data/flags-file0.cabal 13 | >>> 14 | >>>= 0 15 | 16 | cblrepo list 17 | >>> 18 | FlagsSimple0 1.2.3.x0-1 (flag3 -flag2 -flag1 flag0) 19 | >>>= 0 20 | 21 | # {{{1 adding setting 22 | cblrepo add -f tests/data/flags-file0.cabal:-flag0,flag1 23 | >>> 24 | >>>= 0 25 | 26 | cblrepo list 27 | >>> 28 | FlagsSimple0 1.2.3.x0-1 (flag3 -flag2 flag1 -flag0) 29 | >>>= 0 30 | 31 | # {{{1 adding without setting should keep flags 32 | cblrepo add -f tests/data/flags-file0.cabal 33 | >>> 34 | >>>= 0 35 | 36 | cblrepo list 37 | >>> 38 | FlagsSimple0 1.2.3.x0-1 (flag3 -flag2 flag1 -flag0) 39 | >>>= 0 40 | 41 | # {{{1 clean-up 42 | rm -f cblrepo.db 43 | >>>= 0 44 | -------------------------------------------------------------------------------- /tests/list.test: -------------------------------------------------------------------------------- 1 | # a few tests to verify the list command, run with shelltestrunner 2 | 3 | # {{{1 set up 4 | rm -f cblrepo.db 5 | >>>= 0 6 | 7 | cblrepo add -f tests/data/foo.cabal -f tests/data/bar.cabal -f tests/data/baz.cabal 8 | >>>= 0 9 | 10 | # {{{1 ordinary listing 11 | cblrepo list 12 | >>> 13 | bar 0.x0-1 14 | baz 1.x0-1 15 | foo 1.2.3.x3-1 16 | >>>= 0 17 | 18 | cblrepo list -f normal 19 | >>> 20 | bar 0.x0-1 21 | baz 1.x0-1 22 | foo 1.2.3.x3-1 23 | >>>= 0 24 | 25 | # {{{1 short listing 26 | cblrepo list -f short 27 | >>> 28 | bar,0 29 | baz,1 30 | foo,1.2.3 31 | >>>= 0 32 | 33 | # {{{1 hackage listing 34 | cblrepo list --format hackage 35 | >>> 36 | ("bar","0",Nothing) 37 | ("baz","1",Nothing) 38 | ("foo","1.2.3",Nothing) 39 | >>>= 0 40 | 41 | # {{{1 list individual packages 42 | cblrepo list bar 43 | >>> 44 | bar 0.x0-1 45 | >>>= 0 46 | 47 | cblrepo list --format short bar 48 | >>> 49 | bar,0 50 | >>>= 0 51 | 52 | cblrepo list --format hackage bar foo 53 | >>> 54 | ("bar","0",Nothing) 55 | ("foo","1.2.3",Nothing) 56 | >>>= 0 57 | 58 | # {{{1 clean up 59 | rm -f cblrepo.db 60 | >>>= 0 61 | -------------------------------------------------------------------------------- /tests/pkgbuild.test: -------------------------------------------------------------------------------- 1 | # a few tests related to generating source packages, run with shelltestrunner 2 | 3 | rm -fr ,,test_appdir 4 | >>>= 0 5 | mkdir ,,test_appdir 6 | >>>= 0 7 | 8 | cblrepo --appdir=,,test_appdir --db=,,test_appdir/testrepo.db update 9 | >>>= 0 10 | 11 | cblrepo --appdir=,,test_appdir --db=,,test_appdir/testrepo.db add -g array,0 -g base,4.8 -g containers,0 -g directory,0 -g filepath,0 -g pretty,0 -g process,0 -g random,0 12 | >>>= 0 13 | 14 | # this version has a test on the GHC version, with ghc 7.6 it doesn't require 15 | # hashtables, with 7.8 it does 16 | cblrepo --appdir=,,test_appdir --db=,,test_appdir/testrepo.db add --ghc-version=7.6 gtk2hs-buildtools,0.12.5.2 17 | >>> 18 | >>>2 19 | >>>= 0 20 | 21 | cblrepo --appdir=,,test_appdir --db=,,test_appdir/testrepo.db pkgbuild --ghc-version=7.8 gtk2hs-buildtools 22 | >>> 23 | >>>2 24 | Failed to finalize package: gtk2hs-buildtools 25 | >>>= 1 26 | 27 | rm -fr ,,test_appdir 28 | >>>= 0 29 | -------------------------------------------------------------------------------- /tests/self-referencing-cabal.test: -------------------------------------------------------------------------------- 1 | # a few tests around self-referencing CABAL files 2 | 3 | # {{{1 set-up 4 | rm -f cblrepo.db 5 | >>>= 0 6 | 7 | cblrepo list 8 | >>> 9 | >>>= 0 10 | 11 | # {{{1 adding 12 | cblrepo add -f tests/data/foo-self.cabal 13 | >>> 14 | >>>= 0 15 | 16 | # {{{1 build 17 | cblrepo build foo-self 18 | >>> 19 | foo-self 20 | >>>= 0 21 | 22 | # todo: pkgbuild 23 | 24 | # {{{1 rm 25 | cblrepo rm foo-self 26 | >>>= 0 27 | 28 | # {{{1 clean-up 29 | rm -f cblrepo.db 30 | >>>= 0 31 | --------------------------------------------------------------------------------