Skip to content

Commit ef39007

Browse files
committed
Initial commit.
0 parents  commit ef39007

File tree

6 files changed

+252
-0
lines changed

6 files changed

+252
-0
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/dist

LICENSE.txt

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
The MIT License (Expat)
2+
3+
Copyright (c) 2021, whitequark <[email protected]>
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in
13+
all copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+
THE SOFTWARE.

README.md

+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
Zig PyPI distribution
2+
=====================
3+
4+
This repository contains the script used to repackage the [releases][zigdl] of the [Zig programming language][zig] as [Python binary wheels][wheel]. This document is intended for maintainers; see the [package README][pkgreadme] for rationale and usage instructions.
5+
6+
The repackaged artifacts are published as the [ziglang PyPI package][pypi].
7+
8+
[zig]: https://ziglang.org/
9+
[zigdl]: https://ziglang.org/download/
10+
[wheel]: https://github.com/pypa/wheel
11+
[pkgreadme]: README.pypi.md
12+
[pypi]: https://pypi.org/project/ziglang/
13+
14+
Preparation
15+
-----------
16+
17+
The script requires Python 3.5 or later.
18+
19+
Install the dependencies:
20+
21+
```shell
22+
pip install wheel twine libarchive-c
23+
```
24+
25+
The `libarchive-c` Python library requires the native [libarchive][] library to be available.
26+
27+
[libarchive]: https://libarchive.org/
28+
29+
Building wheels
30+
---------------
31+
32+
Run the repackaging script:
33+
34+
```shell
35+
python make_wheels.py
36+
```
37+
38+
This command will download the Zig release archives for every supported platform and convert them to binary wheels, which are placed under `dist/`. The Zig version and platforms are configured in the script source.
39+
40+
The process of converting release archives to binary wheels is deterministic, and the output of the script should be bit-for-bit identical regardless of the environment and platform it runs under. To this end, it prints the SHA256 hashes of inputs and outputs; the hashes of the inputs will match the ones on the [Zig downloads page][zigdl], and the hashes of the outputs will match the ones on the [PyPI downloads page][pypidl].
41+
42+
[pypidl]: https://pypi.org/project/ziglang/#files
43+
44+
Uploading wheels
45+
----------------
46+
47+
Run the publishing utility:
48+
49+
```shell
50+
twine dist/*
51+
```
52+
53+
This command will upload the binary wheels built in the previous step to PyPI.
54+
55+
License
56+
-------
57+
58+
This script is distributed under the terms of the [MIT (Expat) license](LICENSE.txt).

