Come la scrittura di moduli di estensione risulta un po' più complicata della scrittura di moduli in puro Python, anche la loro descrizione alle Distutils è lievemente più complicata. Diversamente dai moduli puri, non è sufficiente una semplice lista di moduli o package ed aspettarsi che le Distutils trovino autonomamente i giusti file; si dovranno specificare i nomi delle estensioni, i file sorgenti ed ogni richiesta di compilazione/link (incluse directory, librerie da linkare, etc. etc.).
Tutto questo viene fatto attraverso un altro argomento chiave di setup(), l'opzione extension. extension è semplicemente una lista di istanze Extension, ognuna delle quali descrive un singolo modulo di estensione. Supponiamo che la distribuzione includa una singola estensione, chiamata foo ed implementata da foo.c. Se non sono necessarie ulteriori istruzioni per il compilatore/linker, descrivere quest'estensione è molto semplice:
Extension('foo', ['foo.c'])
La classe Extension può essere importata da distutils.core attraverso setup(). Pertanto, lo script di setup per la distribuzione di un modulo che contenga solamente un'estensione e niente altro, potrebbe essere:
from distutils.core import setup, Extension setup(name='foo', version='1.0', ext_modules=[Extension('foo', ['foo.c'])], )
La classe Extension (attualmente, quell'importante meccanismo
di estensione/compilazione implementato dal comando
build_ext
) si comporta in maniera molto flessibile nei
riguardi della descrizione delle estensioni Python e viene spiegata
nella sezione seguente.
Il primo argomento del costruttore Extension è sempre il nome dell'estensione, incluso ogni nome di package. Per esempio:
Extension('foo', ['src/foo1.c', 'src/foo2.c'])
descrive un'estensione che si trova nel package principale, mentre:
Extension('pkg.foo', ['src/foo1.c', 'src/foo2.c'])
descrive la stessa estensione nel package pkg. Il file sorgente ed il risultante codice oggetto sono identici in entrambi i casi; l'unica differenza è dove nel filesystem (e pertanto all'interno della gerarchia dello spazio dei nomi di Python) le risultanti estensioni risiedono.
Se si hanno a disposizione un gran numero di estensioni, tutte all'interno dello stesso package (o tutte sotto lo stesso package base), si dovrà usare l'argomento chiave ext_package della funzione setup(). Per esempio:
setup(... ext_package='pkg', ext_modules=[Extension('foo', ['foo.c']), Extension('subpkg.bar', ['bar.c'])], )
compilerà foo.c nell'estensione pkg.foo, e bar.c in pkg.subpkg.bar.
Il secondo argomento del costruttore Extension è una lista di file sorgenti. Siccome le Distutils supportano correntemente solo estensioni C, C++, e Objective-C, sono presenti normalmente solo file sorgenti C/C++/Objective-C. Si deve essere sicuri di usare le estensioni appropriate per distinguere il file sorgente C++: .cc e .cpp sembrano essere riconosciuti sia dai compilatori Unix che Windows.
Comunque, potete anche includere nell'elenco file di interfaccia
SWIG (.i); il comando build_ext
sa come comportarsi
con le estensioni SWIG: esegue SWIG sul file di interfaccia e compila
il file risultante C/C++ nell'estensione scelta.
** Il supporto SWIG è ancora da rifinire e largamente non testato; specialmente il supporto SWIG per l'estensione C++! Vi saranno in questo documento spiegazioni più dettagliate quando l'interfaccia sarà completa. **
Su alcune piattaforme, si possono includere file non sorgenti che verranno elaborati dal compilatore ed inclusi nell'estensione. Attualmente, questo è valido solo per file di messaggi di testo Windows (.mc) e file di definizione di risorse per Visual C++ (.res), che saranno linkati all'interno dell'eseguibile.
Tre argomenti facoltativi di Extension saranno di aiuto se si
ha bisogno di directory include da cercare o macro per preprocessori
per definire/non-definire: include_dirs
, define_macros
e undef_macros
.
Per esempio, se l'estensione richiede file di intestazione nella
directory include, sotto la directory principale della
distribuzione, si usa l'opzione include_dirs
:
Extension('foo', ['foo.c'], include_dirs=['include'])
Qui si può specificare la directory assoluta; se siamo a conoscenza che l'estensione verrà compilata solo su sistemi Unix con X11R6 installato in /usr, si può procedere con
Extension('foo', ['foo.c'], include_dirs=['/usr/include/X11'])
Si dovrebbe evitare questa specie di uso non portabile se si stabilisce di distribuire il proprio codice: è sicuramente meglio scrivere codice C come:
#include <X11/Xlib.h>
Se si ha la necessità di includere file di intestazione di qualche
altra estensione Python, ci si può avvantaggiare dal fatto che i file
di intestazione vengono installati in modo coerente dal comando
Distutils install_header
. Per esempio, i file di
intestazione Python per la rappresentazione numerica vengono
installati (nelle installazioni standard Unix) in
/usr/local/include/python1.5/Numerical. La posizione esatta
può differire rispetto alla propria piattaforma ed al tipo di
installazione Python. Finché la directory di inclusione di
Python--in questo caso /usr/local/include/python1.5 -- viene
sempre inclusa nel percorso di ricerca, quando si compilano le
estensioni Python, il migliore approccio è scrivere codice C come:
#include <Numerical/arrayobject.h>
Se si deve inserire la directory degli include Numerical direttamente nel proprio percorso di ricerca delle intestazioni, comunque si può cercare questa directory usando il modulo Distutils distutils.sysconfig:
from distutils.sysconfig import get_python_inc incdir = os.path.join(get_python_inc(plat_specific=1), 'Numerical') setup(..., Extension(..., include_dirs=[incdir]), )
Si può considerare questo metodo portabile--lavorerà su ogni installazione Python, indipendentemente dalla piattaforma, è probabilmente più facile da scrivere così che direttamente in codice C.
Si possono definire o non-definire macro per il preprocessore con le
opzioni define_macros
e undef_macros
.
define_macros
prende una lista di tuple (name, value)
,
dove name
è il nome della macro da definire (una stringa) e
value
è il suo valore: può essere una stringa o None
.
(Definire una macro #define F00
con none
è
l'equivalente di un semplce #define F00
all'interno di codice
sorgente C: con la maggior parte dei compilatori, questo imposta
F00
nella stringa 1
). undef_macros
è
semplicemente una lista di macro che non necessitano di essere
definite.
Per esempio:
Extension(..., define_macros=[('NDEBUG', '1'), ('HAVE_STRFTIME', None)], undef_macros=['HAVE_FOO', 'HAVE_BAR'])
è equivalente ad avere questo codice al principio del proprio codice sorgente in C:
#define NDEBUG 1 #define HAVE_STRFTIME #undef HAVE_FOO #undef HAVE_BAR
Si possono anche specificare le librerie da linkare quando si compila
la propria estensione e le directory dove cercare queste librerie.
L'opzione libraries
è una lista di librerie da linkare insieme,
library_dirs
è una lista di directory dove cercare le librerie
al momento del linking e runtime_library_dirs
è una lista di
directory dove cercare per librerie condivise (caricate dinamicamente)
durante l'esecuzione.
Per esempio, se si ha la necessità di linkare librerie che si trovano all'interno del percorso di ricerca della libreria standard sul sistema di destinazione:
Extension(..., libraries=['gdbm', 'readline'])
Se si necessita di eseguire il link con la libreria, in posizioni non
standard, se ne deve includere la posizione in library_dirs
Extension(..., library_dirs=['/usr/X11R6/lib'], libraries=['X11', 'Xt'])
Nuovamente, questa sorta di costrutto non portabile dovrebbe essere evitato se si desidera distribuire il proprio codice.
** Si dovrebbe menzionare la libreria clib qui o da qualche altra parte! **
Ci sono alcune altre opzioni che possono essere utilizzate per gestire casi particolari.
L'opzione extra_objects è una lista di oggetti file da passare al linker. Questi file non devono avere estensione, oppure dovranno avere l'estensione predefinita utilizzata dal compilatore.
Le opzioni extra_compile_args ed extra_link_args possono essere usate per specificare opzioni da riga di comando facoltative per le rispettive righe di comando del compilatore e del linker.
export_symbols è utile solo in ambiente Windows. Può
contenere una lista di simboli (funzioni o variabili) che devono
essere esportati. Questa opzione non è necessaria quando si preparano
estensioni compilate: Distutils aggiunge automaticamente
initmodule
alla lista dei simboli esportati.
Vedete Circa questo documento... per informazioni su modifiche e suggerimenti.