# 4. C와 C++ 확장 빌드하기

CPython의 C 확장은 초기화 함수를 내보내는 공유 라이브러리입니다 (예를 들어, 리눅스는 `.so`, 윈도우는 `.pyd`).

임포트 할 수 있으려면, 공유 라이브러리가 `PYTHONPATH`에 있어야 하며, 모듈 이름을 따라 적절한 확장자를 붙여서 이름 지어야 합니다. distutils를 사용하면, 올바른 파일 이름이 자동으로 생성됩니다.

초기화 함수는 다음과 같은 서명을 갖습니다:

PyObject *`PyInit_modulename`(void)

It returns either a fully initialized module, or a `PyModuleDef` instance. See C 모듈 초기화 for details.

ASCII로만 이루어진 이름을 가진 모듈의 경우, 함수의 이름을 `PyInit_<modulename>`이어야 합니다. 여기서 `<modulename>`을 모듈의 이름으로 치환합니다. 다단계 초기화를 사용할 때 ASCII가 아닌 모듈 이름이 허용됩니다. 이 경우, 초기화 함수 이름은 `PyInitU_<modulename>`이며 `<modulename>`은 파이썬의 punycode 인코딩으로 인코딩되고 하이픈을 밑줄로 대체합니다. 파이썬에서:

    def initfunc_name(name):
        try:
            suffix = b'_' + name.encode('ascii')
        except UnicodeEncodeError:
            suffix = b'U_' + name.encode('punycode').replace(b'-', b'_')
        return b'PyInit' + suffix

여러 초기화 함수를 정의하여 단일 공유 라이브러리에서 여러 모듈을 내보낼 수 있습니다. 그러나, 이들을 임포트 하려면 심볼릭 링크나 사용자 정의 임포터를 사용해야 합니다. 기본적으로 파일 이름에 해당하는 함수만 발견되기 때문입니다. 자세한 내용은 PEP 489의 “한 라이브러리에 여러 모듈” 절을 참조하십시오.

## 4.1. distutils로 C와 C++ 확장 빌드하기

확장 모듈은 파이썬에 포함된 distutils를 사용하여 빌드할 수 있습니다. distutils가 바이너리 패키지의 생성을 지원하기 때문에, 사용자는 확장을 설치하기 위해 꼭 컴파일러와 distutils가 필요하지는 않습니다.

distutils 패키지에는 드라이버 스크립트인 `setup.py`가 들어 있습니다. 이것은 평범한 파이썬 파일인데, 대부분 간단한 경우에 이런 식입니다:

    from distutils.core import setup, Extension

    module1 = Extension('demo',
                        sources = ['demo.c'])

    setup (name = 'PackageName',
           version = '1.0',
           description = 'This is a demo package',
           ext_modules = [module1])

이 `setup.py`와 파일 `demo.c`로 다음을 실행하면

    python setup.py build

`demo.c`를 컴파일하고, `build` 디렉터리에 `demo`라는 확장 모듈을 생성합니다. 시스템에 따라, 모듈 파일은 `build/lib.system` 하위 디렉터리에 들어가고, `demo.so`나 `demo.pyd`와 같은 이름을 가질 수 있습니다.

`setup.py`에서, 모든 실행은 `setup` 함수를 호출하여 수행됩니다. 이것은 다양한 키워드 인자를 받아들입니다. 위의 예에서는 일부만 사용합니다. 구체적으로, 이 예는 패키지를 빌드하기 위한 메타 정보를 지정하고 패키지의 내용을 지정합니다. 일반적으로, 패키지는 파이썬 소스 모듈, 문서, 서브 패키지 등과 같은 추가 모듈이 포함됩니다. distutils의 기능에 대한 자세한 내용은 파이썬 모듈 배포 (레거시 버전)의 distutils 설명서를 참조하십시오; 이 절에서는 확장 모듈을 빌드하는 것만 설명합니다.

드라이버 스크립트를 더 잘 구조화하기 위해, `setup()`에 대한 인자를 미리 계산하는 것이 일반적입니다. 위의 예에서, `setup()`에 대한 `ext_modules` 인자는 확장 모듈의 리스트며, 각 모듈은 `Extension`의 인스턴스입니다. 이 예에서, 인스턴스는 단일 소스 파일 `demo.c`를 컴파일하여 빌드하는 `demo`라는 확장을 정의합니다.

많은 경우, 확장을 빌드하는 것은 더 복잡합니다. 왜냐하면, 추가적인 전처리기 정의와 라이브러리가 필요할 수 있기 때문입니다. 이는 아래에서 예시합니다.

    from distutils.core import setup, Extension

    module1 = Extension('demo',
                        define_macros = [('MAJOR_VERSION', '1'),
                                         ('MINOR_VERSION', '0')],
                        include_dirs = ['/usr/local/include'],
                        libraries = ['tcl83'],
                        library_dirs = ['/usr/local/lib'],
                        sources = ['demo.c'])

    setup (name = 'PackageName',
           version = '1.0',
           description = 'This is a demo package',
           author = 'Martin v. Loewis',
           author_email = 'martin@v.loewis.de',
           url = 'https://docs.python.org/extending/building',
           long_description = '''
    This is really just a demo package.
    ''',
           ext_modules = [module1])

이 예에서, `setup()`는 추가 메타 정보로 호출되며, 배포 패키지를 빌드해야 할 때 권장됩니다. 확장 자체에 대해서는, 전처리기 정의, 인클루드 디렉터리, 라이브러리 디렉터리 및 라이브러리를 지정합니다. 컴파일러에 따라, distutils는 이 정보를 다양한 방법으로 컴파일러에 전달합니다. 예를 들어, 유닉스에서는 다음과 같은 컴파일 명령으로 이어질 수 있습니다

    gcc -DNDEBUG -g -O3 -Wall -Wstrict-prototypes -fPIC -DMAJOR_VERSION=1 -DMINOR_VERSION=0 -I/usr/local/include -I/usr/local/include/python2.2 -c demo.c -o build/temp.linux-i686-2.2/demo.o

    gcc -shared build/temp.linux-i686-2.2/demo.o -L/usr/local/lib -ltcl83 -o build/lib.linux-i686-2.2/demo.so

이 줄은 예시 목적일 뿐입니다; distutils 사용자는 distutils가 올바르게 호출한다고 믿어야 합니다.

## 4.2. 확장 모듈 배포하기

확장이 성공적으로 빌드되면, 이를 사용하는 세 가지 방법이 있습니다.

최종 사용자는 보통 모듈을 설치하고 싶을 것이고, 다음을 실행합니다

    python setup.py install

모듈 관리자는 소스 패키지를 생성해야 합니다; 그러려면, 이렇게 실행합니다

    python setup.py sdist

때에 따라, 추가 파일을 소스 배포에 포함해야 합니다; 이 작업은 `MANIFEST.in` 파일을 통해 수행됩니다; 자세한 내용은 배포할 파일 지정하기를 참조하십시오.

소스 배포가 성공적으로 빌드되면, 관리자는 바이너리 배포도 만들 수 있습니다. 플랫폼에 따라, 이를 위해 다음 명령 중 하나를 사용할 수 있습니다.

    python setup.py bdist_rpm
    python setup.py bdist_dumb