README.pypi.md

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
Zig PyPI distribution
2+
=====================
3+
4+
[Zig][] is a general-purpose programming language and toolchain for maintaining robust, optimal, and reusable software. The [ziglang][pypi] Python package redistributes the Zig toolchain so that it can be used as a dependency of Python projects.
5+
6+
[zig]: https://ziglang.org/
7+
[pypi]: https://pypi.org/project/ziglang/
8+
9+
Rationale
10+
---------
11+
12+
Although Zig is useful in itself, the Zig toolchain includes a drop-in C and C++ compiler, [`zig cc`][zigcc], based on [clang][]. Unlike clang itself, `zig cc` is standalone: it does not require additional development files to be installed to target any of the platforms it supports. Through `zig cc`, Python code that generates C or C++ code can build it without any external dependencies.
13+
14+
[clang]: https://clang.llvm.org/
15+
[zigcc]: https://andrewkelley.me/post/zig-cc-powerful-drop-in-replacement-gcc-clang.html
16+
17+
Usage
18+
-----
19+
20+
To run the Zig toolchain from the command line, use:
21+
22+
```shell
23+
python -m ziglang
24+
```
25+
26+
To run the Zig toolchain from a Python program, use `sys.executable` to locate the Python binary to invoke. For example:
27+
28+
```python
29+
import sys, subprocess
30+
31+
subprocess.call([sys.executable, "-m", "ziglang"])
32+
```
33+
34+
License
35+
-------
36+
37+
The [Zig license](https://github.com/ziglang/zig#license).

dist/.gitkeep

Whitespace-only changes.

make_wheels.py

+135
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
import os
2+
import hashlib
3+
import urllib.request
4+
from email.message import EmailMessage
5+
from wheel.wheelfile import WheelFile, get_zipinfo_datetime
6+
from zipfile import ZipInfo, ZIP_DEFLATED
7+
import libarchive # from libarchive-c
8+
9+
10+
class ReproducibleWheelFile(WheelFile):
11+
def writestr(self, zinfo, *args, **kwargs):
12+
if not isinstance(zinfo, ZipInfo):
13+
raise ValueError("ZipInfo required")
14+
zinfo.date_time = (1980,1,1,0,0,0)
15+
zinfo.create_system = 3
16+
super().writestr(zinfo, *args, **kwargs)
17+
18+
19+
def make_message(headers, payload=None):
20+
msg = EmailMessage()
21+
for name, value in headers.items():
22+
if isinstance(value, list):
23+
for value_part in value:
24+
msg[name] = value_part
25+
else:
26+
msg[name] = value
27+
if payload:
28+
msg.set_payload(payload)
29+
return msg
30+
31+
32+
def write_wheel_file(filename, contents):
33+
with ReproducibleWheelFile(filename, 'w') as wheel:
34+
for member_info, member_source in contents.items():
35+
if not isinstance(member_info, ZipInfo):
36+
member_info = ZipInfo(member_info)
37+
member_info.external_attr = 0o644 << 16
38+
member_info.file_size = len(member_source)
39+
member_info.compress_type = ZIP_DEFLATED
40+
wheel.writestr(member_info, bytes(member_source))
41+
return filename
42+
43+
44+
def write_wheel(out_dir, *, name, version, tag, metadata, description, contents):
45+
wheel_name = f'{name}-{version}-{tag}.whl'
46+
dist_info = f'{name}-{version}.dist-info'
47+
return write_wheel_file(os.path.join(out_dir, wheel_name), {
48+
**contents,
49+
f'{dist_info}/METADATA': make_message({
50+
'Metadata-Version': '2.1',
51+
'Name': name,
52+
'Version': version,
53+
**metadata,
54+
}, description),
55+
f'{dist_info}/WHEEL': make_message({
56+
'Wheel-Version': '1.0',
57+
'Generator': 'ziglang make_wheels.py',
58+
'Root-Is-Purelib': 'false',
59+
'Tag': tag,
60+
}),
61+
})
62+
63+
64+
def write_ziglang_wheel(out_dir, *, version, platform, archive):
65+
contents = {}
66+
contents['ziglang/__init__.py'] = b''
67+
68+
with libarchive.memory_reader(archive) as archive:
69+
for entry in archive:
70+
entry_name = '/'.join(entry.name.split('/')[1:])
71+
if entry.isdir or not entry_name:
72+
continue
73+
74+
zip_info = ZipInfo(f'ziglang/{entry_name}')
75+
zip_info.external_attr = (entry.mode & 0xFFFF) << 16
76+
contents[zip_info] = b''.join(entry.get_blocks())
77+
78+
if entry_name.startswith('zig'):
79+
contents['ziglang/__main__.py'] = f'''\
80+
import os, sys, subprocess
81+
sys.exit(subprocess.call([
82+
os.path.join(os.path.dirname(__file__), "{entry_name}"),
83+
*sys.argv[1:]
84+
]))
85+
'''.encode('ascii')
86+
87+
with open('README.pypi.md') as f:
88+
description = f.read()
89+
90+
return write_wheel(out_dir,
91+
name='ziglang',
92+
version=version,
93+
tag=f'py3-none-{platform}',
94+
metadata={
95+
'Summary': 'Zig is a general-purpose programming language and toolchain for maintaining robust, optimal, and reusable software.',
96+
'Description-Content-Type': 'text/markdown',
97+
'License': 'MIT',
98+
'Classifier': [
99+
'License :: OSI Approved :: MIT License',
100+
],
101+
'Project-URL': [
102+
'Homepage, https://ziglang.org',
103+
'Source Code, https://github.com/ziglang/zig-pypi',
104+
'Bug Tracker, https://github.com/ziglang/zig-pypi/issues',
105+
],
106+
'Requires-Python': '~=3.5',
107+
},
108+
description=description,
109+
contents=contents,
110+
)
111+
112+
113+
zig_version = '0.8.0'
114+
115+
for zig_platform, python_platform in {
116+
'windows-i386': 'win32',
117+
'windows-x86_64': 'win_amd64',
118+
'macos-x86_64': 'macosx_10_9_x86_64',
119+
'macos-aarch64': 'macosx_11_0_arm64',
120+
'linux-i386': 'manylinux_2_12_i686.manylinux2010_i686',
121+
'linux-x86_64': 'manylinux_2_12_x86_64.manylinux2010_x86_64',
122+
'linux-aarch64': 'manylinux_2_17_aarch64.manylinux2014_aarch64',
123+
}.items():
124+
zig_url = f'https://ziglang.org/download/{zig_version}/zig-{zig_platform}-{zig_version}.' + \
125+
('zip' if zig_platform.startswith('windows-') else 'tar.xz')
126+
with urllib.request.urlopen(zig_url) as request:
127+
zig_archive = request.read()
128+
print(f'{hashlib.sha256(zig_archive).hexdigest()} {zig_url}')
129+
130+
wheel_path = write_ziglang_wheel('dist/',
131+
version=zig_version,
132+
platform=python_platform,
133+
archive=zig_archive)
134+
with open(wheel_path, 'rb') as wheel:
135+
print(f' {hashlib.sha256(wheel.read()).hexdigest()} {wheel_path}')

0 commit comments

Comments
 (0)