在 C 中使用嵌入式 Cython 模块编写自定义 main() 函数

Writing a custom main() function with Embedded Cython modules in C

我是 Cython 世界的新手,但我很了解 C 和 Python。我正在尝试使用 Cython 将 Python 代码编译成 C,但我需要在 C 中编写自己的 main() 函数。

我在网上只找到了一个代码示例,该代码是从 C 程序编译而来的,该程序调用了 Python/Cython 中编写的函数,但他们没有告诉您如何编译它。请在此处查看该示例:https://docs.cython.org/en/latest/src/tutorial/embedding.html

明确地说,我更希望能够编译 Python 代码并从 C 调用它,而不是 运行在 C 中使用 Python 解释器。

为了自己编译,我正在使用 Rocky Linux 运行 宁默认 Python 版本 (3.6.8)。

我使用的是上面示例中引用的两个代码,没有进行任何修改。为了编译这些代码,我从示例中提到的 github link 的 UNIX Makefile 开始,并添加了一行代码来编译“embedded_main.c”代码:

# Makefile for creating our standalone Cython program
PYTHON := python3
PYVERSION := $(shell $(PYTHON) -c "import sys; print(sys.version[:3])")
PYPREFIX := $(shell $(PYTHON) -c "import sys; print(sys.prefix)")

INCDIR := $(shell $(PYTHON) -c "from distutils import sysconfig; print(sysconfig.get_python_inc())")
PLATINCDIR := $(shell $(PYTHON) -c "from distutils import sysconfig; print(sysconfig.get_python_inc(plat_specific=True))")
LIBDIR1 := $(shell $(PYTHON) -c "from distutils import sysconfig; print(sysconfig.get_config_var('LIBDIR'))")
LIBDIR2 := $(shell $(PYTHON) -c "from distutils import sysconfig; print(sysconfig.get_config_var('LIBPL'))")
PYLIB := $(shell $(PYTHON) -c "from distutils import sysconfig; print(sysconfig.get_config_var('LIBRARY')[3:-2])")

CC := $(shell $(PYTHON) -c "import distutils.sysconfig; print(distutils.sysconfig.get_config_var('CC'))")
LINKCC := $(shell $(PYTHON) -c "import distutils.sysconfig; print(distutils.sysconfig.get_config_var('LINKCC'))")
LINKFORSHARED := $(shell $(PYTHON) -c "import distutils.sysconfig; print(distutils.sysconfig.get_config_var('LINKFORSHARED'))")
LIBS := $(shell $(PYTHON) -c "import distutils.sysconfig; print(distutils.sysconfig.get_config_var('LIBS'))")
SYSLIBS :=  $(shell $(PYTHON) -c "import distutils.sysconfig; print(distutils.sysconfig.get_config_var('SYSLIBS'))")

.PHONY: paths all clean test

paths:
    @echo "PYTHON=$(PYTHON)"
    @echo "PYVERSION=$(PYVERSION)"
    @echo "PYPREFIX=$(PYPREFIX)"
    @echo "INCDIR=$(INCDIR)"
    @echo "PLATINCDIR=$(PLATINCDIR)"
    @echo "LIBDIR1=$(LIBDIR1)"
    @echo "LIBDIR2=$(LIBDIR2)"
    @echo "PYLIB=$(PYLIB)"
    @echo "CC=$(CC)"
    @echo "LINKCC=$(LINKCC)"
    @echo "LINKFORSHARED=$(LINKFORSHARED)"
    @echo "LIBS=$(LIBS)"
    @echo "SYSLIBS=$(SYSLIBS)"

embedded: embedded.o
    $(LINKCC) -o $@ $^ -L$(LIBDIR1) -L$(LIBDIR2) -l$(PYLIB) $(LIBS) $(SYSLIBS) $(LINKFORSHARED)

embedded.o: embedded.c
    $(CC) -c $^ -I$(INCDIR) -I$(PLATINCDIR)

### Added to compile custom C function, 'embedded_main.c' ###
embedded_main: embedded.o embedded_main.c
    gcc -o $@ $^ -L/usr/inlcude/python3.6m/ -lpython3.6m -I /usr/include/python3.6m/
#############################################################


CYTHON := cython.py
embedded.c: embedded.pyx
    @$(PYTHON) $(CYTHON) --embed embedded.pyx

all: embedded

clean:
    @echo Cleaning Demos/embed
    @rm -f *~ *.o *.so core core.* embedded.c embedded test.output

test: clean all
    LD_LIBRARY_PATH=$(LIBDIR1):$$LD_LIBRARY_PATH ./embedded > test.output
    $(PYTHON) assert_equal.py embedded.output test.output

