Hur man använder Linuxs ar-kommando för att skapa statiska bibliotek

By rik

När du utvecklar mjukvara kan du dra nytta av Linux `ar`-kommandot för att skapa funktionsbibliotek. Den här guiden visar dig hur du genererar ett statiskt bibliotek, gör ändringar i det och sedan integrerar det i ett program. Allt detta illustreras med tydlig exempelkod.

`ar`-kommandot är en sann veteran, med rötter ända tillbaka till 1971. Själva namnet, `ar`, härstammar från dess ursprungliga syfte: att skapa arkivfiler. En arkivfil fungerar som en samling av andra filer, och kan rymma ett stort antal av dem. Du kan lägga till, ta bort eller extrahera filer från ett sådant arkiv. Idag är det inte längre `ar` som används för den här typen av uppgifter, utan verktyg som `tar` har tagit över.

Trots detta har `ar` fortfarande viktiga specialområden. Det används framförallt för att skapa statiska bibliotek, en viktig del av mjukvaruutveckling. Dessutom används `ar` för att skapa paketfiler, till exempel ”.deb”-filer som används i Debian Linux och derivat som Ubuntu.

Vi ska nu gå igenom stegen för att bygga och ändra ett statiskt bibliotek och demonstrera hur det används i ett program. För att göra det behöver vi en specifik uppgift för biblioteket att uppfylla. Vårt bibliotek ska kunna koda och avkoda textsträngar.

Viktigt att notera är att detta är ett snabbt och enkelt exempel i demonstrationssyfte. Använd inte den här krypteringsmetoden för något viktigt. Det är en mycket enkel substitutionschiffer där A blir B, B blir C, och så vidare.

Funktionerna cipher_encode() och cipher_decode()

Vi kommer att arbeta i en katalog med namnet ”bibliotek” och skapar senare en underkatalog med namnet ”test”.

I ”bibliotek”-katalogen har vi två filer. Textfilen `cipher_encode.c` innehåller funktionen `cipher_encode()`:

void cipher_encode(char *text)
{
 for (int i=0; text[i] != 0x0; i++) {
   text[i]++;
 }
} // end of cipher_encode

Motsvarande funktion, `cipher_decode()`, finns i textfilen `cipher_decode.c`:

void cipher_decode(char *text)
{
 for (int i=0; text[i] != 0x0; i++) {
   text[i]--;
 }
} // end of cipher_decode

Filer med programmeringsinstruktioner kallas källkodsfiler. Vi ska skapa en biblioteksfil som heter `libcipher.a`. Denna kommer att innehålla de kompilerade versionerna av våra två källkodsfiler. Vi skapar också en kort textfil `libcipher.h`. Denna fil är en header-fil som innehåller deklarationerna för de två funktionerna i vårt nya bibliotek.

Med biblioteket och header-filen kan andra programmerare använda de två funktionerna i sina egna program. De behöver inte skriva om funktionerna; de kan helt enkelt använda kopiorna från vårt bibliotek.

Kompilering av cipher_encode.c och cipher_decode.c

Vi använder `gcc`, GNU-standardkompilatorn, för att kompilera källkodsfilerna. Alternativet `-c` (kompilera, ingen länk) instruerar `gcc` att enbart kompilera filerna och stanna där. Det skapar en mellanliggande fil, en objektfil, för varje källkodsfil. Normalt länkar `gcc`-länkaren alla objektfiler för att skapa en körbar fil. Med `-c` hoppar vi över den länkningsprocessen; vi behöver bara objektfilerna.

Vi kontrollerar först att vi har de förväntade filerna.

ls -l

Källkodsfilerna ska synas i katalogen. Vi använder nu `gcc` för att kompilera dem till objektfiler.

gcc -c cipher_encode.c
gcc -c cipher_decode.c

Om allt går bra ska det inte synas någon utdata från `gcc`.

Detta skapar två objektfiler med samma namn som källkodsfilerna, fast med suffixet ”.o”. Dessa är filerna vi ska lägga till i biblioteksfilen.

ls -l

Skapa biblioteket libcipher.a

För att skapa biblioteksfilen – som i själva verket är en arkivfil – använder vi `ar`.

Vi använder `-c` (skapa) för att skapa biblioteksfilen, `-r` (lägg till med ersätt) för att lägga till filerna i biblioteksfilen och `-s` (index) för att skapa ett index över filerna inuti biblioteksfilen.

Vi namnger biblioteksfilen `libcipher.a`. Det namnet anger vi på kommandoraden, tillsammans med namnen på de objektfiler vi ska lägga till i biblioteket.

