不論是 setuptools 或是 distutils 都無法直接設定 C Extension package 的 version,必須採用紆迴的方式進行。
總而言之,Python 3.6 的年代,我們直接使用 setuptools。
0. 預期結果
1 2 3 |
>>> import jchash >>> jchash.__version__ ‘1.0’ |
1.原始狀況
原始 Init function:
1 2 3 4 5 |
PyMODINIT_FUNC PyInit_jchash(void) { return PyModule_Create(&module); } |
原始 setup.py
1 2 3 4 5 |
PACKAGE_VERSION = ‘1.0’ jchash = Extension(PACKAGE_NAME, define_macros=[ (‘PACKAGE_VERSION’, PACKAGE_VERSION)], sources=[‘src/jchash.c’]) |
只有這樣是不夠的,Extension 的 define_macros 是在編譯時會加上 -DPACKAGE_VERSION=1.0 這樣的 flag,可是不會幫你轉換成為 package 的 version。
2.改用 PyModule_AddStringConstant
參考資料:PyModule_AddStringConstant
如果用 Python 官方教學的方式初始化模組,就沒有辦法加上 __version__ 的部份,必須要分解步驟:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
PyMODINIT_FUNC PyInit_jchash(void) { PyObject *m; m = PyModule_Create(&jchash_module); if (m == NULL) { return NULL; } if (PyModule_AddStringConstant(m, “__version__”, PACKAGE_VERSION)) { Py_DECREF(m); return NULL; } return m; } |
如此一來,透過編譯器設定好的 PACKAGE_VERSION,應該就能夠設定成功。
3. 紆迴將 Macro 的變成字串
前面 define_macros 的部份將 PACKAGE_VERSION 設定為 ‘2.0’,在編譯器上會變成 -DPACKAGE_VERSION=2.0,當這個 macro 在 C 裏面被解析的時候,型態會變成 double。而 PyModule_AddStringConstant() 的第3個參數需要 const char *,會造成型態不符合的情況。
1 2 3 4 5 6 7 8 9 10 11 |
src/jchash.c: In function ‘PyInit_jchash’: <command–line>:0:17: error: incompatible type for argument 3 of ‘PyModule_AddStringConstant’ src/jchash.c:90:54: note: in expansion of macro ‘PACKAGE_VERSION’ if (PyModule_AddStringConstant(m, “__version__”, PACKAGE_VERSION)) { ^~~~~~~~~~~~~~~ In file included from /usr/include/python3.5m/Python.h:114:0, from src/jchash.c:1: /usr/include/python3.5m/modsupport.h:49:17: note: expected ‘const char *’ but argument is of type ‘double’ PyAPI_FUNC(int) PyModule_AddStringConstant(PyObject *, const char *, const char *); ^~~~~~~~~~~~~~~~~~~~~~~~~~ error: command ‘gcc’ failed with exit status 1 |
我們可以透過再定義兩個 macro 解決這個問題:
1 2 3 4 |
define_macros=[ (‘XSTR(s)’, ‘STR(s)’), (‘STR(s)’, ‘#s’), (‘PACKAGE_VERSION’, PACKAGE_VERSION)] |
在 gcc Stringification 的地方有提到這個作法:
1 2 3 4 5 6 7 8 9 |
#define xstr(s) str(s) #define str(s) #s #define foo 4 str (foo) ==> “foo” xstr (foo) ==> xstr (4) ==> str (4) ==> “4” |
因此在程式中改變為:
1 2 3 4 |
if (PyModule_AddStringConstant(m, “__version__”, XSTR(PACKAGE_VERSION))) { Py_DECREF(m); return NULL; } |
就能夠把 PACKAGE_VERSION 改為字串。
4. 柳暗花明又一村,Macro 加上雙引號
回頭一想 f9-kernel 裏面也有字串的用法,為什麼沒有 XSTR 出現呢?翻 code 才發現可以這樣設定:
1 2 3 4 |
CFLAGS_MISC_DEFINE = \ –DGIT_HEAD=\“$(GIT_HEAD)\“ \ –DMACH_TYPE=\“$(MACH_TYPE)\“ \ –DBUILD_TIME=\“$(BUILD_TIME)\“ |
也就是說,先加上雙引號,在 C 裏面使用就會是 string。
1 2 3 4 5 |
jchash = Extension(PACKAGE_NAME, define_macros=[ (‘JCHASH_MODULE_NAME’, ‘”%s”‘ % (PACKAGE_NAME)), (‘PACKAGE_VERSION’, ‘”%s”‘ % (PACKAGE_VERSION))], sources=[‘src/jchash.c’]) |
打包收工。
Leave a Reply