Som PostgreSQL-utvecklare behöver jag ibland få min kod att fungera på Windows. Eftersom jag normalt inte använder Windows och inte har en permanent Windows-installation runt, har detta alltid varit lite krångligt. Jag har utvecklat några tekniker för att göra detta enklare, och jag tycker att de är värda att dela med sig av. Och faktiskt blev den här texten ganska lång, så det här blir en serie med flera blogginlägg.
Det första som är användbart att förstå är de olika varianterna av Windows-byggmål och hur de är lika och olika.
Det, utan tvekan, primära sättet att bygga för Windows är att använda Microsofts Visual Studio kompilator svit. Det här är vad du kan tänka dig som den mest inhemska miljön. De flesta om inte alla binära distributioner av PostgreSQL för Windows använder denna build. Denna build använder inte de normala Unix-makefilerna utan ett separat byggsystem under src/tools/msvc/
. Detta analyserar make-filerna och har lite anpassad logik och bygger "projekt"-filer specifika för denna verktygskedja, som du sedan kan köra för att bygga koden. Låt oss kalla detta MSVC-bygget här. Den här builden är benägen att gå sönder på två sätt:ett, om koden faktiskt inte bygger eller körs på Windows, och två, om du ändrar något i det normala (makefiles-baserade) byggsystemet som gör att dessa ad-hoc-skript ha sönder. Så det här är alltid väldigt roligt att ta itu med.
Det andra sättet är att använda MinGW . Detta använder GNU-verktygskedjan (GCC, GNU binutils, GNU ld, etc.) för att bygga inbyggd kod på Windows. Du kan tänka på det som "GCC på Windows", men i verkligheten innehåller det ytterligare shim och lim för gränssnitt med Windows-systembiblioteken. Detta använder det normala Unix-ish-byggsystemet med hjälp av configure och make-filer, men koden den producerar är inbyggd Windows-kod, i princip motsvarande utdata från MSVC-bygget. Detta betyder också att om koden inte bygger eller körs med MSVC-bygget kommer den inte heller att byggas eller köras här. Men byggsystemet är detsamma som för Linux etc., så det blir svårare att bryta av misstag.
Det tredje sättet är Cygwin . Cygwin är ett delsystem som presenterar en POSIX-liknande miljö på Windows. Till exempel lägger Cygwin till användare, grupper, fork()
, SysV delat minne och andra faciliteter som inte finns på inbyggt Windows men som är standard på till exempel Linux. Tanken är att du kan ta källkod avsedd för Linux eller BSD och bygga den under Cygwin utan förändring (eller åtminstone med endast ändringar som skulle ligga inom det typiska intervallet för porteringsändringar mellan Unix-liknande system). Av denna anledning existerade en Cygwin-port av PostgreSQL långt före den ursprungliga Windows-porten, eftersom det var en mycket mindre ansträngning. I praktiken går abstraktionen sönder i vissa områden, särskilt i nätverkskoden och kring filnamn och åtkomst, men i allmänhet går Cygwin-bygget av väldigt sällan jämfört med andra mål.
Det brukade finnas ett annat sätt att bygga på Windows. Det fanns win32.mak
filer som du kunde använda direkt med nmake på Windows, och det fanns även stöd för Borland-kompilatorer någon gång. Dessa var i princip stoppåtgärder för att bygga bara libpq naturligt på Windows innan den fullständiga inbyggda porten hade anlänt. Dessa har nu tagits bort.
Det finns en annan term som förekommer i detta sammanhang:MSYS . Anledningen till detta är att MinGW i sig inte ofta är användbart. Det är bara en kompilatorverktygskedja. Men för att bygga typisk Unix-mjukvara behöver du ytterligare verktyg som bash, make, sed, grep och alla de saker som vanligtvis används från ett konfigureringsskript eller en makefil. Dessa verktyg finns inte alla som inbyggda Windows-portar. Men du kan köra dem ovanpå Cygwin-delsystemet. Så ett sätt att använda MinGW är inifrån Cygwin. En annan är MSYS, som står för "minimal system", vilket grovt sett är en skräddarsydd delmängd av Cygwin och en del paketering specifikt för att använda MinGW för att bygga mjukvara. Den ursprungliga MSYS är nu övergiven så vitt jag vet, men det finns ett populärt nytt alternativ MSYS2. Mer om detta i ett senare blogginlägg. För nu är det bara att förstå förhållandet mellan alla dessa olika mjukvarupaket.
Låt oss nu överväga hur källkoden ser dessa olika byggmiljöer.
En inbyggd version som använder MSVC eller MinGW definierar _WIN32
. (Konstigt nog är detta fallet för både 32-bitars och 64-bitars builds. En 64-bitars build definierar också _WIN64
, men detta används sällan.) PostgreSQL-källkoden använder WIN32
istället, men det är specifikt för PostgreSQL, inte gjort av kompilatorn.
MSVC definierar också _MSC_VER
till något versionsnummer. Detta är ibland användbart för att komma runt problem med en viss kompilatorversion (ofta den typ av saker som Unix bygger brukar använda konfigurera för). Observera att MinGW inte definierar _MSC_VER
, så koden måste skrivas noggrant för att hantera det också. Det har förekommit några mindre buggar kring detta eftersom kod som #if _MSC_VER < NNNN
att kanske lösa ett problem med en äldre kompilator skulle utlösas på MinGW också, vilket kanske inte var avsett. (Mer korrekt skulle vara #if defined(_MSC_VER) && _MSC_VER < NNNN
och naturligtvis lindas in i #ifdef WIN32
.) MinGW definierar __MINGW32__
och __MINGW64__
, men dessa används mycket sällan. MinGW definierar naturligtvis också __GNUC__
eftersom det är GCC, så kan villkorlig kod specifik för GCC eller en GCC-version också användas. I allmänhet, eftersom MinGW använder Autoconf, bör dessa saker vanligtvis kontrolleras i configure
istället för i koden.
Cygwin definierar __CYGWIN__
. Noterbart är att Cygwin inte definierar _WIN32
, eller WIN32
, och så vidare — eftersom det inte anser sig vara inbyggt Windows. Det är därför du i vissa kodområden där Windows tittar igenom Cygwin-abstraktionen ser mycket kod med #if defined(WIN32) ||
att hantera båda fallen.
defined(__CYGWIN__)
(Det finns några dammiga hörn i koden som inte alltid hanterar alla dessa förprocessordefinieringar på ett vettigt och konsekvent sätt. I vissa fall är detta avsiktligt eftersom verkligheten är konstig, i andra fall är det rutten och felaktig kod som måste städat.)
Var och en av dessa mål existerar i princip som en 32-bitars och en 64-bitars variant. En 64-bitars Windows-operativsysteminstallation, som är den normala moderna installationen, kan köra både 32-bitars och 64-bitars programvara, så du kan installera och köra båda varianterna på ett sådant system. En produktionsinstallation bör förmodligen använda en 64-bitars build, så du kanske väljer att inte bry dig om 32-bitarsmiljön. Faktum är att Cygwins 32-bitarsvariant verkar vara ganska död, och du kanske inte kan få den att fungera alls. Ett problem är dock att 64-bitars MinGW har några oklara buggar, så när man använder MinGW speciellt är det ibland bättre att använda 32-bitarsmiljön, såvida du inte vill bekämpa operativsystem eller verktygskedja. Men 32-bitars datoranvändning är uppenbarligen mest på väg ut i allmänhet, så detta är inte ett framtidssäkert alternativ.
Nu är frågan kanske vilken av dessa miljöer som är "bäst". När det gäller utveckling spelar det ingen roll eftersom all kod måste fungera på dem alla. Som jag nämnde ovan används MSVC-bygget för de flesta produktionsinstallationer av Windows. MinGW (eller snarare MSYS2)-miljön är trevligare att utvecklas i om man är van vid en Unix-liknande miljö, men speciellt 64-bitars MinGW-miljön verkar vara något buggig, så det är svårt att rekommendera denna för produktion. Cygwin kan av vissa anses vara en historisk kuriosa vid denna tidpunkt. Att köra en produktionsserver under Cygwin rekommenderas inte eftersom prestandan är ganska dålig. Men Cygwin är faktiskt användbar i vissa situationer. Till exempel fungerar inte Readline på någon av de inbyggda Windows-byggen, men det gör det på Cygwin, så om du är en psql-användare är det bättre att använda en Cygwin-build för det. Cygwin är också användbar i den situation som är det omvända till det här blogginlägget:Du är en Windows-utvecklare och vill säkerställa att din kod till största delen är kompatibel med Unix. Så alla dessa tre miljöer har sitt värde och är värda att underhållas vid denna tidpunkt.
I nästa del av den här serien kommer jag att diskutera några tekniker för att testa kodändringar på och för Windows om det inte är din primära utvecklingsmiljö.