Hur bygger du ditt system? 27/09/2015

Har tidigare pratat om vikten av gränssnitt och möjligheten att använda 3:de parts delar i sin utveckling. Speciellt möjligheten att fortfarande vara i kontroll över sin kodbas och ha möjlighet att byta ut delar. Varför fokus på detta? Vad gör att det inte 'bara fungerar'?

Embeddedutveckling på microcontrollers (MCU:s) har, med rätta, styrts av behovet att hålla allting snålt. Lite flashåtgång, speciellt lite RAMåtgång. Och detta försvinner inte över en natt. Trycket har minskat och delvis flyttats över till utvecklingstid, men det innebär inte att det är fritt fram att lasta in allt och lite till. Det behövs fortfarande kontroll över åtgången. En effekt av detta, tillsammans med open source kulturen på en del håll, och att MCU tillverkarna stått för mycket hjälpmjukvara, är att mycket av den hjälpmjukvara som används distribueras i form av källkodspaket. Man får en zip fil, packar upp denna, lyfter in koden i sitt projekt och den blir en del av ditt byggsystem. Har fungerat relativt väl och man kommer snabbt fram till något körbart. Exempel på denna distribution är t.ex. FreeRTOS, LwIP, firmware libraries från t.ex.ST.

En annan egenhet av MCU utveckling är att målplattform ofta är rätt unik. Man har valt en krets utifrån behov och det gränssnitt man har att arbeta mot är här den registeruppsättning MCU:n har. Ofta är sammansättning av drivers, och framför allt länkskriptet, faktorer som driver utvecklingen mot att ge en unik mjukvara för varje hårdvaruenhet.

De toolchains som ofta används (IAR, Atollic, etc) lägger väldigt mycket fokus på att det ska vara lätt att komma igång. 'Hello World' med en blinkande led får max ta 10min att få upp på ett eval kort. Mindre fokus ligger på möjlighet till bygge på byggservers, stöd för unit testning eller kompilering mot flera målplattformar eller flera binärer. Detta tillsammans ger en kultur och minsta motståndets väg mot att för varje projekt betrakta kodbasen som unik för hårdvaran. Man utgår från något liknande tidigare, klipper/klistrar ihop en start och bäddar sedan in applikationslogik direkt på target. Testning sker genom provkörningar på evalueringskort eller direkt på målhårdvara.

Det här är inte en hållbar väg framåt.

För små kodbaser visst, där har inte tiderna förändrats. Men det som händer är att mer och mer ska in i hårdvaran och då måste mer fokus läggas på gränssnittsskapandet. Dels för att återanvända då ny hårdvara kommer, dels för att minska underhåll. (Hur många källkodsträd behöver vi fixa den där SSL buggen i?)

Så hur börjar man resan mot mer kontroll över sin kod? Joel Spolsky skrev för 15 år sedan en enkel lista med tumregler som har blivit ett av de mest lättillgängliga sätten att få grundfundament på plats i sin utvecklingsprocess. Har man inte dessa finns ingen anledning till finlir på andra områden. Jämför med tandborstning för personlig hygien eller bilbesiktning. Det ska bara fungera om man vill ha möjlighet att utvecklas över tiden, eller ha någon som helst kvalitetskontroll. Artikeln med listan är väl värd att läsa: The Joel Test: 12 Steps to Better Code. Vid det här laget brukar punkt 1 (Use source control software) finnas på plats. Men punkt 2 (Can you build in one step?), mera tveksamt. Får kanske ut en binär från bygget, men paketering i zip fil med release-dokumentation? Punkt 3 (Do you use a build server?) Brukar behövas betydligt mer dedikerade ledare för mjukvaruteam för att ta detta hela vägen inom MCU utveckling.

Det finns orsaker till detta. Mycket faller tillbaka på byggsystemen som dagens IDE erbjuder. Ligger man kvar där, brukar det vara en hel del jobb att få igång en byggserver. Så enligt min åsikt, så är själva byggsystemet en av nycklarna för att kunna bryta vanan att bygga mjukvara direkt mot målplattformen. Ska man uppnå att en buggfix i ett hjälpbibliotek, bara behöver rättas på ett ställe, så måste den förändringen propageras automatiskt till alla relevanta binärer och då håller det inte att klippa/klistra kod mellan olika källkodsträd. Koden ska ligga i sin egen modul som används av dem som behöver den.