ar -crs libcipher.a cipher_encode.o cipher_decode.o

Om vi listar filerna i katalogen ser vi att `libcipher.a` nu finns med.

ls -l

Med `-t` (tabell) flaggan kan vi se vilka moduler som finns inuti biblioteksfilen.

ar -t libcipher.a

Skapa header-filen libcipher.h

Filen `libcipher.h` kommer att inkluderas i alla program som använder biblioteket `libcipher.a`. Filen `libcipher.h` måste definiera de funktioner som biblioteket innehåller.

Vi skapar header-filen genom att ange funktionsdeklarationerna i en textredigerare, till exempel `gedit`. Vi namnger filen ”libcipher.h” och sparar den i samma katalog som `libcipher.a`.

void cipher_encode(char *text);
void cipher_decode(char *text);

Använda libcipher-biblioteket

Det bästa sättet att testa vårt nya bibliotek är att skriva ett enkelt program som använder det. Först skapar vi en katalog som heter `test`.

mkdir test

Vi kopierar biblioteket och header-filerna till den nya katalogen.

cp libcipher.* ./test

Vi byter till den nya katalogen.

cd test

Nu kontrollerar vi att våra två filer finns här.

ls -l

Vi måste skapa ett litet program som använder biblioteket och som bevisar att det fungerar som förväntat. Skriv in följande rader i en textredigerare. Spara texten som `test.c` i testkatalogen.

#include <stdio.h>
#include <stdlib.h>

#include "libcipher.h"

int main(int argc, char *argv[])
{
 char text[]="How-To Geek loves Linux";

 puts(text);

 cipher_encode(text);
 puts(text);

 cipher_decode(text);
 puts(text);

 exit (0);

} // end of main

Programmets flöde är enkelt:

Det inkluderar `libcipher.h` för att ge tillgång till biblioteksfunktionernas deklarationer.

En sträng med namnet ”text” skapas och fylls med texten ”How-To Geek loves Linux”.

Den texten skrivs ut på skärmen.

Funktionen `cipher_encode()` anropas för att koda strängen, och den kodade strängen skrivs sedan ut på skärmen.

Slutligen anropas `cipher_decode()` för att avkoda strängen, och den avkodade texten skrivs ut på skärmen.

Vi genererar testprogrammet genom att kompilera `test.c` och länka in biblioteket. Alternativet `-o` (utgång) berättar för `gcc` vad den genererade, körbara filen ska heta.

gcc test.c libcipher.a -o test

Om `gcc` återgår till kommandotolken utan utdata har allt gått bra. Nu testar vi vårt program. Sanningens ögonblick:

./test

Och vi ser det förväntade resultatet. Testprogrammet skriver ut den vanliga texten, sedan den krypterade texten, och till sist den dekrypterade. Det använder funktionerna i vårt nya bibliotek. Biblioteket fungerar!

Framgång! Men varför stanna där?

Lägga till ytterligare en modul i biblioteket

Vi utökar nu biblioteket med ytterligare en funktion. Vi lägger till en funktion som ger information om vilken version av biblioteket som används. Vi måste skapa den nya funktionen, kompilera den, och lägga till objektfilen i det befintliga biblioteket.

Skriv in följande rader i en textredigerare och spara innehållet i en fil som heter `cipher_version.c` i bibliotekskatalogen.

#include <stdio.h>

void cipher_version(void)
{
 puts("How-To Geek :: VERY INSECURE Cipher Library");
 puts("Version 0.0.1 Alphan");
} // end of cipher_version

Vi måste även lägga till definitionen för den nya funktionen i header-filen `libcipher.h`. Lägg till en ny rad längst ner i filen, så att den ser ut så här:

void cipher_encode(char *text);
void cipher_decode(char *text);
void cipher_version(void);

Spara den modifierade filen `libcipher.h`.

Vi måste kompilera `cipher_version.c` för att få en objektfil `cipher_version.o`.

gcc -c cipher_version.c

Detta skapar filen `cipher_version.o`. Denna lägger vi till i biblioteket `libcipher.a` med följande kommando. Alternativet `-v` (verbose) gör att `ar` ger oss information om vad som händer, i normala fall är det tyst.

ar -rsv libcipher.a cipher_version.o

Den nya objektfilen läggs till i biblioteket. `ar` ger bekräftelse. ”a” står för ”tillagd”.

Vi kan använda `-t` (tabell) flaggan för att se vilka moduler som finns i biblioteksfilen.

ar -t libcipher.a

