zig-zon 0.2.4__tar.gz → 0.2.6__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {zig-zon-0.2.4 → zig-zon-0.2.6}/PKG-INFO +1 -1
- zig-zon-0.2.6/build.zig +119 -0
- {zig-zon-0.2.4 → zig-zon-0.2.6}/build.zig.zon +3 -5
- zig-zon-0.2.6/setup.py +124 -0
- zig-zon-0.2.6/setup.zig.json +1 -0
- zig-zon-0.2.6/src/zig_zon.pyi +5 -0
- {zig-zon-0.2.4 → zig-zon-0.2.6}/src/zig_zon.zig +39 -13
- zig-zon-0.2.6/zig-pkg/zig_python_sdist-0.0.8/LICENSE +21 -0
- zig-zon-0.2.6/zig-pkg/zig_python_sdist-0.0.8/README.md +275 -0
- zig-zon-0.2.6/zig-pkg/zig_python_sdist-0.0.8/build.zig +296 -0
- {zig-zon-0.2.4/zig-pkg/zig_python_sdist-0.0.3 → zig-zon-0.2.6/zig-pkg/zig_python_sdist-0.0.8}/build.zig.zon +1 -1
- {zig-zon-0.2.4/zig-pkg/zig_python_sdist-0.0.3 → zig-zon-0.2.6/zig-pkg/zig_python_sdist-0.0.8}/pkg-info.zon +1 -1
- zig-zon-0.2.6/zig-pkg/zig_python_sdist-0.0.8/src/build/CPython.zig +185 -0
- zig-zon-0.2.6/zig-pkg/zig_python_sdist-0.0.8/src/build/Compression.zig +33 -0
- {zig-zon-0.2.4/zig-pkg/zig_python_sdist-0.0.3 → zig-zon-0.2.6/zig-pkg/zig_python_sdist-0.0.8}/src/build/SDistStep.zig +36 -20
- {zig-zon-0.2.4/zig-pkg/zig_python_sdist-0.0.3 → zig-zon-0.2.6/zig-pkg/zig_python_sdist-0.0.8}/src/build/VenvInstallTest.zig +35 -9
- {zig-zon-0.2.4/zig-pkg/zig_python_sdist-0.0.3 → zig-zon-0.2.6/zig-pkg/zig_python_sdist-0.0.8}/src/build/extract_link_info.py +4 -12
- zig-zon-0.2.6/zig-pkg/zig_python_sdist-0.0.8/src/build/pip_runner.py +153 -0
- zig-zon-0.2.6/zig-pkg/zig_python_sdist-0.0.8/src/build/python-translatec.h +6 -0
- {zig-zon-0.2.4/zig-pkg/zig_python_sdist-0.0.3 → zig-zon-0.2.6/zig-pkg/zig_python_sdist-0.0.8}/src/build/python.zig +216 -92
- zig-zon-0.2.6/zig-pkg/zig_python_sdist-0.0.8/src/build/python_allocator.zig +201 -0
- zig-zon-0.2.6/zig-pkg/zig_python_sdist-0.0.8/src/build/setup_json.zig +34 -0
- zig-zon-0.2.4/build.zig +0 -75
- zig-zon-0.2.4/setup.py +0 -66
- zig-zon-0.2.4/setup.zig.json +0 -1
- zig-zon-0.2.4/zig-pkg/zig_python_sdist-0.0.3/README.md +0 -114
- zig-zon-0.2.4/zig-pkg/zig_python_sdist-0.0.3/build.zig +0 -159
- zig-zon-0.2.4/zig-pkg/zig_python_sdist-0.0.3/src/build/CPython.zig +0 -112
- zig-zon-0.2.4/zig-pkg/zig_python_sdist-0.0.3/src/build/setup_json.zig +0 -20
- {zig-zon-0.2.4/zig-pkg/zig_python_sdist-0.0.3 → zig-zon-0.2.6}/LICENSE +0 -0
- {zig-zon-0.2.4 → zig-zon-0.2.6}/pkg-info.zon +0 -0
- {zig-zon-0.2.4/zig-pkg/zig_python_sdist-0.0.3 → zig-zon-0.2.6/zig-pkg/zig_python_sdist-0.0.8}/src/build/LowerDashString.zig +0 -0
- {zig-zon-0.2.4/zig-pkg/zig_python_sdist-0.0.3 → zig-zon-0.2.6/zig-pkg/zig_python_sdist-0.0.8}/src/build/pkg_info.zig +0 -0
zig-zon-0.2.6/build.zig
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
const std = @import("std");
|
|
2
|
+
const Build = std.Build;
|
|
3
|
+
const zig_python_sdist = @import("zig_python_sdist");
|
|
4
|
+
const zon = @import("build.zig.zon");
|
|
5
|
+
|
|
6
|
+
pub fn build(b: *Build) void {
|
|
7
|
+
const target = b.standardTargetOptions(.{});
|
|
8
|
+
const optimize = b.standardOptimizeOption(.{});
|
|
9
|
+
|
|
10
|
+
// this target and section of our build is used at pip install time
|
|
11
|
+
const ext_install_step = b.step("bdist-wheel", "build python extension module");
|
|
12
|
+
ext_install_step.dependOn(
|
|
13
|
+
&b.addInstallFile(b.path("src/zig_zon.pyi"), "lib/zig_zon.pyi").step,
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
// this generates a python sdist archive
|
|
17
|
+
const sdist = zig_python_sdist.createSdistStep(b, .{
|
|
18
|
+
.name = "zig-zon",
|
|
19
|
+
.version = zon.version,
|
|
20
|
+
.description = b.path("README.md"),
|
|
21
|
+
.build = .{
|
|
22
|
+
.args = &.{
|
|
23
|
+
ext_install_step.name,
|
|
24
|
+
b.fmt("-Doptimize={s}", .{@tagName(optimize)}),
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
.extra_pkg_info = @import("pkg-info.zon"),
|
|
28
|
+
.files = &.{
|
|
29
|
+
.{ .src = b.path("LICENSE"), .dest = "LICENSE" },
|
|
30
|
+
.{ .src = b.path("pkg-info.zon"), .dest = "pkg-info.zon" },
|
|
31
|
+
.{ .src = b.path("build.zig.zon"), .dest = "build.zig.zon" },
|
|
32
|
+
.{ .src = b.path("build.zig"), .dest = "build.zig" },
|
|
33
|
+
.{ .src = b.path("src/zig_zon.zig"), .dest = "src/zig_zon.zig" },
|
|
34
|
+
.{ .src = b.path("src/zig_zon.pyi"), .dest = "src/zig_zon.pyi" },
|
|
35
|
+
},
|
|
36
|
+
.compression = .best,
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const sdistfile = b.addInstallFileWithDir(
|
|
40
|
+
sdist.getEmittedArchive(),
|
|
41
|
+
.{ .custom = "src" },
|
|
42
|
+
sdist.basename,
|
|
43
|
+
);
|
|
44
|
+
b.getInstallStep().dependOn(&sdistfile.step);
|
|
45
|
+
|
|
46
|
+
// we can use a python virtual environment inside
|
|
47
|
+
// the zig cache to test pip installation
|
|
48
|
+
const test_pip_install = zig_python_sdist.createVenvInstallTest(
|
|
49
|
+
b,
|
|
50
|
+
.{
|
|
51
|
+
.sdist = sdist.getEmittedArchive(),
|
|
52
|
+
.unittest_py = &.{b.path("src/test_zig_zon.py")},
|
|
53
|
+
.twine_check = true,
|
|
54
|
+
.no_clean = b.option(bool, "pip-no-clean", "dont cleanup pip temp dirs") orelse false,
|
|
55
|
+
},
|
|
56
|
+
);
|
|
57
|
+
b.step("test-pip-install", "").dependOn(test_pip_install.step);
|
|
58
|
+
|
|
59
|
+
const cpython = zig_python_sdist.createCPythonModule(b, .{
|
|
60
|
+
.target = target,
|
|
61
|
+
.version = zon.version,
|
|
62
|
+
.optimize = .ReleaseSmall,
|
|
63
|
+
});
|
|
64
|
+
const lib = b.addLibrary(.{
|
|
65
|
+
.name = "zig_zon",
|
|
66
|
+
.root_module = b.addModule("zig_zon", .{
|
|
67
|
+
.root_source_file = b.path("src/zig_zon.zig"),
|
|
68
|
+
.target = target,
|
|
69
|
+
.optimize = optimize,
|
|
70
|
+
.imports = &.{
|
|
71
|
+
.{ .name = "cpython", .module = cpython },
|
|
72
|
+
},
|
|
73
|
+
}),
|
|
74
|
+
.linkage = .dynamic,
|
|
75
|
+
});
|
|
76
|
+
const ext_install = zig_python_sdist.installExtLib(b, lib);
|
|
77
|
+
ext_install_step.dependOn(&ext_install.step);
|
|
78
|
+
|
|
79
|
+
ext_install_step.dependOn(&ext_install.step);
|
|
80
|
+
|
|
81
|
+
const ext_path = zig_python_sdist.CPython.renameExtLib(b, lib);
|
|
82
|
+
|
|
83
|
+
{
|
|
84
|
+
const ext_tests = ext_path.wf.addCopyFile(b.path("src/test_zig_zon.py"), "test_zig_zon.py");
|
|
85
|
+
_ = ext_path.wf.addCopyFile(b.path("build.zig.zon"), "build.zig.zon");
|
|
86
|
+
const test_run = Build.Step.Run.create(b, "unittest");
|
|
87
|
+
test_run.setCwd(ext_path.wf.getDirectory());
|
|
88
|
+
test_run.setEnvironmentVariable("PYTHONPATH", ".");
|
|
89
|
+
test_run.addFileArg(zig_python_sdist.CPython.find(b));
|
|
90
|
+
if (b.option(bool, "tracetest", "run unittest with tracing") orelse false) {
|
|
91
|
+
test_run.addArgs(&.{ "-m", "trace", "--trace" });
|
|
92
|
+
} else {
|
|
93
|
+
test_run.addArgs(&.{ "-m", "unittest" });
|
|
94
|
+
}
|
|
95
|
+
test_run.addDirectoryArg(ext_tests);
|
|
96
|
+
b.step("test-bdist", "run unittests against the extension").dependOn(&test_run.step);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const usf = b.addUpdateSourceFiles();
|
|
100
|
+
b.step("update-src", "updates the pyi file. needs 'mypy' installed").dependOn(&usf.step);
|
|
101
|
+
|
|
102
|
+
{
|
|
103
|
+
const stubgen_run = Build.Step.Run.create(b, "stubgen");
|
|
104
|
+
stubgen_run.setCwd(ext_path.wf.getDirectory());
|
|
105
|
+
stubgen_run.addArgs(&.{
|
|
106
|
+
"stubgen",
|
|
107
|
+
"-m",
|
|
108
|
+
lib.name,
|
|
109
|
+
"-o",
|
|
110
|
+
});
|
|
111
|
+
const stub_dir = stubgen_run.addOutputDirectoryArg("stubgen");
|
|
112
|
+
stubgen_run.addArg("--search-path");
|
|
113
|
+
stubgen_run.addDirectoryArg(ext_path.wf.getDirectory());
|
|
114
|
+
usf.addCopyFileToSource(
|
|
115
|
+
stub_dir.path(b, b.fmt("{s}.pyi", .{lib.name})),
|
|
116
|
+
b.fmt("src/{s}.pyi", .{lib.name}),
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
.{
|
|
2
2
|
.name = .python_zig_zon,
|
|
3
3
|
.fingerprint = 0x3d332656a3644fc9,
|
|
4
|
-
.version = "0.2.
|
|
4
|
+
.version = "0.2.6",
|
|
5
5
|
.minimum_zig_version = "0.16.0",
|
|
6
6
|
.dependencies = .{
|
|
7
7
|
.zig_python_sdist = .{
|
|
8
|
-
.url = "git+https://codeberg.org/dasimmet/zig-python-sdist.git#
|
|
9
|
-
.hash = "zig_python_sdist-0.0.
|
|
8
|
+
.url = "git+https://codeberg.org/dasimmet/zig-python-sdist.git?ref=0.0.8#3f06dd8177fbba62a68b7f0894b4a3d26cfb9fcf",
|
|
9
|
+
.hash = "zig_python_sdist-0.0.8-wjgdOimGAQBiUtyW-KGTUzlNhuwanlP9ealvenf3vcpw",
|
|
10
10
|
},
|
|
11
11
|
},
|
|
12
12
|
.paths = .{
|
|
@@ -16,7 +16,5 @@
|
|
|
16
16
|
"pkg-info.zon",
|
|
17
17
|
"LICENSE",
|
|
18
18
|
"README.md",
|
|
19
|
-
"setup.py",
|
|
20
|
-
"pyproject.toml",
|
|
21
19
|
},
|
|
22
20
|
}
|
zig-zon-0.2.6/setup.py
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
|
|
3
|
+
from setuptools import setup
|
|
4
|
+
from setuptools.dist import Distribution
|
|
5
|
+
from setuptools.command.bdist_wheel import bdist_wheel
|
|
6
|
+
import subprocess
|
|
7
|
+
import sys
|
|
8
|
+
import os
|
|
9
|
+
import json
|
|
10
|
+
import glob
|
|
11
|
+
import shutil
|
|
12
|
+
|
|
13
|
+
with open("setup.zig.json") as fd:
|
|
14
|
+
setup_json = json.load(fd)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def mkversion(v, need_release=False):
|
|
18
|
+
'''assembles a dict of semantic version to string'''
|
|
19
|
+
if need_release:
|
|
20
|
+
assert v.get('pre') is None, "cannot use version with pre: " + str(v)
|
|
21
|
+
assert v.get(
|
|
22
|
+
'build') is None, "cannot use version with build: " + str(v)
|
|
23
|
+
return str(v['major']) + '.' + str(
|
|
24
|
+
v['minor']) + '.' + str(v['patch'])
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class ZigDistribution(Distribution):
|
|
28
|
+
def has_ext_modules(self):
|
|
29
|
+
return setup_json['has_ext_modules']
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class ZigBuildWheel(bdist_wheel):
|
|
33
|
+
def initialize_options(self):
|
|
34
|
+
super().initialize_options()
|
|
35
|
+
if 'py_limited_api' in setup_json and setup_json['py_limited_api']:
|
|
36
|
+
self.py_limited_api = setup_json['py_limited_api']
|
|
37
|
+
|
|
38
|
+
def run(self, *args, **kwargs):
|
|
39
|
+
print("py_limited_api:", self.py_limited_api, file=sys.stderr)
|
|
40
|
+
print("plat_name:", self.plat_name, file=sys.stderr)
|
|
41
|
+
|
|
42
|
+
import ziglang
|
|
43
|
+
zig_exe = os.path.join(
|
|
44
|
+
os.path.dirname(ziglang.__file__), "zig")
|
|
45
|
+
|
|
46
|
+
cwd = os.getcwd()
|
|
47
|
+
print("zig build dir:", cwd, file=sys.stderr)
|
|
48
|
+
print("using zig exe:", zig_exe, file=sys.stderr)
|
|
49
|
+
|
|
50
|
+
extra_build_args = setup_json['build']['args']
|
|
51
|
+
build_cmd = [zig_exe, 'build'] + extra_build_args
|
|
52
|
+
|
|
53
|
+
print("zig build:", build_cmd, file=sys.stderr)
|
|
54
|
+
subprocess.check_call(build_cmd)
|
|
55
|
+
|
|
56
|
+
os.makedirs(self.bdist_dir, exist_ok=True)
|
|
57
|
+
copy_patterns = setup_json['build']['copy_patterns']
|
|
58
|
+
pkg_name = setup_json['name'].replace('-', '_')
|
|
59
|
+
|
|
60
|
+
for item in copy_patterns:
|
|
61
|
+
pattern = item['pattern']
|
|
62
|
+
dest = item.get('dest')
|
|
63
|
+
package_dir = item.get('package_dir', False)
|
|
64
|
+
|
|
65
|
+
if package_dir:
|
|
66
|
+
if dest:
|
|
67
|
+
dest = os.path.join(pkg_name, dest)
|
|
68
|
+
else:
|
|
69
|
+
dest = pkg_name
|
|
70
|
+
elif dest:
|
|
71
|
+
dest = dest.replace('{name}', pkg_name).replace(
|
|
72
|
+
'{package}', pkg_name)
|
|
73
|
+
|
|
74
|
+
artifacts = glob.glob(pattern)
|
|
75
|
+
if len(artifacts) == 0:
|
|
76
|
+
raise Exception(
|
|
77
|
+
'no zig artifacts found in {}/{}'.format(cwd, pattern))
|
|
78
|
+
|
|
79
|
+
for it in artifacts:
|
|
80
|
+
if dest:
|
|
81
|
+
is_dir = (
|
|
82
|
+
dest.endswith('/') or
|
|
83
|
+
dest.endswith('\\') or
|
|
84
|
+
any(c in pattern for c in '*?[]') or
|
|
85
|
+
(os.path.splitext(dest)[
|
|
86
|
+
1] == '' and os.path.splitext(it)[1] != '')
|
|
87
|
+
)
|
|
88
|
+
if is_dir:
|
|
89
|
+
dst = os.path.join(
|
|
90
|
+
self.bdist_dir, dest, os.path.basename(it))
|
|
91
|
+
else:
|
|
92
|
+
dst = os.path.join(self.bdist_dir, dest)
|
|
93
|
+
else:
|
|
94
|
+
dst = os.path.join(self.bdist_dir, os.path.basename(it))
|
|
95
|
+
|
|
96
|
+
os.makedirs(os.path.dirname(dst), exist_ok=True)
|
|
97
|
+
print("cp", it, dst, file=sys.stderr)
|
|
98
|
+
if os.path.isdir(it):
|
|
99
|
+
shutil.copytree(it, dst, dirs_exist_ok=True)
|
|
100
|
+
else:
|
|
101
|
+
shutil.copyfile(it, dst)
|
|
102
|
+
|
|
103
|
+
return super().run(*args, **kwargs)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
options = {}
|
|
107
|
+
if 'py_limited_api' in setup_json and setup_json['py_limited_api']:
|
|
108
|
+
options['bdist_wheel'] = {'py_limited_api': setup_json['py_limited_api']}
|
|
109
|
+
|
|
110
|
+
setup(
|
|
111
|
+
name=setup_json['name'],
|
|
112
|
+
version=mkversion(setup_json['version']),
|
|
113
|
+
cmdclass={'bdist_wheel': ZigBuildWheel},
|
|
114
|
+
options=options,
|
|
115
|
+
install_requires=setup_json['install_requires'] if 'install_requires' in setup_json else None,
|
|
116
|
+
entry_points=setup_json['entry_points'] if 'entry_points' in setup_json else None,
|
|
117
|
+
packages=setup_json['build']['packages'],
|
|
118
|
+
distclass=ZigDistribution,
|
|
119
|
+
setup_requires=[
|
|
120
|
+
"setuptools",
|
|
121
|
+
"wheel",
|
|
122
|
+
"ziglang==" + mkversion(setup_json['zig_version'], need_release=True),
|
|
123
|
+
] + setup_json['build']['requires'],
|
|
124
|
+
)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"name":"zig_zon","version":{"major":0,"minor":2,"patch":6},"zig_version":{"major":0,"minor":16,"patch":0},"build":{"args":["--fork=zig-pkg/zig_python_sdist-0.0.8","bdist-wheel","-Doptimize=ReleaseSmall"],"requires":[],"packages":[],"copy_patterns":[{"pattern":"zig-out/lib/*","package_dir":false}]},"has_ext_modules":true}
|
|
@@ -8,28 +8,54 @@ const cpython = @import("cpython");
|
|
|
8
8
|
const c = cpython.c;
|
|
9
9
|
const PyObject = c.PyObject;
|
|
10
10
|
|
|
11
|
-
pub var
|
|
12
|
-
c.PyMethodDef{
|
|
13
|
-
.ml_name = "parse",
|
|
14
|
-
.ml_meth = @ptrCast(@alignCast(&parse)),
|
|
15
|
-
.ml_flags = @as(c_int, 1),
|
|
16
|
-
.ml_doc = "parse a zon literal to a python object",
|
|
17
|
-
},
|
|
18
|
-
c.PyMethodDef{},
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
pub var zigmodule = c.PyModuleDef{
|
|
11
|
+
pub var zigmodule: c.PyModuleDef = .{
|
|
22
12
|
.m_name = "zig_zon",
|
|
23
|
-
.m_methods = &
|
|
13
|
+
.m_methods = @constCast(&[_]c.PyMethodDef{
|
|
14
|
+
.{
|
|
15
|
+
.ml_name = "parse",
|
|
16
|
+
.ml_meth = @ptrCast(@alignCast(&parse)),
|
|
17
|
+
.ml_flags = 1,
|
|
18
|
+
.ml_doc = "parse a zon literal to a python object",
|
|
19
|
+
},
|
|
20
|
+
.{},
|
|
21
|
+
}),
|
|
24
22
|
};
|
|
25
23
|
|
|
26
24
|
pub export fn PyInit_zig_zon() [*c]c.PyObject {
|
|
27
25
|
const mod = c.PyModule_Create(&zigmodule);
|
|
26
|
+
|
|
27
|
+
{
|
|
28
|
+
// const annotations = c.PyDict_New();
|
|
29
|
+
// defer c.Py_DecRef(annotations);
|
|
30
|
+
// const builtins = c.PyImport_ImportModule("builtins");
|
|
31
|
+
// defer c.Py_DecRef(builtins);
|
|
32
|
+
|
|
33
|
+
// const str_type = c.PyObject_GetAttrString(builtins, "str") orelse PyErrPanic();
|
|
34
|
+
// defer c.Py_DecRef(str_type);
|
|
35
|
+
|
|
36
|
+
// assert(0 <= c.PyDict_SetItemString(annotations, "value", str_type));
|
|
37
|
+
|
|
38
|
+
// const parse_ref = c.PyObject_GetAttrString(mod, "parse") orelse PyErrPanic();
|
|
39
|
+
// defer c.Py_DecRef(parse_ref);
|
|
40
|
+
|
|
41
|
+
//TODO: find another way to set annotations, since a builtin function is immutable.
|
|
42
|
+
|
|
43
|
+
// if (0 > c.PyObject_SetAttrString(parse_ref, "__annotations__", annotations)) {
|
|
44
|
+
// c.PyErr_Print();
|
|
45
|
+
// @panic("WTF");
|
|
46
|
+
// }
|
|
47
|
+
}
|
|
48
|
+
|
|
28
49
|
cpython.addVersion(mod);
|
|
29
50
|
cpython.addZigBuiltin(mod);
|
|
30
51
|
return mod;
|
|
31
52
|
}
|
|
32
53
|
|
|
54
|
+
pub fn PyErrPanic() noreturn {
|
|
55
|
+
c.PyErr_Print();
|
|
56
|
+
@panic("Unexpected PyErr!");
|
|
57
|
+
}
|
|
58
|
+
|
|
33
59
|
pub fn parse(self: [*]PyObject, args: [*]PyObject) callconv(.c) [*c]PyObject {
|
|
34
60
|
_ = self;
|
|
35
61
|
const zon_slice: [:0]u8 = zon_slice: {
|
|
@@ -38,7 +64,7 @@ pub fn parse(self: [*]PyObject, args: [*]PyObject) callconv(.c) [*c]PyObject {
|
|
|
38
64
|
break :zon_slice std.mem.sliceTo(zon_txt, 0);
|
|
39
65
|
};
|
|
40
66
|
|
|
41
|
-
var arena_instance = std.heap.ArenaAllocator.init(
|
|
67
|
+
var arena_instance = std.heap.ArenaAllocator.init(cpython.py_allocator);
|
|
42
68
|
defer arena_instance.deinit();
|
|
43
69
|
const arena = arena_instance.allocator();
|
|
44
70
|
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (Expat)
|
|
2
|
+
|
|
3
|
+
Copyright (c) Zig contributors
|
|
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.
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
# Zig Python Sdist
|
|
2
|
+
|
|
3
|
+
A toolset to build Python source distributions (`sdist`) and wheels directly with `zig build`. Inspired by Loris Cro's talk [Deinventing the Wheel](https://www.youtube.com/watch?v=HPmefnqirHk).
|
|
4
|
+
|
|
5
|
+
`zig-python-sdist` packages your Zig project into a standard `.tar.gz` source distribution for PyPI. When a user runs `pip install your-package`, `pip` automatically installs the `ziglang` compiler wheel from PyPI and invokes `zig build` to compile native extensions on the target machine—no manual Python packaging toolchain (C/C++ compilers, CMake, setup.py boilerplate) required.
|
|
6
|
+
|
|
7
|
+
A complete runnable example is available in the [`example/`](example/) directory.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
|
|
13
|
+
Add `zig-python-sdist` to your project's `build.zig.zon` (requires Zig >= 0.16.0):
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
zig fetch --save git+https://codeberg.org/dasimmet/zig-python-sdist
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Then import it in `build.zig`:
|
|
20
|
+
|
|
21
|
+
```zig
|
|
22
|
+
const zig_python_sdist = @import("zig_python_sdist");
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Usage Scenarios
|
|
28
|
+
|
|
29
|
+
`zig-python-sdist` supports two main workflows:
|
|
30
|
+
1. **CPython Native Extension**: A compiled module (`.so` / `.pyd`) using CPython headers and the Python C API (supporting the Python Limited API / `abi3`).
|
|
31
|
+
2. **Ctypes Wrapper**: A standalone shared library accompanied by Python files loaded via `ctypes`.
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
### 1. CPython Extension (Limited API / `abi3`)
|
|
36
|
+
|
|
37
|
+
#### `build.zig`
|
|
38
|
+
|
|
39
|
+
```zig
|
|
40
|
+
const std = @import("std");
|
|
41
|
+
const zig_python_sdist = @import("zig_python_sdist");
|
|
42
|
+
const zon = @import("build.zig.zon");
|
|
43
|
+
|
|
44
|
+
pub fn build(b: *std.Build) void {
|
|
45
|
+
const target = b.standardTargetOptions(.{});
|
|
46
|
+
const optimize = b.standardOptimizeOption(.{});
|
|
47
|
+
|
|
48
|
+
// 1. Step executed by pip at wheel build time
|
|
49
|
+
const build_ext_step = b.step("bdist-wheel-cpython", "build the extension module");
|
|
50
|
+
|
|
51
|
+
// 2. Generate CPython module and translate Python headers
|
|
52
|
+
const cpython = zig_python_sdist.createCPythonModule(b, .{
|
|
53
|
+
.target = target,
|
|
54
|
+
.optimize = optimize,
|
|
55
|
+
.version = zon.version,
|
|
56
|
+
.py_limited_api = "cp39", // sets Py_LIMITED_API for Python 3.9+
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// 3. Build the dynamic extension module
|
|
60
|
+
const example_cpython = b.addLibrary(.{
|
|
61
|
+
.name = "example_cpython",
|
|
62
|
+
.linkage = .dynamic,
|
|
63
|
+
.root_module = b.createModule(.{
|
|
64
|
+
.root_source_file = b.path("example_cpython.zig"),
|
|
65
|
+
.target = target,
|
|
66
|
+
.optimize = optimize,
|
|
67
|
+
.imports = &.{
|
|
68
|
+
.{ .name = "cpython", .module = cpython },
|
|
69
|
+
},
|
|
70
|
+
}),
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
// Install extension to zig-out/lib/
|
|
74
|
+
const example_cpython_install = zig_python_sdist.installExtLib(b, example_cpython);
|
|
75
|
+
build_ext_step.dependOn(&example_cpython_install.step);
|
|
76
|
+
|
|
77
|
+
// 4. Define the sdist archive
|
|
78
|
+
const sdist = zig_python_sdist.createSdistStep(b, .{
|
|
79
|
+
.name = "example-cpython",
|
|
80
|
+
.version = zon.version,
|
|
81
|
+
.description = b.path("README.md"),
|
|
82
|
+
.py_limited_api = "cp39", // tags wheel with cp39-abi3
|
|
83
|
+
.build = .{
|
|
84
|
+
.args = &.{
|
|
85
|
+
build_ext_step.name,
|
|
86
|
+
b.fmt("-Doptimize={s}", .{@tagName(optimize)}),
|
|
87
|
+
},
|
|
88
|
+
.copy_patterns = &.{
|
|
89
|
+
.{ .pattern = "zig-out/lib/*" },
|
|
90
|
+
.{ .pattern = "example_cpython.pyi" },
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
.files = &.{
|
|
94
|
+
.{ .src = b.path("build.zig.zon"), .dest = "build.zig.zon" },
|
|
95
|
+
.{ .src = b.path("build.zig"), .dest = "build.zig" },
|
|
96
|
+
.{ .src = b.path("example_cpython.zig"), .dest = "example_cpython.zig" },
|
|
97
|
+
.{ .src = b.path("example_cpython.pyi"), .dest = "example_cpython.pyi" },
|
|
98
|
+
.{ .src = b.path("README.md"), .dest = "README.md" },
|
|
99
|
+
.{ .src = b.path("LICENSE"), .dest = "LICENSE" },
|
|
100
|
+
},
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
// Building the default install step produces the .tar.gz sdist archive
|
|
104
|
+
const sdist_install = sdist.install();
|
|
105
|
+
b.getInstallStep().dependOn(&sdist_install.step);
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
#### `example_cpython.zig`
|
|
110
|
+
|
|
111
|
+
```zig
|
|
112
|
+
const std = @import("std");
|
|
113
|
+
const python = @import("cpython");
|
|
114
|
+
const c = python.c;
|
|
115
|
+
|
|
116
|
+
pub export fn PyInit_example_cpython() [*c]c.PyObject {
|
|
117
|
+
const mod = c.PyModule_Create(&zigmodule);
|
|
118
|
+
python.addVersion(mod);
|
|
119
|
+
python.addZigBuiltin(mod);
|
|
120
|
+
return mod;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
pub var zigmodule: c.PyModuleDef = .{
|
|
124
|
+
.m_name = "example_cpython",
|
|
125
|
+
.m_methods = @constCast(&[_]c.PyMethodDef{
|
|
126
|
+
.{
|
|
127
|
+
.ml_name = "__main__",
|
|
128
|
+
.ml_meth = @ptrCast(@alignCast(&python.wrapMain(main, .{}))),
|
|
129
|
+
.ml_flags = @as(c_int, 1),
|
|
130
|
+
.ml_doc = "main script entrypoint",
|
|
131
|
+
},
|
|
132
|
+
.{},
|
|
133
|
+
}),
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
pub fn main(init: std.process.Init) !void {
|
|
137
|
+
const stdout = std.Io.File.stdout();
|
|
138
|
+
var buf: [4096]u8 = undefined;
|
|
139
|
+
var w = stdout.writer(init.io, &buf);
|
|
140
|
+
try w.interface.print("Hello from Zig!\n", .{});
|
|
141
|
+
try w.interface.flush();
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
### 2. Ctypes Dynamic Library (Pure Wheel)
|
|
148
|
+
|
|
149
|
+
When distributing a shared library called through Python's `ctypes`, you can set `has_ext_modules = false` so setuptools produces a pure wheel (`py3-none-any.whl`), and use `package_dir = true` to place the library and `__init__.py` inside the package directory.
|
|
150
|
+
|
|
151
|
+
#### `build.zig`
|
|
152
|
+
|
|
153
|
+
```zig
|
|
154
|
+
const example_ctypes = b.addLibrary(.{
|
|
155
|
+
.name = "example_ctypes",
|
|
156
|
+
.root_module = b.createModule(.{
|
|
157
|
+
.root_source_file = b.path("example_ctypes.zig"),
|
|
158
|
+
.target = target,
|
|
159
|
+
.optimize = optimize,
|
|
160
|
+
.link_libc = false,
|
|
161
|
+
}),
|
|
162
|
+
.linkage = .dynamic,
|
|
163
|
+
});
|
|
164
|
+
example_ctypes.rdynamic = true;
|
|
165
|
+
|
|
166
|
+
const build_ctypes_step = b.step("bdist-wheel-ctypes", "build shared library");
|
|
167
|
+
build_ctypes_step.dependOn(&b.addInstallArtifact(example_ctypes, .{}).step);
|
|
168
|
+
|
|
169
|
+
const sdist_ctypes = zig_python_sdist.createSdistStep(b, .{
|
|
170
|
+
.name = "example-ctypes",
|
|
171
|
+
.version = zon.version,
|
|
172
|
+
.description = b.path("README.md"),
|
|
173
|
+
.has_ext_modules = false, // builds a py3-none-any wheel
|
|
174
|
+
.build = .{
|
|
175
|
+
.args = &.{
|
|
176
|
+
b.fmt("-Doptimize={s}", .{@tagName(optimize)}),
|
|
177
|
+
build_ctypes_step.name,
|
|
178
|
+
},
|
|
179
|
+
.copy_patterns = &.{
|
|
180
|
+
// places compiled shared library into example_ctypes/
|
|
181
|
+
.{ .pattern = "zig-out/*/*", .package_dir = true },
|
|
182
|
+
// renames example_ctypes.py to __init__.py inside example_ctypes/
|
|
183
|
+
.{ .pattern = "example_ctypes.py", .dest = "__init__.py", .package_dir = true },
|
|
184
|
+
},
|
|
185
|
+
},
|
|
186
|
+
.files = &.{
|
|
187
|
+
.{ .src = b.path("build.zig.zon"), .dest = "build.zig.zon" },
|
|
188
|
+
.{ .src = b.path("build.zig"), .dest = "build.zig" },
|
|
189
|
+
.{ .src = b.path("example_ctypes.zig"), .dest = "example_ctypes.zig" },
|
|
190
|
+
.{ .src = b.path("example_ctypes.py"), .dest = "example_ctypes.py" },
|
|
191
|
+
},
|
|
192
|
+
});
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
## API Reference
|
|
198
|
+
|
|
199
|
+
### `createCPythonModule(b, options)`
|
|
200
|
+
|
|
201
|
+
Generates a Zig module linked against CPython headers:
|
|
202
|
+
- `target`: Target architecture and OS.
|
|
203
|
+
- `optimize`: Optimization mode.
|
|
204
|
+
- `version`: Package version string.
|
|
205
|
+
- `py_limited_api`: Python tag (e.g., `"cp39"`, `"cp310"`) or hex version (e.g. `"0x03090000"`). When set, `#define Py_LIMITED_API` is enabled during header translation.
|
|
206
|
+
- `header_file`: Optional custom C header file (defaults to `python-translatec.h`).
|
|
207
|
+
- `python_exe`: Optional path to Python executable used for finding header search paths.
|
|
208
|
+
- `macros`: Additional C macro definitions.
|
|
209
|
+
|
|
210
|
+
### `createSdistStep(b, options)`
|
|
211
|
+
|
|
212
|
+
Creates an `SDistStep` that packages source files into an sdist archive:
|
|
213
|
+
- `name`: Distribution name on PyPI.
|
|
214
|
+
- `version`: Package version string.
|
|
215
|
+
- `description`: Path to README file (e.g. `b.path("README.md")`). Added to `PKG-INFO`.
|
|
216
|
+
- `py_limited_api`: Python tag (e.g. `"cp39"`) passed to setuptools to build an `abi3` wheel.
|
|
217
|
+
- `has_ext_modules`: Set to `false` for pure-Python or ctypes packages (defaults to `true`).
|
|
218
|
+
- `build`: Configuration for building wheels:
|
|
219
|
+
- `args`: Command-line arguments forwarded to `zig build` when `pip` builds the wheel.
|
|
220
|
+
- `copy_patterns`: Files and directories to copy into the wheel:
|
|
221
|
+
- `pattern`: Glob pattern (e.g. `"zig-out/lib/*"`).
|
|
222
|
+
- `dest`: Optional destination path or filename.
|
|
223
|
+
- `package_dir`: If `true`, prefixes destination with the package name.
|
|
224
|
+
- `packages`: Python packages to include.
|
|
225
|
+
- `requires`: Additional Python build dependencies.
|
|
226
|
+
- `files`: Slice of `.{ .src = LazyPath, .dest = []const u8 }` to include in the archive.
|
|
227
|
+
- `directories`: Slice of directory paths to include in the archive.
|
|
228
|
+
- `entry_points`: Command-line scripts, e.g. `.{ .console_scripts = &.{ "my-cli = my_pkg:__main__" } }`.
|
|
229
|
+
- `install_requires`: Slice of runtime dependencies (e.g. `&.{ "requests>=2.28" }`).
|
|
230
|
+
- `extra_pkg_info`: Extra metadata headers added to `PKG-INFO`.
|
|
231
|
+
- `compression`: Compression level (`.default`, `.best`, `.fast`).
|
|
232
|
+
|
|
233
|
+
> [!TIP]
|
|
234
|
+
> When your package uses dependencies from `build.zig.zon`, package the dependency directory into the sdist and pass `--fork=zig-pkg/<dep>` in `build.args` so `zig build` does not re-download it during pip install. `zig-python-sdist` automatically adds itself this way.
|
|
235
|
+
|
|
236
|
+
---
|
|
237
|
+
|
|
238
|
+
## The `cpython` Helper Library
|
|
239
|
+
|
|
240
|
+
The imported `cpython` module provides:
|
|
241
|
+
- **`c`**: Direct access to translated Python C API headers (`Python.h`).
|
|
242
|
+
- **`py_allocator`**: A `std.mem.Allocator` implementation backed by Python's `PyMem_RawMalloc` / `PyMem_RawFree`.
|
|
243
|
+
- **`wrapMain`**: Wraps a Zig main function (`fn (std.process.Init) !void` or `fn (std.process.Init.Minimal) !void`) into a CPython entrypoint function (`callconv(.c) [*c]PyObject`). CLI arguments and environment variables are automatically populated from Python's `sys.argv` and `os.environ`.
|
|
244
|
+
- **`addVersion`**: Adds `__version__` and `__version_str__` attributes to a Python module.
|
|
245
|
+
- **`addZigBuiltin`**: Adds a `__zig_builtin__` dictionary with Zig compiler target and build information.
|
|
246
|
+
|
|
247
|
+
---
|
|
248
|
+
|
|
249
|
+
## Testing Installations (`createVenvInstallTest`)
|
|
250
|
+
|
|
251
|
+
You can verify the source distribution end-to-end by building and installing it into a temporary virtual environment:
|
|
252
|
+
|
|
253
|
+
```zig
|
|
254
|
+
const test_pip = zig_python_sdist.createVenvInstallTest(b, .{
|
|
255
|
+
.sdist = sdist.getEmittedArchive(),
|
|
256
|
+
.unittest_py = &.{b.path("test/test_cpython_ext.py")},
|
|
257
|
+
.twine_check = true, // runs `twine check --strict` on the archive
|
|
258
|
+
.no_clean = b.option(bool, "pip-no-clean", "keep pip temp dirs") orelse false,
|
|
259
|
+
});
|
|
260
|
+
b.step("test-pip-install", "test installing sdist with pip").dependOn(test_pip.step);
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
Run tests with:
|
|
264
|
+
|
|
265
|
+
```bash
|
|
266
|
+
zig build test-pip-install
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
---
|
|
270
|
+
|
|
271
|
+
## References
|
|
272
|
+
|
|
273
|
+
- [Deinventing the Wheel - Loris Cro](https://www.youtube.com/watch?v=HPmefnqirHk)
|
|
274
|
+
- [Python C API - Limited API](https://docs.python.org/3/c-api/stable.html)
|
|
275
|
+
- [PEP 384 – Defining a Stable ABI](https://peps.python.org/pep-0384/)
|