Како користити Линук команду ар за креирање статичких библиотека

Приликом развоја софтвера, често се јавља потреба за креирањем библиотека функција, а у Линук окружењу за то се користи команда `ar`. Овај текст ће вас упутити како да формирате статичку библиотеку, како да је мењате и користите у вашим програмима. Уз то, даћемо и пример кода.

Команда `ar` је поприлично стара, датира још из 1971. године. Име `ar` је скраћеница од „архивер“, што указује на њену првобитну сврху: креирање архивских датотека. Архивска датотека је једна датотека која служи као контејнер за друге датотеке, понекад и за велики број њих. Датотеке се могу додавати, брисати или издвајати из архиве. Међутим, за ову функцију, људи данас ретко користе `ar`, јер су ту улогу преузели други алати попут `tar`.

Међутим, команда `ar` и даље има важну улогу у одређеним ситуацијама. Најчешће се користи за креирање статичких библиотека које су кључне у развоју софтвера. Такође, користи се и за креирање пакетских датотека, на пример `.deb` датотека које се користе у Дебиан Линук дистрибуцијама и њима сличним, као што је Убунту.

У наставку ћемо проћи кроз неопходне кораке за креирање и модификовање статичке библиотеке, и показати како се та библиотека може искористити у програму. За нашу демонстрацију, креираћемо библиотеку која има задатак да кодира и декодира текст.

Важно је напоменути да је ово једноставан пример у сврху демонстрације. Немојте користити ову енкрипцију за било какве озбиљне сврхе, јер се ради о веома једноставној супституционој шифри где свако слово замењује наредно слово у абецеди (А постаје Б, Б постаје Ц, итд.).

Функције `cipher_encode()` и `cipher_decode()`

За почетак, радићемо у директоријуму који ћемо назвати „библиотека“. Касније ћемо унутар њега направити поддиректоријум „тест“.

У овом директоријуму имаћемо две датотеке. У текстуалној датотеци `cipher_encode.c` налазиће се функција `cipher_encode()`:

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

Слично томе, одговарајућа функција `cipher_decode()` ће се налазити у текстуалној датотеци `cipher_decode.c`:

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

Датотеке које садрже програмске инструкције називају се датотеке изворног кода. Ми ћемо направити датотеку библиотеке под именом `libcipher.a` која ће садржати компајлиране верзије ове две датотеке изворног кода. Такође ћемо направити кратку текстуалну датотеку под називом `libcipher.h`, која ће представљати заглавље и садржати дефиниције две функције из наше нове библиотеке.

Свако ко поседује библиотеку и заглавље ће моћи да користи ове функције у својим програмима, без потребе да поново пише тај код, већ једноставно користећи копије из наше библиотеке.

Превођење датотека `cipher_encode.c` и `cipher_decode.c`

За компајлирање изворног кода користићемо `gcc`, стандардни ГНУ компајлер. Опција `-c` (компајлирај, без повезивања) говори `gcc`-у да преведе датотеке изворног кода и да се ту заустави. На овај начин добијамо такозване објектне датотеке. Линкер, који је део `gcc`-а, обично узима све објектне датотеке и повезује их у извршни програм. Међутим, у овом случају, прескачемо тај корак и коритимо само објектне датотеке.

Прво ћемо проверити да ли се датотеке налазе у директоријуму:

ls -l

Као што видимо, датотеке изворног кода су присутне. Сада их преводимо у објектне датотеке помоћу `gcc`:

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

Уколико је све прошло како треба, не би требало да буде никаквог излаза из `gcc`-а.

На овај начин су генерисане две објектне датотеке које имају исто име као и датотеке изворног кода, али са екстензијом `.o`. Ове датотеке ће бити додате у нашу библиотеку.

ls -l

Креирање библиотеке `libcipher.a`

За креирање библиотеке, користићемо команду `ar`. Библиотека је, у ствари, архивска датотека.

Користићемо опцију `-c` (креирај) да направимо датотеку библиотеке, опцију `-r` (додај са заменом) да додамо датотеке у библиотеку, и опцију `-s` (индекс) да направимо индекс датотека унутар библиотеке.