Nu innehåller biblioteksfilen tre moduler. Vi testar den nya funktionen.

Använda funktionen cipher_version().

Vi tar bort den gamla biblioteksfilen och header-filen från testkatalogen, kopierar in de nya filerna, och byter sedan till testkatalogen igen.

Vi raderar de gamla versionerna av filerna.

rm ./test/libcipher.*

Vi kopierar in de nya versionerna till testkatalogen.

cp libcipher.* ./test

Vi byter till testkatalogen.

cd test

Vi ändrar programmet `test.c` så att det använder den nya biblioteksfunktionen.

Vi lägger till en ny rad i `test.c` som anropar `cipher_version()`. Den placerar vi före den första `puts(text);` raden.

#include <stdio.h>
#include <stdlib.h>

#include "libcipher.h"

int main(int argc, char *argv[])
{
 char text[]="How-To Geek loves Linux";

 // new line added here
 cipher_version();

 puts(text);

 cipher_encode(text);
 puts(text);

 cipher_decode(text);
 puts(text);

 exit (0);

} // end of main

Spara detta som `test.c`. Vi kompilerar och testar nu att den nya funktionen fungerar.

gcc test.c libcipher.a -o test

Vi kör nu den nya versionen av test:

Den nya funktionen fungerar. Vi kan se bibliotekets version i början av `test`programmets utdata.

Men det finns ett potentiellt problem.

Byta ut en modul i biblioteket

Detta är inte den första versionen av biblioteket; det är den andra. Versionsnumret är inte korrekt. Den första versionen hade ingen funktion `cipher_version()`. Den här har det. Därför borde detta vara version ”0.0.2”. Vi måste ersätta `cipher_version()` funktionen i biblioteket med en korrigerad.

Tack och lov gör `ar` detta väldigt enkelt.

Först redigerar vi `cipher_version.c` i bibliotekskatalogen. Vi ändrar ”Version 0.0.1 Alpha” texten till ”Version 0.0.2 Alpha”. Den ska se ut så här:

#include <stdio.h>

void cipher_version(void)
{
 puts("How-To Geek :: VERY INSECURE Cipher Library");
 puts("Version 0.0.2 Alphan");
} // end of cipher_version

Spara den här filen. Vi kompilerar den igen för att skapa en ny objektfil `cipher_version.o`.

gcc -c cipher_version.c

Nu ersätter vi den befintliga `cipher_version.o`-objektfilen i biblioteket med vår nykompilerade version.

Vi har tidigare använt `-r` (lägg till med ersätt) för att lägga till moduler i biblioteket. När vi använder den på en modul som redan finns i biblioteket, kommer `ar` att ersätta den gamla versionen med den nya. Alternativet `-s` (index) uppdaterar bibliotekets index, och `-v` (verbose) gör att `ar` ger ut information.

ar -rsv libcipher.a cipher_version.o

Den här gången rapporterar `ar` att den har ersatt modulen `cipher_version.o`. ”r” betyder ”ersatt”.

Använda den uppdaterade funktionen cipher_version()

Vi använder vårt modifierade bibliotek och ser att det fungerar.

Vi kopierar biblioteksfilerna till testkatalogen.

cp libcipher.* ./test

Vi byter till testkatalogen.

cd ./test

Vi måste kompilera testprogrammet igen med vårt nya bibliotek.

gcc test.c libcipher.a -o test

Nu kör vi vårt program.

./test

Utdata från testprogrammet är som förväntat. Rätt versionsnummer visas i versionssträngen, och krypterings- och avkrypteringsrutinerna fungerar också.

Ta bort moduler från ett bibliotek

Efter allt detta kan vi ta bort `cipher_version.o` filen från biblioteket. Det känns nästan synd.

Det gör vi med alternativet `-d` (radera). Vi använder även `-v` (verbose), så att `ar` berättar vad den har gjort. Vi lägger även till `-s` (index) för att uppdatera indexet i biblioteksfilen.

ar -dsv libcipher.a cipher_version.o

`ar` rapporterar att modulen har tagits bort. ”d” står för ”raderad”.

Om vi ber `ar` att lista modulerna i biblioteksfilen ser vi att vi är tillbaka till två moduler.

ar -t libcipher.a

Om du ska ta bort moduler från ditt bibliotek, kom ihåg att ta bort motsvarande deklarationer från bibliotekets header-fil.

Dela din kod

Bibliotek möjliggör delning av kod på ett praktiskt och privat sätt. Alla du delar biblioteksfilen och header-filen med kan använda ditt bibliotek, medan den faktiska källkoden förblir privat.