Бібліотеки, утиліта libtool, Розробка програмного забезпечення для Linux
Сенс використання бібліотек у тому, що той самий код може застосовуватися в різних програмах, і його не треба писати щоразу заново. У разі динамічних бібліотек вигода ще й у тому, що можна цей код взагалі не включати у файли, що виконуються. У разі виклику він прочитається з файлу бібліотеки.
У нашому калькуляторі вміст вмістника calculate.c повністю включали в виконуваний файл. Отже, якщо ми захочемо ті ж самі функції використовувати в іншій програмі, ми повинні писати їх заново. Можна, звичайно, скористатися широко відомими командами Виділити-Копіювати-Вставити. Але, все одно, це - зайві рухи тіла, зайве збільшення в розмірах виконуваного файлу внаслідок засмічення його рутинними функціями. Та й самого вихідника може опинитися під рукою. А можливо, я захочу внести в ці функції поліпшення. Це означає, що доведеться переробляти всі програми, у яких використовуються, щоб у всіх цих програмах були нові версії функцій. Чи не можна, щоб вони були десь в одному місці, і їх можна було брати звідти для всіх програм, коли вони знадобляться?
Як ви, напевно, здогадуєтеся, ми робитимемо з вихідника calculate.c бібліотеку, яку потім підключатимемо до програми, що виконується, отриманої з одного main.c.
Отже, беремося за чергову переробку нашого калькулятора.
Найпростіший спосіб створити бібліотеку з файлу calculate.c наступний:
gcc-c calculate.c
ar cru libcalculate.a calculate.o
Тут gcc з прапором -c отримує відомий нам об'єктний файл, команда ar створює архів, як бібліотеки існують. А команда ranlib генерує індекс функцій, що містяться в бібліотеці, і зберігає цей індекс у її файлі. Цю процедуру завжди рекомендуєтьсяробити, оскільки індекс прискорює процедуру зв'язування бібліотеки з програмою.
Надалі цей файл можна вручну помістити у стандартний каталог бібліотек (це /usr/lib та /usr/local/lib) і можна пов'язувати її за допомогою прапора -lcalculate (зверніть увагу, що назва бібліотек має починатися з префікса lib-).
Але відомо, що це нераціональний метод щодо продуктивність праці. Давайте розглянемо більш практичні варіанти. Для цього ми познайомимося із ще одним компонентом набору інструментів GNU – libtool. Його призначення – автоматизувати рутинні операції по роботі з бібліотеками та забезпечувати розробнику зручність при їх написанні та тестуванні.
Створіть новий каталог проекту libtooldemo. Перенесіть до нього файли calculate.c та main.c із C-версії калькулятора. Тільки обов'язково видаліть з кожного три рядки, що вимагають підключення заголовкового файлу config.h. Поки ми користуватися цим заголовним файлом не будемо.
Вищевказані рядки мають бути видалені!
А що файл calculate.h?
Про нього особлива розмова. Наша бібліотека компілюватиметься компілятором C. Але викликатися вона може і з програми, написаної на С++. А компілятор C++ має таку особливість – він змінює назви функцій компіляції. Отже, назва функції в програмі, що викликає, не буде збігатися з назвою функції в бібліотеці.
Щоб уникнути такої ситуації, треба в заголовному файлі, що включається в код програми, що виконується, явно вказати, що бібліотечна функція написана на C. Таким чином, наш новий заголовний файл calculate.h виглядатиме так:
float Calculate (float Numeral, char Operation [4]);
Давайте створимо бібліотеку, що підключається, з вихідника calculate.c. Для цьогоспочатку створимо об'єктний файл.
libtool gcc -c calculate.c
Подивіться, що з'явилося у каталозі проекту. Це по-перше, вже знайомий нам об'єктний файл calculcate.o. По-друге, це скрипт calculate.lo, згенерований утилітою libtool. Він вказуватиме, як зібрати бібліотеку з об'єктних файлів. Більше нічого не бачите? Тоді наберіть:
І ви виявите, що перейшли у вкладений каталог .libs, який не видно у файловому менеджері, тому що за замовчуванням у ньому не видно всіх файлів і папок, назви яких починаються з точки. У цьому вкладеному каталозі міститься інша версія об'єктного файлу calculate.o. Різниця між ними в тому, що один має позиційно-незалежний код, а інший – позиційно-залежний. Але дозвольте поки не вдаватися до подробиць про це. Утиліта libtool сама вирішить за нас, який варіант краще взяти на формування бібліотеки при цій конфігурації системи.
Зрештою сформуємо саму бібліотеку.
libtool gcc -rpath /usr/local/lib -o libcalculate.la calculate.lo
У проекті з'явився libcalculate.la. Це скрипт, який знадобиться програмі libtool. Сама бібліотека знаходиться у вкладеному каталозі .libs. Якщо ви туди зайдете, ви виявите файл із розширенням статичної бібліотеки libcalculate.a та файли з розширенням динамічної бібліотеки libcalculate.so (ми вже свого часу згадували, що ці типи бібліотек мають саме такі розширення файлів).
Вміст файлу libcalculate.a зв'язується з програмою під час компонування, але бібліотечних функцій цей файл не містить. Вона містить лише посилання на інший файл – libcalculate.so, який містить бібліотечні функції та зв'язується з програмою динамічно, тобто, на вимогу у процесі виконання програми.
Опція -rpath із зазначенням шляху достандартному каталогу бібліотек вказує компонувальнику, що необхідно створити бібліотеку з динамічним зв'язуванням, тобто передбачити обидва - і a-файл і so-файл, і що вони при інсталяції будуть розміщуватися саме в тому каталозі, який вказаний після -rpath. Шлях до цього каталогу буде збережено в a-файлі, і саме цим шляхом буде здійснюватися пошук динамічної бібліотеки.
Можна вказати компонувальнику, що потрібно створити статичну бібліотеку. Для цього потрібно скористатися опцією -static.
libtool gcc -static -o libcalculate.la calculate.lo
Як ви бачите, опції -rpath тут немає. Вона і не потрібна, тому що всі бібліотечні функції будуть включені в виконуваний файл і немає необхідності будь-що викликати динамічно.
У каталозі .libs ви so-файлу вже не знайдете, тому що весь його вміст включено в статично підключений a-файл.
Давайте тепер створимо файл, який буде пов'язаний з цією бібліотекою.
libtool gcc -o kalkul main.c libcalculate.la -lm
Можна за бажання зробити так, щоб математичні функції входили до нашої бібліотеки. Для цього перекомпонуємо її із зазначенням обов'язкового включення libm.
libtool gcc -rpath /usr/local/lib -o libcalculate.la calculate.lo -lm
Тоді до програми, що виконується, можна вже не підключати libm.
libtool gcc -o kalkul main.c libcalculate.la
Оскільки ми виконуваний файл kalkul формували за допомогою libtool, він також міститься в каталогі .libs, а в каталозі проекту формується скрипт з такою самою назвою kalkul. Але це не повинно вас турбувати. Ці скрипти створюються з метою забезпечення головної зручності libtool. Головна зручність у тому, що вона дозволяє одночасно в одному каталозі працювати і з програмами, що виконуються, і знеобхідними їм бібліотеками. Спробуйте запустити програму, що виконується. Програма без проблем знайде та відкриє бібліотеку, хоча ця бібліотека не розташована в системному каталозі. Це дозволяє уникнути надмірного звернення до системного каталогу на етапі розробки.
Вона також дає можливість миттєво встановити всі файли бібліотеки до системного каталогу. Давайте встановимо бібліотеку libcalculate у каталог /usr/local/lib (попередньо зайшовши від імені суперкористувача).
libtool cp libcalculate.la /usr/local/lib
Якщо при цій команді видаються помилки, просто повторіть з початку процедуру компіляції та складання бібліотеки. Можливо, за якихось маніпуляцій ви втратили деякі допоміжні файли.
Зверніть увагу, що установка повинна здійснюватися саме в каталог, який був вказаний в опції -rpath. У цей каталог буде скопійовано не сам скрипт libcalculate.la, а статична та динамічна бібліотеки libcalculate.a та libcalculate.so.
Тепер також за допомогою libtool зробимо по-новому файл, що виконується.
libtool gcc -o kalkul main.c -lcalculate
Скопіюємо його у системний каталог.
libtool cp kalkul /usr/local/bin
І ось тут швидше за все на багатьох з вас чекає неприємний сюрприз – програма вперше не запуститься. Вона видасть повідомлення:
error while loading shared libraries: libcalculate.so.0: не можна Open shared object file: No such file or directory
Тобто лінкер знає шлях /usr/local/lib, а завантажувач динамічних бібліотек його не знає. Це властивість більшості сучасних дистрибутивів. Шлях до бібліотек, що завантажуються, доведеться додавати в систему вручну. Це дуже незручно. Людина, яка вперше зіткнулася з цією проблемою, довго думатиме, в чому справа. Зроблено так, мабуть,з метою безпеки, щоб «ліва» бібліотека, випадково встановлена неуважним користувачем, не вступила в конфлікт із системними бібліотеками. Очевидно, ви повинні дуже докладно описати цю ситуацію для користувача у файлі README.
Додамо вручну потрібну нам дорогу. Для цього треба задати системної змінної LD_LIBRARY_PATH значення /usr/local/lib (за умовчанням ця змінна у більшості версій Linux взагалі відсутня).
Знову запускаємо kalkul. Нарешті він заробив!
Замести сліди своїх експериментів можна так:
libtool rm /usr/local/lib/libcalculate.la
libtool rm /usr/local/bin/kalkul
Дмитро Пантелєїчев (dimanix2006 at rambler dot ru) - Бібліотеки, утиліта libtool Версія для друку