Датотеку библиотеке ћемо назвати `libcipher.a`. Ово име уносимо у командној линији, заједно са именима објектних датотека које ће бити додате у библиотеку:

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

Уколико излистамо датотеке у директоријуму, видећемо да сада постоји и датотека `libcipher.a`.

ls -l

Користећи опцију `-t` (табела) са командом `ar`, можемо видети модуле који се налазе унутар библиотеке:

ar -t libcipher.a

Креирање заглавља `libcipher.h`

Датотека `libcipher.h` ће бити укључена у све програме који користе библиотеку `libcipher.a`. Ова датотека мора садржати дефиниције функција које се налазе у библиотеци.

За креирање ове датотеке, морамо уписати дефиниције функција у текстуални едитор (нпр. `gedit`). Датотеку треба назвати `libcipher.h` и сачувати је у истом директоријуму као и датотека `libcipher.a`.

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

Коришћење библиотеке `libcipher`

Једини начин да тестирамо нашу нову библиотеку је да напишемо кратак програм који ће је користити. Прво ћемо направити директоријум под називом `test`.

mkdir test

Сада ћемо копирати библиотеку и заглавље у нови директоријум.

cp libcipher.* ./test

Прелазимо у новонаправљени директоријум:

cd test

Проверимо да ли се датотеке налазе овде:

ls -l

Сада треба да креирамо кратак програм који ће користити библиотеку и демонстрирати да све ради како треба. Следеће редове кода укуцајте у текстуални едитор и сачувајте у датотеку `test.c` унутар директоријума `test`:

#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

Ток програма је једноставан:

  • Укључује заглавље `libcipher.h`, како би програм могао да види дефиниције функција из библиотеке.
  • Креира стринг под именом `text` и у њега складишти текст „How-To Geek loves Linux“.
  • Штампа тај стринг на екрану.
  • Позива функцију `cipher_encode()` да кодира стринг и штампа кодирани стринг на екрану.
  • Позива функцију `cipher_decode()` да декодира стринг и штампа декодирани стринг на екрану.

Да бисмо генерисали програм за тестирање, потребно је да компајлирамо `test.c` и повежемо га са библиотеком. Опција `-o` (излаз) говори `gcc`-у како да назове извршни програм који генерише.

gcc test.c libcipher.a -o test

Уколико `gcc` тихо врати командну линију, све је у реду. Сада тестирамо наш програм:

./test

И добијамо очекивани резултат. Програм штампа обичан текст, затим шифровани, а затим дешифровани текст. За то користи функције из наше библиотеке, што значи да наша библиотека ради.

Успели смо. Али, нећемо се ту зауставити.

Додавање новог модула у библиотеку

Сада ћемо додати још једну функцију у библиотеку. Додаћемо функцију коју програмер може користити за приказ верзије библиотеке коју користи. За то је потребно да направимо нову функцију, компајлирамо је и додамо у постојећу датотеку библиотеке.

Следеће редове упишите у текстуални едитор и сачувајте у датотеку под називом `cipher_version.c`, у директоријуму библиотеке.

#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

Потребно је да додамо дефиницију ове функције у заглавље `libcipher.h`. Додајте нови ред на крај датотеке тако да изгледа овако:

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

Сачувајте измењено заглавље `libcipher.h`.

Сада компајлирамо `cipher_version.c` како бисмо добили објектну датотеку `cipher_version.o`.

gcc -c cipher_version.c

Ово ће креирати датотеку `cipher_version.o`. Нову објектну датотеку можемо додати у `libcipher.a` библиотеку помоћу следеће команде. Опција `-v` (вербосе) ће учинити да `ar` исписује шта је урадио.

ar -rsv libcipher.a cipher_version.o

Нова објектна датотека је додата у библиотеку, и ар то потврђује словом `a` што значи `додато`.

Можемо користити опцију `-t` (табела) да видимо који се модули налазе унутар библиотеке.

ar -t libcipher.a