Så vad är alternativen? Tyvärr få, ålderdomliga, och inte speciellt bra. Under alla år jag arbetat har jag alltid landat i att 'make' får göra jobbet. En enda anledning. Programmet får jobbet gjort.

Jag svär under uppsättningen, resultatet har ofta övrigt att önska. (lägga tid på header dependency tracking den här gången? är det värt det?) Speciellt då MCU utveckling ofta sker i Windowsmiljö så kommer den extra lilla egenheten av att köra allt under Cygwin eller MinGW. Var skulle det vara \ och var / för att skilja sökvägar? Det löser sig, men det tar tid, är frustrerande och felbenäget arbete.

Övriga alternativ är oftast anpassade för desktop OS. Där finns det flera. cmake, qmake, scons, waf, etc. Men där är fokus på hantering av versionshanterade bibliotek i systemet. Sällan ser jag stöd för hantering av länkfiler, modifiering av c-startup och dessa andra frihetsgrader som brukar behövas varefter systemet växer.

Så vad vill jag ha ut av ett byggsystem för embedded? En liten önskelista:

  • Command line tool. Kan dels startas från eclipse, dels byggservrar. Mer flexibelt.
  • Out of tree build. Dvs alla genererade filer ligger _inte_ i källkodsträdet. Ska räcka  att radera en katalog för att få totalt ombygge. Underlättar för bygge mot flera targets också.
  • Textbaserade regler. Ska gå att spåra regler och de kommandon som invokeras så byggprocessen  blir transparent.
  • Full dependency tracking. Dels under kompilering av .c/.h filer. Men även av preprocessning där källkodsfiler och länkfiler samt post processing.
  • Används wildcards (t.ex. *.c för alla .c filer i katalog) ska systemet upptäcka när filer läggs till/tas bort.
  • Stöd för att direkt från .zip filer packa upp dessa, patcha och sätta upp sökvägar så  att vid ny version räcker det med att uppdatera .zip paket och en versionssiffra. Byggsystemet  ska kunna hantera resten. Detta inkluderar att beskrivningen av bygget ska ligga utanför det uppackade biblioteket. Beskrivningen av bygget är del av din mjukvara, inte 3:de partsleverantören om de levererar i form av ren källkod.
  • Stöd för bygge mot flera targets. Vill dels kunna bygga för målhårdvara, dels för evalueringskort, dels för desktopmiljöer för unit testning. Förutsätter att koden klarar detta. Frågan är om ditt byggsystem gör det?
  • Bygge av delar av systemet. Under utveckling skriver man ofta i enskilda moduler samt har en unit test driver för första checker. Ska gå att bygga detta utan att resten drar igång.
  • Det ska vara snabbt. En av de mest frustrerande sakerna att ägna tid åt är att se kompileringskommandon sakta rulla förbi, för att inte tala om försteg för dependency trackning. Koppla detta till att köra under cygwin på windows och det är många timmar som  flyter iväg. Det dödar 'flow' som är så viktigt för produktiv utveckling.
  • Ska klara att bygga flera olika binärer för samma target. Hur ofta sitter man inte med  en huvudapplikation, en produktionstestning och en emc binär? De delar 90% av koden som ligger i gemensamma moduler men har lite olika uppsättning.

Har inte hittat något byggsystem som kommer i närheten av min önskelista. Större projekt med en lokal 'make' guru kan komma i närheten men det ligger många timmar bakom. Sedan tenderar byggen att ta tid när projektet växer. Speciellt dependency tracking är ett problem.

Så, är det kört? Nej. Det går att få en dräglig miljö. Bara att det är en viss investering att få till det. Byggsystem behandlas rätt styvmoderligt och det kostar i längden. Det tar bort frihetsgrader från designen som är svåra att komma till rätta med längre fram. Har man behov av att utveckla mer än triviala mjukvarusystem så är byggmiljön definitivt en vattendelare mellan att sitta bakom ratten eller bara hänga med på färden.

Till sist. Finns ett system som jag tror kan ha potential som en komponent i ett större system. Ninja build system. Som författaren säger: Ska fungera som nedskalad make. Någon annan skriver reglerna och analyserar beroenden då ninja enbart bygger. Tack vare det så är den snabb. Så det enda som fattas är en MCU anpassad frontend. Tror att detta är en väg framåt.