Metadata-Version: 2.1
Name: setuptools-zig
Version: 0.1.1
Summary: A setuptools extension, for building cpython extensions with Zig.
Home-page: https://sourceforge.net/p/setuptools-zig/code/ref/default/
Author-email: a.van.der.neut@ruamel.eu
License: MIT
Platform: UNKNOWN
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.6
Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: Implementation :: CPython
Requires-Python: >=3.6.5
Description-Content-Type: text/x-rst


Setuptools-Zig
==============

.. image:: https://sourceforge.net/p/setuptools-zig/code/ci/default/tree/_doc/_static/license.svg?format=raw
   :target: https://opensource.org/licenses/MIT

.. image:: https://sourceforge.net/p/setuptools-zig/code/ci/default/tree/_doc/_static/pypi.svg?format=raw
   :target: https://pypi.org/project/pon/

.. image:: https://sourceforge.net/p/oitnb/code/ci/default/tree/_doc/_static/oitnb.svg?format=raw
   :target: https://bitbucket.org/ruamel/oitnb/


A setuptools extension for building cpython extensions written in Zig and/or C with the `Zig compiler <https://ziglang.org>`_.

This extension expects to find the ``zig`` command in your ``PATH``. If it is not
there, or if you need to select a specific version, you can set the environment
variable ``PY_ZIG`` to the full path of the executable. E.g.::

   PY_VER=/usr/local/bin/zig

This module has been developed with Zig 7.0.0, but should work with other versions (as long as you adapt your Zig code).


The package ``setuptools-zig`` is available on PyPI, but doesn't need to be
installed as it is a setup requirement. Once your ``setup.py`` has the apropriate
entries, building an ``sdist`` or ``bdist_wheel`` will automatically downloaded the
package (cached in the .eggs directory).

Setup.py
++++++++

Your ``setup.py`` file should look like::

  from setuptools import Extension
  from setuptools import setup

  setup(
      name=NAME,
      version='MAJ.MIN.PATCH',
      python_requires='>=3.6.5',
      build_zig=True,
      ext_modules=[Extension(NAME, [XX1, XX2])],
      setup_requires=['setuptools-zig'],
  )

with ``NAME`` replaced by the string that is your package name. MAJ, MIN, and PATCH
your package's version, and XX1, XX2 being your source files (you can have just
one, or more).

With that adapted to your project::

  python setup.py bdist_wheel

will result in a ``.whl`` file in your ``dist`` directory. That wheel file can be installed in a virtualenv,
and the functions defined in the package imported and used.


Using Zig as a C compiler
+++++++++++++++++++++++++