当我在终端中 运行 “make embedded_main” 时,我得到以下信息:

$ make embedded_main
gcc -o embedded_main embedded.o embedded_main.c -L/usr/include/python3.6m/ -python3.6m -I /usr/include/python3.6m/
/tmp/ccTNbcJ0.o: In function `main':
embedded_main.c:(.text+0x0): multiple definition of `main'
embedded.o:embedded.c:(.text+0x1c07): first defined here
collect2: error: ld returned 1 exit status
make: *** [Makefile:43: embedded_main] Error 1

我知道 Cython 创建了带有 main() 结构的“embedded.c”,但我希望能够用上述“embedded_main.c 代替 link 这个 c 程序”。任何帮助将不胜感激!

在 DavidW 的评论的帮助下,我从 embedded.c 行中删除了 --embed 标志。这是经过这个小改动后的工作 makefile:

# Makefile for creating our standalone Cython program
PYTHON := python3
PYVERSION := $(shell $(PYTHON) -c "import sys; print(sys.version[:3])")
PYPREFIX := $(shell $(PYTHON) -c "import sys; print(sys.prefix)")

INCDIR := $(shell $(PYTHON) -c "from distutils import sysconfig; print(sysconfig.get_python_inc())")
PLATINCDIR := $(shell $(PYTHON) -c "from distutils import sysconfig; print(sysconfig.get_python_inc(plat_specific=True))")
LIBDIR1 := $(shell $(PYTHON) -c "from distutils import sysconfig; print(sysconfig.get_config_var('LIBDIR'))")
LIBDIR2 := $(shell $(PYTHON) -c "from distutils import sysconfig; print(sysconfig.get_config_var('LIBPL'))")
PYLIB := $(shell $(PYTHON) -c "from distutils import sysconfig; print(sysconfig.get_config_var('LIBRARY')[3:-2])")

CC := $(shell $(PYTHON) -c "import distutils.sysconfig; print(distutils.sysconfig.get_config_var('CC'))")
LINKCC := $(shell $(PYTHON) -c "import distutils.sysconfig; print(distutils.sysconfig.get_config_var('LINKCC'))")
LINKFORSHARED := $(shell $(PYTHON) -c "import distutils.sysconfig; print(distutils.sysconfig.get_config_var('LINKFORSHARED'))")
LIBS := $(shell $(PYTHON) -c "import distutils.sysconfig; print(distutils.sysconfig.get_config_var('LIBS'))")
SYSLIBS :=  $(shell $(PYTHON) -c "import distutils.sysconfig; print(distutils.sysconfig.get_config_var('SYSLIBS'))")

.PHONY: paths all clean test

paths:
    @echo "PYTHON=$(PYTHON)"
    @echo "PYVERSION=$(PYVERSION)"
    @echo "PYPREFIX=$(PYPREFIX)"
    @echo "INCDIR=$(INCDIR)"
    @echo "PLATINCDIR=$(PLATINCDIR)"
    @echo "LIBDIR1=$(LIBDIR1)"
    @echo "LIBDIR2=$(LIBDIR2)"
    @echo "PYLIB=$(PYLIB)"
    @echo "CC=$(CC)"
    @echo "LINKCC=$(LINKCC)"
    @echo "LINKFORSHARED=$(LINKFORSHARED)"
    @echo "LIBS=$(LIBS)"
    @echo "SYSLIBS=$(SYSLIBS)"

embedded: embedded.o
    $(LINKCC) -o $@ $^ -L$(LIBDIR1) -L$(LIBDIR2) -l$(PYLIB) $(LIBS) $(SYSLIBS) $(LINKFORSHARED)

embedded.o: embedded.c
    $(CC) -c $^ -I$(INCDIR) -I$(PLATINCDIR)

### Added to compile custom C function, 'embedded_main.c' ###
embedded_main: embedded.o embedded_main.c
    gcc -o $@ $^ -L/usr/inlcude/python3.6m/ -lpython3.6m -I /usr/include/python3.6m/
#############################################################


CYTHON := cython.py
embedded.c: embedded.pyx
    @$(PYTHON) $(CYTHON) embedded.pyx

all: embedded

clean:
    @echo Cleaning Demos/embed
    @rm -f *~ *.o *.so core core.* embedded.c embedded test.output

test: clean all
    LD_LIBRARY_PATH=$(LIBDIR1):$$LD_LIBRARY_PATH ./embedded > test.output
    $(PYTHON) assert_equal.py embedded.output test.output