├── .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 | [](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 |
--------------------------------------------------------------------------------