2010-04-13 8 views
7

Mam pakiet Pythona z kodem C potrzebnym do zbudowania rozszerzenia (z pewnymi nietrywialnymi potrzebami budowlanymi). Użyłem SCons jako mojego systemu kompilacji, ponieważ jest naprawdę dobry i elastyczny.Używanie SCons jako kompilatora dla distutils

Szukam sposobu na kompilację rozszerzeń Pythona za pomocą programów SCon gotowych do dystrybucji za pomocą distutils. Chcę, aby użytkownik po prostu wpisał setup.py i uzyskał rozszerzenie skompilowane za pomocą SCons zamiast domyślnego silnika kompilacji distutils.

Pomysł, który przychodzi do głowy, to przedefiniowanie polecenia build_ext w distutils, ale nie mogę znaleźć dla niego obszernej dokumentacji.

Jakieś sugestie?

Odpowiedz

2

enscons package wydaje się być tak zaprojektowane, aby robić to, co zadał pytanie. Przykład użycia go do zbudowania pakietu z rozszerzeniami C to here.

Można mieć podstawową strukturę pakietów takiego jak:

pkgroot/ 
    pyproject.toml 
    setup.py 
    SConstruct 
    README.md 
    pkgname/ 
     __init__.py 
     pkgname.py 
     cfile.c 

W ten plik, pyproject.toml może wyglądać mniej więcej tak:

[build-system] 
requires = ["enscons"] 

[tool.enscons] 
name = "pkgname" 
description = "My nice packahe" 
version = "0.0.1" 
author = "Me" 
author_email = "[email protected]" 
keywords = ["spam"] 
url = "https://github.com/me/pkgname" 
src_root = "" 
packages = ["pkgname"] 

gdzie sekcja [tool.enscons] zawiera wiele rzeczy znanych do setuptools/distutils setup funkcji. Kopiowanie z here funkcja setup.py może zawierać coś takiego:

#!/usr/bin/env python 

# Call enscons to emulate setup.py, installing if necessary. 

import sys, subprocess, os.path 

sys.path[0:0] = ['setup-requires'] 

try: 
    import enscons.setup 
except ImportError: 
    requires = ["enscons"] 
    subprocess.check_call([sys.executable, "-m", "pip", "install", 
     "-t", "setup-requires"] + requires) 
    del sys.path_importer_cache['setup-requires'] # needed if setup-requires was absent 
    import enscons.setup 

enscons.setup.setup() 

Wreszcie, plik SConstruct mógłby wyglądać następująco:

# Build pkgname 

import sys, os 
import pytoml as toml 
import enscons, enscons.cpyext 

metadata = dict(toml.load(open('pyproject.toml')))['tool']['enscons'] 

# most specific binary, non-manylinux1 tag should be at the top of this list 
import wheel.pep425tags 
full_tag = next(tag for tag in wheel.pep425tags.get_supported() if not 'manylinux' in tag) 

env = Environment(tools=['default', 'packaging', enscons.generate, enscons.cpyext.generate], 
        PACKAGE_METADATA=metadata, 
        WHEEL_TAG=full_tag) 

ext_filename = os.path.join('pkgname', 'libcfile') 

extension = env.SharedLibrary(target=ext_filename, 
           source=['pkgname/cfile.c']) 

py_source = Glob('pkgname/*.py') 

platlib = env.Whl('platlib', py_source + extension, root='') 
whl = env.WhlFile(source=platlib) 

# Add automatic source files, plus any other needed files. 
sdist_source=list(set(FindSourceFiles() + 
    ['PKG-INFO', 'setup.py'] + 
    Glob('pkgname/*', exclude=['pkgname/*.os']))) 

sdist = env.SDist(source=sdist_source) 
env.Alias('sdist', sdist) 

install = env.Command("#DUMMY", whl, 
         ' '.join([sys.executable, '-m', 'pip', 'install', '--no-deps', '$SOURCE'])) 
env.Alias('install', install) 
env.AlwaysBuild(install) 

env.Default(whl, sdist) 

Po tym powinieneś być w stanie uruchomić

sudo python setup.py install 

, aby skompilować rozszerzenie C i zbudować koło, i zainstalować pakiet python, lub

python setup.py sdist 

zbudować dystrybucję źródła.

Myślę, że zasadniczo można zrobić wszystko, co można z SCons w pliku SConstruct.

+0

