Automatické konfigurování prostředí před kompilací
19.8.2010 00:00 | Radim Kolář | přečteno 6549×
Konfigurovat před kompilací můžeme dvěma způsoby. První je použít námi předdefinované volby podle platformy na které překládáme. Nejjednoduší způsob jak detekovat platformu na které překládáme je zavolat python funkci os.uname() a podle výsledku pak nastavit do proměnných prostředí příslušné přepínače. Pokud tato funkce neexistuje tak překládáme pod windows.
Například na FreeBSD musíme přidat /usr/local/include do dráhy C preprocesoru, protože ho GCC zahrnuté v systému nemá v seznamu adresářů ve kterých bude hledat include soubory. Pokud chceme rozlišit i verzi systému tak tu funkce uname vrací ve třetí položce jak uvidíte v následujícím příkladu:
import os env = Environment() if os.uname()[0] == 'FreeBSD': env.Append(CPPFLAGS = "-I/usr/local/include") print "Compiling on FreeBSD",os.uname()[2] fbsd8:/tmp> scons -Q Compiling on FreeBSD 8.1-RELEASE scons: `.' is up to date.
Tento způsob nastavování kompilačních voleb používají zejména komerční nebo Windows produkty, protože tam dopředu znáte platformu na které překládáte a není potřeba detekovat zda na ní existují jednotlivé hlavičkové soubory. Naproti tomu u open source produktů které budou překládány uživatelem se vyskytuje mnohem větší rozdílnost systémů na kterých kompilujeme a je proto potřeba dělat autodetekci jako dělá GNU Autoconf.
Mnohem zajímavější bude počtení o autodetekci. Funkčnost je podobná programu GNU autoconf, ale ovládá se mnohem snadněji. Není potřeba programovat v m4 makrojazyku nebo v shellu. Namísto toho máme k dispozici SCons funkce volané z Pythonu. GNU autoconf má více připravených testů v SCons najdete jen takové ty základní, ale velmi snadno se rozšiřuje o vlastní testy.
Před zahájením autokonfigurace musíme vytvořit kontext pro prostředí které chceme konfigurovat. Důležité je nazapomenout po skončení konfigurace změnit prostředí podle parametrů, které jsme si v průběhu autokonfigurace nastavili. Dělá se to zavoláním funkce Finish v konfiguračním kontextu .
Nejčastější test který budeme používat bude ověření existence hlavičkového souboru. SCons nabízí funkce CheckCHeader a CheckCXXHeader pro ověření existence hlavičkového souboru pro jazyky C, případně C++. Pokud hlavičkový soubor najdeme tak si nadefinujeme symbol pro C preprocesor standardními prostředky pro práci s prostředími.
import os env = Environment() conf = Configure(env) if not conf.CheckCHeader('stdio.h'): print "Headers not found, check your installation" Exit(1) if conf.CheckCHeader('unistd.h'): conf.env.Append(CPPFLAGS = '-DHAVE_UNISTD_H') env = conf.Finish() fbsd8:/tmp> scons scons: Reading SConscript files ... Checking for C header file stdio.h... yes Checking for C header file unistd.h... yes scons: done reading SConscript files. scons: Building targets ... scons: `.' is up to date.
SCons umí narozdíl od GNU Autoconf automaticky cachovat výsledky testů pokud se nezměnily vstupní podmínky. Není k tomu zapotřebí žádného kódu nebo nastavování navíc. Pokud spustíme SCons podruhé, tak se testy neprovedou ale použije se nacachovaná hodnota. U testu existence hlavičkových souborů moc velké zrychlení použitím cache nebude, ale u sofistikovanějších testů které překládají, linkují a spouští testovací program to již bude znát. Testy se cachují do podadresáře .sconf_temp v základním adresáři sestavovaného projektu.
fbsd8:/tmp> scons scons: Reading SConscript files ... Checking for C header file stdio.h... (cached) yes Checking for C header file unistd.h... (cached) yes scons: done reading SConscript files. scons: Building targets ... scons: `.' is up to date. scons: done building targets.
Pokud chceme ignorovat cache a poctivě spustit všechny testy použijeme volbu --config=force
fbsd8:/tmp> scons --config=force scons: Reading SConscript files ... Checking for C header file stdio.h... yes Checking for C header file unistd.h... yes scons: done reading SConscript files. scons: Building targets ... scons: `.' is up to date. scons: done building targets. fbsd8:/tmp>
SCons nabízí i řadu dalších testů. Najdete je uvedené v manuálové stránce nebo v popisu API, v online HTML dokumentaci nejsou. Probereme si ty nejdůležitější.
Testování existence funkce v standardní C knihovně je snadné. Stačí zavolat CheckFunc('jméno funkce').
if conf.CheckFunc('fork'): conf.env.Append(CPPFLAGS = '-DHAVE_FORK')
tento formát funguje i na funkce s argumenty:
if conf.CheckFunc('malloc'): conf.env.Append(CPPFLAGS = '-DHAVE_MALLOC')
problém nastává pokud začneme používat volitelný argument obsahující hlavičkové soubory v kterých má být funkce obsažena. Když použijeme:
if conf.CheckFunc('malloc','#include <stdlib.h>'): conf.env.Append(CPPFLAGS = '-DHAVE_MALLOC')
tak bude GCC protestovat a test selže, protože se v testovacím programu pokoušíme volat funkci bez argumentů a funkce malloc vyžaduje argument. Smysl použití uvedení hlaviček spolu se jménem funkce mi tedy poněkud uniká. Naštěstí máme i jiné funkce které pracují i s hlavičkami správně. Nejlepší je proto testovat hlavičkové soubory na obsažené funkce pomocí CheckDeclaration.
if conf.CheckDeclaration('malloc','#include <stdlib.h>'): conf.env.Append(CPPFLAGS = '-DHAVE_MALLOC')
Pokud testujeme existenci symbolu v knihovně, tak jsme pro tento případ dobře vybaveni. Funkce CheckLib nejenže otestuje knihovnu na existanci zadaného symbolu, ale v případě úspěšného testu, pokud není uvedeno jinak, přidá knihovnu do seznamu v proměnné LIBS. Není proto potřeba ručně volat conf.env.Append což zjednodušuje kód.
conf.CheckLib('ssl','SSL_connect')
Ke knihovnám patří hlavičkové soubory. SCons umí kontrolovat existenci knihovny i hlavičkového souboru na jeden zátah. Slouží k tomu CheckLibWithHeader. Pokud chcete mít opravdu robustní auto test tak nejprve otestujte existenci knihovny a hlavičkového souboru a poté existenci symbolu v knihovně. Stejně jako v předchozím případě bude v případě úspěšného testu knihovna přidána do seznamu LIBS.
conf.CheckLibWithHeader('ssl','openssl/ssl.h','C')
Poslední dnešní kategorii testů jsou testy typů. Můžeme testovat jak existenci typu pomocí CheckType, ale i jeho velikost pomocí CheckTypeSize. Můžeme si kupříkladu zjistit zda typ int je 4 nebo 8 bajtů.
if conf.CheckType('off_t','#include <sys/types.h>','C'): conf.env.Append(CPPFLAGS = '-DHAVE_OFFT') offt = conf.CheckTypeSize('off_t','#include <sys/types.h>','C') print "off_t size is",offt
U velikosti typů můžeme ještě pri testování uvést očekávanou velikost. Tímto otestujeme zda typ int má právě čtyři bajty.
conf.CheckTypeSize('int',None,'C',4)
V dalším díle se podíváme jak psát vlastní testy.