├── .github └── FUNDING.yml ├── .gitignore ├── .gitlab-ci.yml ├── .yamllint.yaml ├── LICENSE.md ├── README.md ├── config.yaml ├── dockerfiles └── .gitkeep ├── generate.py ├── requirements.txt ├── setup.cfg ├── templates ├── debian.Dockerfile └── gitlab-ci.yml └── tests ├── run.sh ├── test.c └── test.cpp /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | --- 2 | github: silkeh 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | tests/test 2 | dockerfiles/* 3 | .buildx-cache 4 | -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | --- 2 | include: 3 | - project: silkeh/pipelines 4 | file: 5 | - base.gitlab-ci.yml 6 | - linters/shellcheck.gitlab-ci.yml 7 | - linters/yamllint.gitlab-ci.yml 8 | 9 | generate: 10 | stage: build 11 | image: python:alpine 12 | script: 13 | - pip3 install -r requirements.txt 14 | - python3 generate.py ci 15 | artifacts: 16 | paths: 17 | - dockerfiles 18 | 19 | builds: 20 | stage: build 21 | needs: [generate] 22 | variables: 23 | PARENT_PIPELINE_ID: $CI_PIPELINE_ID 24 | trigger: 25 | strategy: depend 26 | include: 27 | - artifact: dockerfiles/gitlab-ci.yml 28 | job: generate 29 | -------------------------------------------------------------------------------- /.yamllint.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | extends: default 3 | rules: 4 | colons: 5 | max-spaces-after: 10 6 | line-length: 7 | max: 100 8 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # EUROPEAN UNION PUBLIC LICENCE v. 1.2 2 | 3 | EUPL © the European Union 2007, 2016 4 | 5 | This European Union Public Licence (the ‘EUPL’) applies to the Work (as defined 6 | below) which is provided under the terms of this Licence. Any use of the Work, 7 | other than as authorised under this Licence is prohibited (to the extent such 8 | use is covered by a right of the copyright holder of the Work). The Work is 9 | provided under the terms of this Licence when the Licensor (as defined below) 10 | has placed the following notice immediately following the copyright notice for 11 | the Work: 12 | 13 | > Licensed under the EUPL 14 | 15 | or has expressed by any other means his willingness to license under the EUPL. 16 | 17 | ## 1. Definitions 18 | 19 | In this Licence, the following terms have the following meaning: 20 | 21 | - ‘The Licence’: this Licence. 22 | 23 | - ‘The Original Work’: the work or software distributed or communicated by the 24 | Licensor under this Licence, available as Source Code and also as Executable 25 | Code as the case may be. 26 | 27 | - ‘Derivative Works’: the works or software that could be created by the 28 | Licensee, based upon the Original Work or modifications thereof. This Licence 29 | does not define the extent of modification or dependence on the Original Work 30 | required in order to classify a work as a Derivative Work; this extent is 31 | determined by copyright law applicable in the country mentioned in Article 15. 32 | 33 | - ‘The Work’: the Original Work or its Derivative Works. 34 | 35 | - ‘The Source Code’: the human-readable form of the Work which is the most 36 | convenient for people to study and modify. 37 | 38 | - ‘The Executable Code’: any code which has generally been compiled and which is 39 | meant to be interpreted by a computer as a program. 40 | 41 | - ‘The Licensor’: the natural or legal person that distributes or communicates 42 | the Work under the Licence. 43 | 44 | - ‘Contributor(s)’: any natural or legal person who modifies the Work under the 45 | Licence, or otherwise contributes to the creation of a Derivative Work. 46 | 47 | - ‘The Licensee’ or ‘You’: any natural or legal person who makes any usage of the 48 | Work under the terms of the Licence. 49 | 50 | - ‘Distribution’ or ‘Communication’: any act of selling, giving, lending, 51 | renting, distributing, communicating, transmitting, or otherwise making 52 | available, online or offline, copies of the Work or providing access to its 53 | essential functionalities at the disposal of any other natural or legal 54 | person. 55 | 56 | ## 2. Scope of the rights granted by the Licence 57 | 58 | The Licensor hereby grants You a worldwide, royalty-free, non-exclusive, 59 | sublicensable licence to do the following, for the duration of copyright vested 60 | in the Original Work: 61 | 62 | - use the Work in any circumstance and for all usage, 63 | - reproduce the Work, 64 | - modify the Work, and make Derivative Works based upon the Work, 65 | - communicate to the public, including the right to make available or display 66 | the Work or copies thereof to the public and perform publicly, as the case may 67 | be, the Work, 68 | - distribute the Work or copies thereof, 69 | - lend and rent the Work or copies thereof, 70 | - sublicense rights in the Work or copies thereof. 71 | 72 | Those rights can be exercised on any media, supports and formats, whether now 73 | known or later invented, as far as the applicable law permits so. 74 | 75 | In the countries where moral rights apply, the Licensor waives his right to 76 | exercise his moral right to the extent allowed by law in order to make effective 77 | the licence of the economic rights here above listed. 78 | 79 | The Licensor grants to the Licensee royalty-free, non-exclusive usage rights to 80 | any patents held by the Licensor, to the extent necessary to make use of the 81 | rights granted on the Work under this Licence. 82 | 83 | ## 3. Communication of the Source Code 84 | 85 | The Licensor may provide the Work either in its Source Code form, or as 86 | Executable Code. If the Work is provided as Executable Code, the Licensor 87 | provides in addition a machine-readable copy of the Source Code of the Work 88 | along with each copy of the Work that the Licensor distributes or indicates, in 89 | a notice following the copyright notice attached to the Work, a repository where 90 | the Source Code is easily and freely accessible for as long as the Licensor 91 | continues to distribute or communicate the Work. 92 | 93 | ## 4. Limitations on copyright 94 | 95 | Nothing in this Licence is intended to deprive the Licensee of the benefits from 96 | any exception or limitation to the exclusive rights of the rights owners in the 97 | Work, of the exhaustion of those rights or of other applicable limitations 98 | thereto. 99 | 100 | ## 5. Obligations of the Licensee 101 | 102 | The grant of the rights mentioned above is subject to some restrictions and 103 | obligations imposed on the Licensee. Those obligations are the following: 104 | 105 | **Attribution right**: The Licensee shall keep intact all copyright, patent or 106 | trademarks notices and all notices that refer to the Licence and to the 107 | disclaimer of warranties. The Licensee must include a copy of such notices and a 108 | copy of the Licence with every copy of the Work he/she distributes or 109 | communicates. The Licensee must cause any Derivative Work to carry prominent 110 | notices stating that the Work has been modified and the date of modification. 111 | 112 | **Copyleft clause**: If the Licensee distributes or communicates copies of the 113 | Original Works or Derivative Works, this Distribution or Communication will be 114 | done under the terms of this Licence or of a later version of this Licence 115 | unless the Original Work is expressly distributed only under this version of the 116 | Licence — for example by communicating ‘EUPL v. 1.2 only’. The Licensee 117 | (becoming Licensor) cannot offer or impose any additional terms or conditions on 118 | the Work or Derivative Work that alter or restrict the terms of the Licence. 119 | 120 | **Compatibility clause**: If the Licensee Distributes or Communicates Derivative 121 | Works or copies thereof based upon both the Work and another work licensed under 122 | a Compatible Licence, this Distribution or Communication can be done under the 123 | terms of this Compatible Licence. For the sake of this clause, ‘Compatible 124 | Licence’ refers to the licences listed in the appendix attached to this 125 | Licence. Should the Licensee's obligations under the Compatible Licence conflict 126 | with his/her obligations under this Licence, the obligations of the Compatible 127 | Licence shall prevail. 128 | 129 | **Provision of Source Code**: When distributing or communicating copies of the 130 | Work, the Licensee will provide a machine-readable copy of the Source Code or 131 | indicate a repository where this Source will be easily and freely available for 132 | as long as the Licensee continues to distribute or communicate the Work. 133 | 134 | **Legal Protection**: This Licence does not grant permission to use the trade 135 | names, trademarks, service marks, or names of the Licensor, except as required 136 | for reasonable and customary use in describing the origin of the Work and 137 | reproducing the content of the copyright notice. 138 | 139 | ## 6. Chain of Authorship 140 | 141 | The original Licensor warrants that the copyright in the Original Work granted 142 | hereunder is owned by him/her or licensed to him/her and that he/she has the 143 | power and authority to grant the Licence. 144 | 145 | Each Contributor warrants that the copyright in the modifications he/she brings 146 | to the Work are owned by him/her or licensed to him/her and that he/she has the 147 | power and authority to grant the Licence. 148 | 149 | Each time You accept the Licence, the original Licensor and subsequent 150 | Contributors grant You a licence to their contributions to the Work, under the 151 | terms of this Licence. 152 | 153 | ## 7. Disclaimer of Warranty 154 | 155 | The Work is a work in progress, which is continuously improved by numerous 156 | Contributors. It is not a finished work and may therefore contain defects or 157 | ‘bugs’ inherent to this type of development. 158 | 159 | For the above reason, the Work is provided under the Licence on an ‘as is’ basis 160 | and without warranties of any kind concerning the Work, including without 161 | limitation merchantability, fitness for a particular purpose, absence of defects 162 | or errors, accuracy, non-infringement of intellectual property rights other than 163 | copyright as stated in Article 6 of this Licence. 164 | 165 | This disclaimer of warranty is an essential part of the Licence and a condition 166 | for the grant of any rights to the Work. 167 | 168 | ## 8. Disclaimer of Liability 169 | 170 | Except in the cases of wilful misconduct or damages directly caused to natural 171 | persons, the Licensor will in no event be liable for any direct or indirect, 172 | material or moral, damages of any kind, arising out of the Licence or of the use 173 | of the Work, including without limitation, damages for loss of goodwill, work 174 | stoppage, computer failure or malfunction, loss of data or any commercial 175 | damage, even if the Licensor has been advised of the possibility of such 176 | damage. However, the Licensor will be liable under statutory product liability 177 | laws as far such laws apply to the Work. 178 | 179 | ## 9. Additional agreements 180 | 181 | While distributing the Work, You may choose to conclude an additional agreement, 182 | defining obligations or services consistent with this Licence. However, if 183 | accepting obligations, You may act only on your own behalf and on your sole 184 | responsibility, not on behalf of the original Licensor or any other Contributor, 185 | and only if You agree to indemnify, defend, and hold each Contributor harmless 186 | for any liability incurred by, or claims asserted against such Contributor by 187 | the fact You have accepted any warranty or additional liability. 188 | 189 | ## 10. Acceptance of the Licence 190 | 191 | The provisions of this Licence can be accepted by clicking on an icon ‘I agree’ 192 | placed under the bottom of a window displaying the text of this Licence or by 193 | affirming consent in any other similar way, in accordance with the rules of 194 | applicable law. Clicking on that icon indicates your clear and irrevocable 195 | acceptance of this Licence and all of its terms and conditions. 196 | 197 | Similarly, you irrevocably accept this Licence and all of its terms and 198 | conditions by exercising any rights granted to You by Article 2 of this Licence, 199 | such as the use of the Work, the creation by You of a Derivative Work or the 200 | Distribution or Communication by You of the Work or copies thereof. 201 | 202 | ## 11. Information to the public 203 | 204 | In case of any Distribution or Communication of the Work by means of electronic 205 | communication by You (for example, by offering to download the Work from a 206 | remote location) the distribution channel or media (for example, a website) must 207 | at least provide to the public the information requested by the applicable law 208 | regarding the Licensor, the Licence and the way it may be accessible, concluded, 209 | stored and reproduced by the Licensee. 210 | 211 | ## 12. Termination of the Licence 212 | 213 | The Licence and the rights granted hereunder will terminate automatically upon 214 | any breach by the Licensee of the terms of the Licence. 215 | 216 | Such a termination will not terminate the licences of any person who has 217 | received the Work from the Licensee under the Licence, provided such persons 218 | remain in full compliance with the Licence. 219 | 220 | ## 13. Miscellaneous 221 | 222 | Without prejudice of Article 9 above, the Licence represents the complete 223 | agreement between the Parties as to the Work. 224 | 225 | If any provision of the Licence is invalid or unenforceable under applicable 226 | law, this will not affect the validity or enforceability of the Licence as a 227 | whole. Such provision will be construed or reformed so as necessary to make it 228 | valid and enforceable. 229 | 230 | The European Commission may publish other linguistic versions or new versions of 231 | this Licence or updated versions of the Appendix, so far this is required and 232 | reasonable, without reducing the scope of the rights granted by the Licence. 233 | New versions of the Licence will be published with a unique version number. 234 | 235 | All linguistic versions of this Licence, approved by the European Commission, 236 | have identical value. Parties can take advantage of the linguistic version of 237 | their choice. 238 | 239 | ## 14. Jurisdiction 240 | 241 | Without prejudice to specific agreement between parties, 242 | 243 | - any litigation resulting from the interpretation of this License, arising 244 | between the European Union institutions, bodies, offices or agencies, as a 245 | Licensor, and any Licensee, will be subject to the jurisdiction of the Court 246 | of Justice of the European Union, as laid down in article 272 of the Treaty on 247 | the Functioning of the European Union, 248 | 249 | - any litigation arising between other parties and resulting from the 250 | interpretation of this License, will be subject to the exclusive jurisdiction 251 | of the competent court where the Licensor resides or conducts its primary 252 | business. 253 | 254 | ## 15. Applicable Law 255 | 256 | Without prejudice to specific agreement between parties, 257 | 258 | - this Licence shall be governed by the law of the European Union Member State 259 | where the Licensor has his seat, resides or has his registered office, 260 | 261 | - this licence shall be governed by Belgian law if the Licensor has no seat, 262 | residence or registered office inside a European Union Member State. 263 | 264 | ## *Appendix* 265 | 266 | ‘Compatible Licences’ according to Article 5 EUPL are: 267 | 268 | - GNU General Public License (GPL) v. 2, v. 3 269 | - GNU Affero General Public License (AGPL) v. 3 270 | - Open Software License (OSL) v. 2.1, v. 3.0 271 | - Eclipse Public License (EPL) v. 1.0 272 | - CeCILL v. 2.0, v. 2.1 273 | - Mozilla Public Licence (MPL) v. 2 274 | - GNU Lesser General Public Licence (LGPL) v. 2.1, v. 3 275 | - Creative Commons Attribution-ShareAlike v. 3.0 Unported (CC BY-SA 3.0) for 276 | works other than software 277 | - European Union Public Licence (EUPL) v. 1.1, v. 1.2 278 | - Québec Free and Open-Source Licence — Reciprocity (LiLiQ-R) or Strong 279 | Reciprocity (LiLiQ-R+). 280 | 281 | The European Commission may update this Appendix to later versions of the above 282 | licences without producing a new version of the EUPL, as long as they provide 283 | the rights granted in Article 2 of this Licence and protect the covered Source 284 | Code from exclusive appropriation. 285 | 286 | All other changes or additions to this Appendix require the production of a new 287 | EUPL version. 288 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Clang / LLVM docker containers 2 | [![docker](https://img.shields.io/docker/pulls/silkeh/clang.svg)](https://hub.docker.com/r/silkeh/clang/) 3 | 4 | A collection of images containing official Clang/LLVM releases. 5 | All Docker images are generated in GitLab CI, and can be found [here][artifacts]. 6 | 7 | The `latest` tag contains the latest stable LLVM version on the latest stable Debian version. 8 | The `dev` tag contains a nightly build of the unstable branch from . 9 | 10 | Images can be pulled from [Docker Hub], [GitHub] and [GitLab]. 11 | 12 | The versions and contents of the Debian packages are generated based on [config.yaml][], 13 | in which new versions of LLVM and Debian can be added. 14 | 15 | [config.yaml]: https://gitlab.com/silkeh/docker-clang/-/blob/master/config.yaml 16 | [pipeline]: https://gitlab.com/silkeh/docker-clang/pipelines/master/latest 17 | [artifacts]: https://gitlab.com/silkeh/docker-clang/-/jobs/artifacts/master/browse/dockerfiles?job=generate 18 | [Docker Hub]: https://hub.docker.com/r/silkeh/clang/ 19 | [GitLab]: https://gitlab.com/slxh/docker/clang/container_registry/3598362 20 | [GitHub]: https://github.com/-/silkeh/packages/container/package/clang 21 | -------------------------------------------------------------------------------- /config.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # Alternate environments to push to. 3 | environments: 4 | - Docker 5 | - GitHub 6 | 7 | # Possible URLs of the Release files of the Debian repository. 8 | repo_url: https://apt.llvm.org/{debian_version} 9 | repo_component: main 10 | repo_distributions: 11 | default: 12 | - llvm-toolchain-{debian_version}-{version} 13 | - llvm-toolchain-{version} 14 | dev: 15 | - llvm-toolchain-{debian_version} 16 | - llvm-toolchain 17 | 18 | # List of LLVM versions to build. 19 | versions: 20 | - dev 21 | - '4.0' 22 | - '5.0' 23 | - '6.0' 24 | - '7' 25 | - '8' 26 | - '9' 27 | - '10' 28 | - '11' 29 | - '12' 30 | - '13' 31 | - '14' 32 | - '15' 33 | - '16' 34 | - '17' 35 | - '18' 36 | - '19' 37 | - '20' 38 | 39 | # Versions of Debian to build for. 40 | debian_versions: 41 | - unstable 42 | - trixie 43 | - buster 44 | - bullseye 45 | - bookworm 46 | 47 | # Architectures supported by the Docker Debian images and the LLVM repositories. 48 | # Support per LLVM version is detected automatically, but some architectures have been 49 | # excluded because the repository does not contain the correct packages. 50 | debian_architectures: 51 | default: [amd64, arm64, s390x] 52 | unstable: [amd64] 53 | buster: [amd64] 54 | bullseye: [amd64, arm64] 55 | 56 | # Mapping of Debian architectures to Docker architectures. 57 | docker_platforms: 58 | amd64: linux/amd64 59 | arm64: linux/arm64/v8 60 | i386: linux/386 61 | s390x: linux/s390x 62 | 63 | # Packages to install for LLVM versions. 64 | packages: 65 | default: 66 | - clang-{version} 67 | - clang-tidy-{version} 68 | - clang-format-{version} 69 | - lld-{version} 70 | - libc++-{version}-dev 71 | - libc++abi-{version}-dev 72 | dev: 73 | - clang 74 | - clang-tidy 75 | - clang-format 76 | - lld 77 | - libc++-dev 78 | - libc++abi-dev 79 | '3.6': &3 80 | - clang-{version} 81 | - clang-format-{version} 82 | - libc++-{version}-dev 83 | - libc++abi-{version}-dev 84 | '3.7': *3 85 | '3.8': *3 86 | '3.9': *3 87 | '4.0': &4 88 | - clang-{version} 89 | - clang-tidy-{version} 90 | - clang-format-{version} 91 | - libc++-{version}-dev 92 | - libc++abi-{version}-dev 93 | '5.0': *4 94 | '6.0': &6 95 | - clang-{version} 96 | - clang-tidy-{version} 97 | - clang-format-{version} 98 | - libc++-{version}-dev 99 | - libc++abi-{version}-dev 100 | '7': *6 101 | '8': *6 102 | '9': *6 103 | '10': *6 104 | '11': *6 105 | '14': 106 | - clang-{version} 107 | - clang-tidy-{version} 108 | - clang-format-{version} 109 | - lld-{version} 110 | - libc++-{version}-dev 111 | - libc++abi-{version}-dev 112 | - libclang-rt-{version}-dev 113 | -------------------------------------------------------------------------------- /dockerfiles/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/silkeh/docker-clang/9e0d3f93f6bcf8b97fbac54dd86d63d9da819095/dockerfiles/.gitkeep -------------------------------------------------------------------------------- /generate.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import os 4 | import os.path 5 | import requests 6 | import yaml 7 | import argparse 8 | import subprocess 9 | 10 | 11 | def replace(s, version, debian_version, architecture='', packages='', repo=None): 12 | if repo is not None: 13 | s = s.\ 14 | replace('{repo_url}', repo.url).\ 15 | replace('{repo_distribution}', repo.distribution).\ 16 | replace('{repo_component}', repo.component) 17 | 18 | return s.\ 19 | replace('{packages}', packages).\ 20 | replace('{version}', version).\ 21 | replace('{debian_version}', debian_version).\ 22 | replace('{architecture}', architecture) 23 | 24 | 25 | def get(d, key): 26 | return d.get(key, d['default']) 27 | 28 | 29 | class Replacer: 30 | def __init__(self, version, debian_version, architecture='', packages=''): 31 | self.version = version 32 | self.debian_version = debian_version 33 | self.architecture = architecture 34 | self.packages = packages 35 | 36 | def replace(self, s): 37 | return replace(s, self.version, self.debian_version, self.architecture, self.packages) 38 | 39 | 40 | class DebianRepo: 41 | def __init__(self, url, distribution, component): 42 | self.url = url 43 | self.distribution = distribution 44 | self.component = component 45 | 46 | def architectures(self, architectures): 47 | return [a for a in architectures if self._exists(a)] 48 | 49 | def _exists(self, architecture): 50 | res = requests.get(self._release_url(architecture)) 51 | if res.status_code == 200: 52 | return True 53 | 54 | if res.status_code == 404: 55 | return False 56 | 57 | raise Exception(f'Unexpected status code: {res.status_code}') 58 | 59 | def _release_url(self, architecture): 60 | return f'{self.url}/dists/{self.distribution}/{self.component}/binary-{architecture}/Release' 61 | 62 | 63 | class Builder: 64 | UNSTABLE = ['unstable'] 65 | 66 | def __init__(self, config): 67 | self.repo_url = config['repo_url'] 68 | self.repo_distributions = config['repo_distributions'] 69 | self.repo_component = config['repo_component'] 70 | self.versions = config['versions'] 71 | self.packages = config['packages'] 72 | self.debian_architectures = config['debian_architectures'] 73 | self.debian_versions = config['debian_versions'] 74 | self.docker_platforms = config['docker_platforms'] 75 | self.environments = config['environments'] 76 | self.environment = self.environments[0] 77 | self._dockerfiles = [] 78 | 79 | def _repo_distributions(self, version): 80 | return get(self.repo_distributions, version) 81 | 82 | def _find_repo(self, version, debian_version, architectures): 83 | for distribution in self._repo_distributions(version): 84 | repl = Replacer(version, debian_version) 85 | repo = DebianRepo(repl.replace(self.repo_url), 86 | repl.replace(distribution), 87 | repl.replace(self.repo_component)) 88 | arch = repo.architectures(architectures) 89 | if len(arch) > 0: 90 | return repo, arch 91 | 92 | return None, None 93 | 94 | def _architectures(self, debian_version): 95 | return get(self.debian_architectures, debian_version) 96 | 97 | def _platforms(self, architectures): 98 | return [self.docker_platforms[a] for a in architectures] 99 | 100 | def _packages(self, version): 101 | return get(self.packages, version) 102 | 103 | def _new_dockerfile(self, version, debian_version, repo, architectures): 104 | d = Dockerfile(version, debian_version, repo, 105 | self._platforms(architectures), self._packages(version)) 106 | 107 | self._dockerfiles.append(d) 108 | 109 | return d 110 | 111 | def _get_dockerfiles(self): 112 | if len(self._dockerfiles) > 0: 113 | for d in self._dockerfiles: 114 | yield d 115 | 116 | return 117 | 118 | for version in self.versions: 119 | for debian_version in self.debian_versions: 120 | if self._skip_version(version, debian_version): 121 | print(f'Skipping: {version}-{debian_version}') 122 | continue 123 | 124 | repo, architectures = self._find_repo(version, debian_version, 125 | self._architectures(debian_version)) 126 | if repo is None: 127 | print(f'No repo: {version}-{debian_version}') 128 | continue 129 | 130 | yield self._new_dockerfile(version, debian_version, repo, architectures) 131 | 132 | def _skip_version(self, version, debian_version): 133 | return \ 134 | self._skip_dev_release(version, debian_version) or \ 135 | self._skip_unstable_release(version, debian_version) 136 | 137 | def _skip_dev_release(self, version, debian_version): 138 | return version == 'dev' and \ 139 | debian_version not in (self.UNSTABLE + self.debian_versions[-1:]) 140 | 141 | def _skip_unstable_release(self, version, debian_version): 142 | return version != 'dev' and \ 143 | version not in self.versions[-2:] and \ 144 | debian_version in self.UNSTABLE 145 | 146 | def dockerfiles(self): 147 | return list(self._get_dockerfiles()) 148 | 149 | def generate(self): 150 | for dockerfile in self._get_dockerfiles(): 151 | print(f"Creating: {dockerfile.tag}") 152 | dockerfile.create() 153 | 154 | def build(self, name: str): 155 | for dockerfile in self._get_dockerfiles(): 156 | print(f"Building: {dockerfile.tag}") 157 | dockerfile.create() 158 | dockerfile.build(name) 159 | 160 | def buildx(self, name: str): 161 | for dockerfile in self._get_dockerfiles(): 162 | print(f"Cross-building: {dockerfile.tag} ({', '.join(dockerfile.platforms)})") 163 | dockerfile.create() 164 | dockerfile.buildx(name) 165 | 166 | def _latest(self): 167 | dockerfiles = list(self._get_dockerfiles()) 168 | if len(dockerfiles) == 0: 169 | raise Exception('No docker files') 170 | 171 | return dockerfiles[-1].tag 172 | 173 | def _latest_version(self, v: str): 174 | dockerfiles = [d for d in self._get_dockerfiles() if d.version == v] 175 | if len(dockerfiles) == 0: 176 | return None 177 | 178 | return dockerfiles[-1].tag 179 | 180 | def _latest_versions(self): 181 | versions = {v: self._latest_version(v) for v in self.versions} 182 | return {v: l for v, l in versions.items() if l is not None} 183 | 184 | def aliases(self): 185 | return self._latest_versions() | { 186 | 'latest': self._latest(), 187 | } 188 | 189 | def environment_aliases(self): 190 | tags = [d.tag for d in builder.dockerfiles()] 191 | aliases = [tag for tag in builder.aliases()] 192 | 193 | return [(env, tag) 194 | for tag in (tags + aliases) 195 | for env in self.environments] 196 | 197 | def tag_aliases(self, name: str): 198 | for alias, tag in self.aliases().items(): 199 | print(f'Tagging {tag} as {alias}') 200 | 201 | r = subprocess.run(['docker', 'tag', f'{name}:{tag}', f'{name}:{alias}']) 202 | if r.returncode != 0: 203 | raise Exception('Error') 204 | 205 | 206 | class Dockerfile: 207 | def __init__(self, version, debian_version, repo, platforms, packages): 208 | self.version = version 209 | self.debian_version = debian_version 210 | self.repo = repo 211 | self.platforms = platforms 212 | self.packages = packages 213 | 214 | def create(self): 215 | with open(self._path, 'w') as f: 216 | f.write(replace( 217 | self._template(), 218 | self.version, 219 | self.debian_version, 220 | packages=' '.join(self.packages), 221 | repo=self.repo 222 | )) 223 | 224 | def build(self, name: str): 225 | r = subprocess.run(['docker', 'build', '.', 226 | '--file', self._path, 227 | '--tag', f'{name}:{self.tag}']) 228 | if r.returncode != 0: 229 | raise Exception('Build failed') 230 | 231 | def test(self, name: str): 232 | r = subprocess.run(['docker', 'run', '--tty', 233 | '--volume', os.getcwd() + '/tests:/tests', 234 | f'{name}:{self.tag}', '/tests/run.sh', self.version]) 235 | if r.returncode != 0: 236 | raise Exception('Test failed') 237 | 238 | def buildx(self, name: str): 239 | r = subprocess.run(['docker', 'buildx', 'build', '.', 240 | '--target', 'test', 241 | '--cache-from', 'type=local,src=/tmp/buildx', 242 | '--cache-to', 'type=local,dest=/tmp/buildx,mode=max', 243 | '--file', self._path, 244 | '--platform', ','.join(self.platforms), 245 | '--tag', f'{name}:{self.tag}']) 246 | if r.returncode != 0: 247 | raise Exception('Cross-platform build failed') 248 | 249 | @property 250 | def tag(self): 251 | return f'{self.version}-{self.debian_version}' 252 | 253 | @property 254 | def _path(self): 255 | return os.path.join('dockerfiles', f'{self.version}-{self.debian_version}.Dockerfile') 256 | 257 | def _template(self): 258 | path = \ 259 | self._template_path_version_debian_version() or \ 260 | self._template_path_version() or \ 261 | self._template_path_debian_version() or \ 262 | self._template_path('debian') 263 | 264 | with open(path) as f: 265 | return f.read() 266 | 267 | def _template_path_version_debian_version(self): 268 | path = self._template_path(f'{self.version}-{self.debian_version}') 269 | if os.path.exists(path): 270 | return path 271 | 272 | return None 273 | 274 | def _template_path_debian_version(self): 275 | path = self._template_path(self.debian_version) 276 | if os.path.exists(path): 277 | return path 278 | 279 | return None 280 | 281 | def _template_path_version(self): 282 | path = self._template_path(self.version) 283 | if os.path.exists(path): 284 | return path 285 | 286 | return None 287 | 288 | def _template_path(self, name): 289 | return os.path.join('templates', name + '.Dockerfile') 290 | 291 | 292 | class GitLabCI: 293 | def __init__(self, data): 294 | self.data = data 295 | 296 | def generate(self, dockerfiles, aliases, env_aliases): 297 | self.add_dockerfiles(dockerfiles) 298 | self.add_aliases(aliases) 299 | self.add_environments(env_aliases) 300 | self.save() 301 | 302 | def add_aliases(self, aliases): 303 | for alias, tag in aliases.items(): 304 | self.data[alias] = { 305 | 'extends': '.alias', 306 | 'needs': [tag], 307 | 'variables': { 308 | 'DST_TAG': alias, 309 | 'SRC_TAG': tag 310 | } 311 | } 312 | 313 | def add_dockerfiles(self, dockerfiles): 314 | for dockerfile in dockerfiles: 315 | self.data[dockerfile.tag] = { 316 | 'extends': GitLabCI._extends(dockerfile), 317 | 'variables': { 318 | 'IMAGE_TAG': dockerfile.tag, 319 | 'CLANG_VERSION': dockerfile.version, 320 | 'PLATFORMS': ','.join(dockerfile.platforms), 321 | } 322 | } 323 | 324 | def add_environments(self, env_aliases): 325 | for env, tag in env_aliases: 326 | self.data[f"{tag} ({env})"] = { 327 | 'extends': '.environment-alias', 328 | 'needs': [tag], 329 | 'environment': { 330 | 'name': env, 331 | }, 332 | 'variables': { 333 | 'SRC_TAG': tag, 334 | 'DST_TAG': tag, 335 | } 336 | } 337 | 338 | def save(self): 339 | with open(os.path.join('dockerfiles', 'gitlab-ci.yml'), 'w') as f: 340 | f.write(yaml.dump(self.data)) 341 | 342 | @staticmethod 343 | def _extends(dockerfile): 344 | if dockerfile.platforms == ['linux/amd64']: 345 | return '.docker' 346 | 347 | return '.docker-buildx' 348 | 349 | 350 | if __name__ == '__main__': 351 | ci_template_file = os.path.join('templates', 'gitlab-ci.yml') 352 | 353 | parser = argparse.ArgumentParser(description='Generate and build docker files') 354 | parser.add_argument('-c', '--config', default='config.yaml', 355 | help='Config file') 356 | parser.add_argument('-i', '--image', default='silkeh/clang', 357 | help='Image name') 358 | parser.add_argument('-t', '--ci-template', default=ci_template_file, 359 | help='GitLab CI template') 360 | parser.add_argument('-v', '--version', default=[], action='append', 361 | help='LLVM version to build, all by default') 362 | parser.add_argument('-d', '--debian', default=[], action='append', 363 | help='Debian version to build, all by default') 364 | parser.add_argument('command', choices=['ci', 'alias', 'generate', 'build', 'buildx'], 365 | help='Action to execute') 366 | 367 | args = parser.parse_args() 368 | 369 | with open(args.ci_template) as f: 370 | gitlab_ci = GitLabCI(yaml.safe_load(f.read())) 371 | 372 | with open(args.config) as f: 373 | builder = Builder(yaml.safe_load(f.read())) 374 | 375 | if len(args.version) > 0: 376 | builder.versions = args.version 377 | 378 | if len(args.debian) > 0: 379 | builder.debian_versions = args.debian 380 | 381 | match args.command: 382 | case "ci": 383 | builder.generate() 384 | gitlab_ci.generate(builder.dockerfiles(), 385 | builder.aliases(), 386 | builder.environment_aliases()) 387 | case "build": 388 | builder.build(args.image) 389 | builder.tag_aliases(args.image) 390 | case "buildx": 391 | builder.buildx(args.image) 392 | case "generate": 393 | builder.generate() 394 | case "alias": 395 | builder.tag_aliases(args.image) 396 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | PyYAML 2 | requests 3 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | license = EUPL-1.2 3 | 4 | [flake8] 5 | max-line-length = 100 6 | exclude = .git,.idea,.vscode,__pycache__,venv 7 | -------------------------------------------------------------------------------- /templates/debian.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM debian:{debian_version}-slim AS intermediate 2 | 3 | # Install dependencies 4 | RUN apt-get -qq update; \ 5 | apt-get install -qqy --no-install-recommends \ 6 | gnupg2 wget ca-certificates apt-transport-https \ 7 | autoconf automake cmake dpkg-dev file make patch libc6-dev 8 | 9 | # Install LLVM 10 | RUN echo "deb {repo_url} {repo_distribution} {repo_component}" \ 11 | > /etc/apt/sources.list.d/llvm.list && \ 12 | wget -qO /etc/apt/trusted.gpg.d/llvm.asc \ 13 | https://apt.llvm.org/llvm-snapshot.gpg.key && \ 14 | apt-get -qq update && \ 15 | apt-get install -qqy -t {repo_distribution} {packages} && \ 16 | for f in /usr/lib/llvm-*/bin/*; do ln -sf "$f" /usr/bin; done && \ 17 | ln -sf clang /usr/bin/cc && \ 18 | ln -sf clang /usr/bin/c89 && \ 19 | ln -sf clang /usr/bin/c99 && \ 20 | ln -sf clang++ /usr/bin/c++ && \ 21 | ln -sf clang++ /usr/bin/g++ && \ 22 | rm -rf /var/lib/apt/lists/* 23 | 24 | FROM intermediate AS test 25 | 26 | COPY tests /tests 27 | 28 | RUN /tests/run.sh {version} 29 | 30 | FROM intermediate AS final 31 | -------------------------------------------------------------------------------- /templates/gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | --- 2 | default: 3 | interruptible: true 4 | 5 | .docker: 6 | stage: build 7 | image: docker:stable 8 | needs: 9 | - pipeline: $PARENT_PIPELINE_ID 10 | job: generate 11 | services: 12 | - docker:dind 13 | variables: 14 | DOCKER_HOST: tcp://docker:2375 15 | DOCKER_DRIVER: overlay2 16 | before_script: 17 | - docker info 18 | - echo $CI_JOB_TOKEN | docker login -u $CI_REGISTRY_USER --password-stdin $CI_REGISTRY 19 | script: 20 | - docker build -t $CI_REGISTRY_IMAGE:$IMAGE_TAG -f dockerfiles/$IMAGE_TAG.Dockerfile . 21 | - docker push $CI_REGISTRY_IMAGE:$IMAGE_TAG 22 | 23 | .docker-buildx: 24 | extends: .docker 25 | image: jdrouet/docker-with-buildx:stable 26 | cache: 27 | key: buildx-$IMAGE_TAG 28 | paths: [.buildx-cache] 29 | script: 30 | - docker run --rm --privileged multiarch/qemu-user-static --reset -p yes 31 | - docker buildx create --driver=docker-container --use 32 | - docker buildx build . 33 | --pull 34 | --target=test 35 | --cache-from type=local,src=.buildx-cache 36 | --cache-to type=local,dest=.buildx-cache 37 | --file dockerfiles/$IMAGE_TAG.Dockerfile 38 | --platform $PLATFORMS 39 | - docker buildx build . --target=final 40 | --pull 41 | --push 42 | --tag $CI_REGISTRY_IMAGE:$IMAGE_TAG 43 | --cache-from type=local,src=.buildx-cache 44 | --file dockerfiles/$IMAGE_TAG.Dockerfile 45 | --platform $PLATFORMS 46 | 47 | .alias: 48 | stage: build 49 | image: docker:stable 50 | services: 51 | - docker:dind 52 | variables: 53 | DOCKER_HOST: tcp://docker:2375 54 | DOCKER_DRIVER: overlay2 55 | script: 56 | - docker info 57 | - echo $CI_JOB_TOKEN | docker login -u $CI_REGISTRY_USER --password-stdin $CI_REGISTRY 58 | - docker pull $CI_REGISTRY_IMAGE:$SRC_TAG 59 | - docker tag $CI_REGISTRY_IMAGE:$SRC_TAG $CI_REGISTRY_IMAGE:$DST_TAG 60 | - docker push $CI_REGISTRY_IMAGE:$DST_TAG 61 | 62 | .environment-alias: 63 | stage: build 64 | image: docker:stable 65 | services: 66 | - docker:dind 67 | variables: 68 | DOCKER_HOST: tcp://docker:2375 69 | DOCKER_DRIVER: overlay2 70 | script: 71 | - docker info 72 | - echo $DOCKER_REGISTRY_TOKEN | docker login 73 | -u $DOCKER_REGISTRY_USER 74 | --password-stdin $DOCKER_REGISTRY 75 | - docker pull $CI_REGISTRY_IMAGE:$SRC_TAG 76 | - docker tag $CI_REGISTRY_IMAGE:$SRC_TAG $DOCKER_REGISTRY_IMAGE:$DST_TAG 77 | - docker push $DOCKER_REGISTRY_IMAGE:$DST_TAG 78 | -------------------------------------------------------------------------------- /tests/run.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -eou pipefail 3 | 4 | cd "$(realpath "$(dirname "$0")")" 5 | 6 | version="$1" 7 | 8 | # Show version 9 | clang --version 10 | 11 | # Check version 12 | exp_version="clang version ${version}" 13 | cur_version="$(clang --version | grep -Po 'clang version .*')" 14 | 15 | if [[ "$1" != "dev" ]] && [[ "${cur_version}" != "${exp_version}"* ]] 16 | then 17 | echo "Incorrect version: ${cur_version}" 18 | echo "Expected: ${exp_version}" 19 | exit 1 20 | fi 21 | 22 | # Compile C 23 | cc test.c -o test && ./test 24 | clang test.c -o test && ./test 25 | 26 | # Compile C++ 27 | g++ test.cpp -o test && ./test 28 | clang++ test.cpp -o test && ./test 29 | 30 | if grep -q 'Debian GNU/Linux 1' /etc/issue 31 | then 32 | echo "Testing LTO" 33 | clang++ test.cpp -o test -flto=thin 34 | ./test 35 | fi 36 | -------------------------------------------------------------------------------- /tests/test.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main() { 4 | printf("C compiled successfully!\n"); 5 | return 0; 6 | } 7 | -------------------------------------------------------------------------------- /tests/test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main() { 4 | std::cout << "C++ compiled successfully!" << std::endl; 5 | return 0; 6 | } 7 | --------------------------------------------------------------------------------