Create your ``setup.py``::

  from setuptools import Extension
  from setuptools import setup

  setup(
      name='c_sum',
      version='1.0.0',
      python_requires='>=3.6.5',
      build_zig=True,
      ext_modules=[Extension('c_sum', ['sum.c', ])],
      setup_requires=['setuptools-zig'],

and ``sum.c``::

  /* based on https://docs.python.org/3.9/extending/extending.html */

  #define PY_SSIZE_T_CLEAN
  #include <Python.h>

  PyObject* sum(PyObject* self, PyObject* args) {
      long a, b;

      if (!PyArg_ParseTuple(args, "ll", &a, &b))
  	      return NULL;
      return PyLong_FromLong(a+b);
  }


  static struct PyMethodDef methods[] = {
      {"sum", (PyCFunction)sum, METH_VARARGS},
      {NULL, NULL}
  };

  static struct PyModuleDef zigmodule = {
      PyModuleDef_HEAD_INIT,
      "sum",
      NULL,
      -1,
      methods
  };

  PyMODINIT_FUNC PyInit_c_sum(void) {
      return PyModule_Create(&zigmodule);

install the resulting wheel using ``pip`` and use::

  python -c "from c_sum import sum; print(sum(20, 22))"

Using Zig with .zig and .c
++++++++++++++++++++++++++

The Zig compiler can easily mix and match here we use the C code to provide the
interface and do the heavy lifting of calculating the sum in Zig.

``setup.py``::

  from setuptools import Extension
  from setuptools import setup

  setup(
      name='c_zig_sum',
      version='1.0.0',
      python_requires='>=3.6.5',
      build_zig=True,
      ext_modules=[Extension('c_zig_sum', ['c_int.c', 'sum.zig', ])],
      setup_requires=['setuptools-zig'],
  )

``c_int.c``::

  /* based on https://docs.python.org/3.9/extending/extending.html */

  #define PY_SSIZE_T_CLEAN
  #include <Python.h>

  PyObject* sum(PyObject* , PyObject*);

  /*
  PyObject* sum(PyObject* self, PyObject* args) {
      long a, b;

      if (!PyArg_ParseTuple(args, "ll", &a, &b))
          return NULL;
      return PyLong_FromLong(a+b);
  }
  */


  static struct PyMethodDef methods[] = {
      {"sum", (PyCFunction)sum, METH_VARARGS},
      {NULL, NULL}
  };

  static struct PyModuleDef zigmodule = {
      PyModuleDef_HEAD_INIT,
      "c_zig_sum",
      NULL,
      -1,
      methods
  };

  PyMODINIT_FUNC PyInit_c_zig_sum(void) {
      return PyModule_Create(&zigmodule);
  }

``sum.zig``::

  const c = @cImport({
      @cDefine("PY_SSIZE_T_CLEAN", "1");
      @cInclude("Python.h");
  });

  pub export fn sum(self: [*]c.PyObject, args: [*]c.PyObject) [*c]c.PyObject {
      var a: c_long = undefined;
      var b: c_long = undefined;
      if (!(c._PyArg_ParseTuple_SizeT(args, "ll", &a, &b) != 0)) return null;
      return c.PyLong_FromLong((a + b));


Zig code only
+++++++++++++

This code is rather ugly, and is only tested using Python 3.8. A different Python version
might need different setup, which for C is abstracted from by the version
dependent ``Python.h`` file.

``setup.py``::

  from setuptools import Extension
  from setuptools import setup

  setup(
      name='zig_sum',
      version='1.0.0',
      python_requires='>=3.6.5',
      build_zig=True,
      ext_modules=[Extension('zig_sum', ['sum.zig' ])],
      setup_requires=['setuptools-zig'],
  )

``sum.zig``::

  // translated using zig from the C version

  const c = @cImport({
      @cDefine("PY_SSIZE_T_CLEAN", "1");
      @cInclude("Python.h");
  });

  const PyObject = c.PyObject;

  pub export fn sum(self: [*]PyObject, args: [*]PyObject) [*c]PyObject {
      var a: c_long = undefined;
      var b: c_long = undefined;
      if (!(c._PyArg_ParseTuple_SizeT(args, "ll", &a, &b) != 0)) return null;
      _ = c.printf("here5\n");
      return c.PyLong_FromLong((a + b));
  }

  pub var methods = [_]c.struct_PyMethodDef{
      c.struct_PyMethodDef{
          .ml_name = "sum",
          .ml_meth = @ptrCast(c.PyCFunction, @alignCast(@alignOf(fn ([*]PyObject, [*]PyObject) callconv(.C) [*]PyObject),
              sum)),
          .ml_flags = @as(c_int, 1),
          .ml_doc = null,
      },
      c.struct_PyMethodDef{
          .ml_name = null,
          .ml_meth = null,
          .ml_flags = 0,
          .ml_doc = null,
      },
  };

  pub var zigmodule = c.struct_PyModuleDef{
      .m_base = c.PyModuleDef_Base{
          .ob_base = c.PyObject{
              .ob_refcnt = 1, // @bitCast(c.Py_ssize_t, @as(c_long, @as(c_int, 1))),
              .ob_type = null,
          },
          .m_init = null,
          .m_index = 0,  //  @bitCast(c.Py_ssize_t, @as(c_long, @as(c_int, 0))),
          .m_copy = null,
      },
      .m_name = "zig_sum",
      .m_doc = null,
      .m_size = -1, // @bitCast(c.Py_ssize_t, @as(c_long, -@as(c_int, 1))),
      .m_methods = &methods,
      .m_slots = null,
      .m_traverse = null,
      .m_clear = null,
      .m_free = null,
  };

  pub export fn PyInit_zig_sum() [*c]c.PyObject {
      return c.PyModule_Create2(&zigmodule, @as(c_int, 1013));
  }