Сада се унутар библиотеке налазе три модула. Искористимо сада нову функцију.

Коришћење функције `cipher_version()`

Уклонимо стару библиотеку и заглавље из директоријума `test`, копирајмо нове датотеке и вратимо се у директоријум `test`.

Прво бришемо старе верзије датотека:

rm ./test/libcipher.*

Затим копирамо нове верзије у директоријум `test`.

cp libcipher.* ./test

Прелазимо у директоријум `test`.

cd test

Сада можемо да модификујемо програм `test.c` како бисмо користили нову функцију из библиотеке.

Додајемо нови ред у `test.c` који позива функцију `cipher_version()`. Додаћемо га пре првог `puts(text);` реда:

#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

Сачувајте ово као `test.c`. Сада можемо да компајлирамо и тестирамо да ли нова функција ради.

gcc test.c libcipher.a -o test

Покрећемо нову верзију програма `test`:

Нова функција ради. Можемо видети верзију библиотеке на почетку излаза програма `test`.

Међутим, могуће је да постоји проблем.

Замена модула у библиотеци

Ово није прва верзија библиотеке, већ друга. Стога, број верзије коју приказујемо је нетачан. Прва верзија библиотеке није садржала функцију `cipher_version()`. Ова верзија је садржи. Значи, ово би требала бити верзија `0.0.2`. Потребно је да заменимо функцију `cipher_version()` у библиотеци исправљеном верзијом.

Срећом, команда `ar` то чини врло једноставним.

Прво, мењамо датотеку `cipher_version.c` у директоријуму библиотеке. Промените текст „Version 0.0.1 Alphan“ у „Version 0.0.2 Alphan“. Датотека би требала изгледати овако:

#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

Сачувајте ову датотеку. Потребно је поново је компајлирати како бисмо добили нову објектну датотеку `cipher_version.o`.

gcc -c cipher_version.c

Сада ћемо заменити постојећу `cipher_version.o` датотеку у библиотеци са нашом новокомпајлираном верзијом.

Претходно смо користили опцију `-r` (додај са заменом) за додавање нових модула у библиотеку. Када је користимо са модулом који већ постоји у библиотеци, `ar` ће заменити стару верзију новом. Опција `-s` (индекс) ће ажурирати индекс библиотеке, док ће опција `-v` (вербосе) учинити да `ar` исписује шта је урадио.

ar -rsv libcipher.a cipher_version.o

Сада `ar` извештава да је заменио модул `cipher_version.o`. Слово `r` значи `замењено`.

Коришћење функције `cipher_version()` након измене

Сада треба да искористимо модификовану библиотеку и проверимо да ли ради.

Копираћемо датотеке библиотеке у директоријум `test`:

cp libcipher.* ./test

Прелазимо у директоријум `test`.

cd ./test

Сада је потребно поново компајлирати наш тест програм са новом библиотеком.

gcc test.c libcipher.a -o test

И на крају, можемо тестирати наш програм:

./test

Излаз програма је очекиван. Тачан број верзије се приказује у стрингу верзије, а рутине шифровања и дешифровања раде без проблема.

Брисање модула из библиотеке

Сада, након свега овога, обрисаћемо датотеку `cipher_version.o` из библиотеке.

За то ћемо користити опцију `-d` (избриши). Користићемо и опцију `-v` (вербосе) како би `ar` исписао шта је урадио, као и опцију `-s` (индекс) за ажурирање индекса у датотеци библиотеке.

ar -dsv libcipher.a cipher_version.o

`ar` извештава да је уклонио модул. Слово `d` значи `избрисан`.

Уколико од `ar` затражимо да наведе модуле у библиотеци, видећемо да смо се вратили на два модула.

ar -t libcipher.a

Уколико планирате да бришете модуле из своје библиотеке, не заборавите да уклоните њихове дефиниције из заглавља библиотеке.

Делите свој код

Библиотеке омогућавају дељење кода на практичан, али и приватан начин. Свако коме дате библиотеку и заглавље може да користи вашу библиотеку, док сам изворни код остаје приватан.