Zamiast publikowania linków jako odpowiedzi dodaj tekst wyjaśniający, w jaki sposób ta odpowiedź pomaga OP w naprawianiu bieżącego wydania. Dzięki –

+0

@ ρяσѕρєяK dodałem bardziej konkretny przykład wywodzący się z linków. –

+0

Myślę, że @joeforker jest autorem enscons, więc mogliby dodać do tej odpowiedzi. –

1

Zobacz stronę: http://www.scons.org/wiki/PythonExtensions

używam wersji nieznacznie zmodyfikowanej zbudować rozszerzeń Pyrex-C dla Pythona.

+0

Moje pytanie dotyczy raczej tego (skompilowanie rozszerzenia Pythona) w skrypcie setup.py w najczystszy możliwy sposób. W ten sposób mogę łatwo dystrybuować moją aplikację. – pygabriel

+0

@pygabriel Aby skompilować rozszerzenia C, nie potrzebujesz SConów. W ogóle. – bialix

+2

@bialix, Nie chodzi o to, że potrzebujemy scons, czy nie, na przykład mam projekt już używający scons do budowania ... Jak miałbym go podłączyć do distutils, aby napisać tylko "python setup.py install" lub rozwijać i distutils byłoby po prostu dzwonić scons –

0

Używam scons do generowania pliku setup.py. Stworzyłem więc szablon o nazwie setup.py.in i używam scons, aby rozwinąć szablon i wygenerować plik setup.py.

Oto kilka linków do mojego projektu, który to robi:

setup.py.in template

The SConstruct

ten oblicza słownika klucz, wartość pary zastąpił w szablonie setup.py.in generować setup.py.

więc użytkownik końcowy robi dwie rzeczy:

scons setup.py 
python setup.py install 

Warning: mój sconstruct rzeczy jest nieco niechlujny jak napisałem go chwilę temu, ale powinna wykazać koncepcji.

Jeśli nauczyć się pisać odpowiednie SCons narzędzi, to może być przekształcony w jeden cel, na przykład:

scons --pymod 

Oto narzędzie scons generuje otoki Python z SWIG:

import SCons.Action 
from SCons.Script import EnsureSConsVersion 

SCons.Script.EnsureSConsVersion(0,96,92) 

SwigGenAction = SCons.Action.Action('$SWIGGENCOM', '$SWIGGENCOMSTR') 

def emitter(target, source, env): 
    """ 
    Add dependency from target to source 
    """ 

    env.Depends(target, source) 

    return target, source 


def generate(env): 
    """ 
    Add builders and construction variables for the SwigGen builder. 
    """ 

    if 'SWIGCOM' not in env: 
     raise SystemError("SCons build environment could not detect tool: swig") 

    bld = env.Builder(
     action = SwigGenAction, 
     emitter = emitter, 
     target_factory = env.fs.File) 

    env['BUILDERS']['SwigGen'] = bld 

    env['SWIGGENCOM'] = env['SWIGCOM'] 


def exists(env): 
    return env.Detect('swig') 

teraz z pliku SConstruct można wygenerować kod wrapper:

foobar_cc = env.SwigGen("foobar_wrap.cc", "foobar.i") 

W przypadku modyfikacji foobar.i nastąpi regeneracja foobar_wrap.cc. Kiedy już to zrobisz, możesz napisać inne narzędzia do faktycznego uruchamiania instalacji pytona setup.py, więc jeśli podasz --pymod, to zbudujesz moduł python.

0

Rozwiązaniem jest stworzenie niestandardowego cmdclass pochodzący z distutils.cmd.Commmand która buduje moduł jak chcesz:

import distutils.cmd 

class build_py_cmd(distutils.cmd.Command): 
    def initialize_options(self): 
     pass 

    def finalize_options(self): 
     pass 
    def run(self): 
     print("Calling SCons to build the module") 
     pbs.scons() 


setup(name = 'riak3k', 
     packages = ['riak3k'], 
     package_dir = {'': 'build/release'}, 
     cmdclass = {'build_py': build_py_cmd}, 
+0

Co to jest import 'pbs'? –

+0

Wygląda na to, że moduł 'pbs' jest z https://pypi.python.org/pypi/pbs, do wywoływania funkcji systemu operacyjnego jako funkcji Pythona. O ile mogę powiedzieć, byłby to odpowiednik 'subprocess.check_call (['scons'])'. –

Powiązane problemy