Create lib file linux

Shared libraries with GCC on Linux

Libraries are an indispensable tool for any programmer. They are pre-existing code that is compiled and ready for you to use. They often provide generic functionality, like linked lists or binary trees that can hold any data, or specific functionality like an interface to a database server such as MySQL.

Most larger software projects will contain several components, some of which you may find use for later on in some other project, or that you just want to separate out for organizational purposes. When you have a reusable or logically distinct set of functions, it is helpful to build a library from it so that you do not have to copy the source code into your current project and recompile it all the time — and so you can keep different modules of your program disjoint and change one without affecting others. Once it is been written and tested, you can safely reuse it over and over again, saving the time and hassle of building it into your project every time.

Building static libraries is fairly simple, and since we rarely get questions on them, I will not cover them. I will stick with shared libraries, which seem to be more confusing for most people.

Before we get started, it might help to get a quick rundown of everything that happens from source code to running program:

  1. C Preprocessor: This stage processes all the preprocessor directives. Basically, any line that starts with a #, such as #define and #include.
  2. Compilation Proper: Once the source file has been preprocessed, the result is then compiled. Since many people refer to the entire build process as compilation, this stage is often referred to as compilation proper. This stage turns a .c file into an .o (object) file.
  3. Linking: Here is where all of the object files and any libraries are linked together to make your final program. Note that for static libraries, the actual library is placed in your final program, while for shared libraries, only a reference to the library is placed inside. Now you have a complete program that is ready to run. You launch it from the shell, and the program is handed off to the loader.
  4. Loading: This stage happens when your program starts up. Your program is scanned for references to shared libraries. Any references found are resolved and the libraries are mapped into your program.

Steps 3 and 4 are where the magic (and confusion) happens with shared libraries.

Now, on to our (very simple) example.

foo.h defines the interface to our library, a single function, foo(). foo.c contains the implementation of that function, and main.c is a driver program that uses our library.

For the purposes of this example, everything will happen in /home/username/foo

Step 1: Compiling with Position Independent Code

We need to compile our library source code into position-independent code (PIC): 1

Step 2: Creating a shared library from an object file

Now we need to actually turn this object file into a shared library. We will call it libfoo.so:

Step 3: Linking with a shared library

As you can see, that was actually pretty easy. We have a shared library. Let us compile our main.c and link it with libfoo. We will call our final program test. Note that the -lfoo option is not looking for foo.o, but libfoo.so. GCC assumes that all libraries start with lib and end with .so or .a (.so is for shared object or shared libraries, and .a is for archive, or statically linked libraries).

Telling GCC where to find the shared library

Uh-oh! The linker does not know where to find libfoo. GCC has a list of places it looks by default, but our directory is not in that list. 2 We need to tell GCC where to find libfoo.so. We will do that with the -L option. In this example, we will use the current directory, /home/username/foo:

Step 4: Making the library available at runtime

Good, no errors. Now let us run our program:

Oh no! The loader cannot find the shared library. 3 We did not install it in a standard location, so we need to give the loader a little help. We have a couple of options: we can use the environment variable LD_LIBRARY_PATH for this, or rpath. Let us take a look first at LD_LIBRARY_PATH:

Using LD_LIBRARY_PATH

There is nothing in there. Let us fix that by prepending our working directory to the existing LD_LIBRARY_PATH:

What happened? Our directory is in LD_LIBRARY_PATH, but we did not export it. In Linux, if you do not export the changes to an environment variable, they will not be inherited by the child processes. The loader and our test program did not inherit the changes we made. Thankfully, the fix is easy:

Good, it worked! LD_LIBRARY_PATH is great for quick tests and for systems on which you do not have admin privileges. As a downside, however, exporting the LD_LIBRARY_PATH variable means it may cause problems with other programs you run that also rely on LD_LIBRARY_PATH if you do not reset it to its previous state when you are done.

Using rpath

Now let s try rpath (first we will clear LD_LIBRARY_PATH to ensure it is rpath that is finding our library). Rpath, or the run path, is a way of embedding the location of shared libraries in the executable itself, instead of relying on default locations or environment variables. We do this during the linking stage. Notice the lengthy “-Wl,-rpath=/home/username/foo” option. The -Wl portion sends comma-separated options to the linker, so we tell it to send the -rpath option to the linker with our working directory.

Excellent, it worked. The rpath method is great because each program gets to list its shared library locations independently, so there are no issues with different programs looking in the wrong paths like there were for LD_LIBRARY_PATH.

rpath vs. LD_LIBRARY_PATH

There are a few downsides to rpath, however. First, it requires that shared libraries be installed in a fixed location so that all users of your program will have access to those libraries in those locations. That means less flexibility in system configuration. Second, if that library refers to a NFS mount or other network drive, you may experience undesirable delays — or worse — on program startup.

Using ldconfig to modify ld.so

What if we want to install our library so everybody on the system can use it? For that, you will need admin privileges. You will need this for two reasons: first, to put the library in a standard location, probably /usr/lib or /usr/local/lib, which normal users do not have write access to. Second, you will need to modify the ld.so config file and cache. As root, do the following:

Now the file is in a standard location, with correct permissions, readable by everybody. We need to tell the loader it is available for use, so let us update the cache:

That should create a link to our shared library and update the cache so it is available for immediate use. Let us double check:

Now our library is installed. Before we test it, we have to clean up a few things:

Clear our LD_LIBRARY_PATH once more, just in case:

Re-link our executable. Notice we do not need the -L option since our library is stored in a default location and we are not using the rpath option:

Читайте также:  Windows как обновить эскизы

Let us make sure we are using the /usr/lib instance of our library using ldd:

Good, now let us run it:

That about wraps it up. We have covered how to build a shared library, how to link with it, and how to resolve the most common loader issues with shared libraries — as well as the positives and negatives of different approaches.

  1. It looks in the DT_RPATH section of the executable, unless there is a DT_RUNPATH section.
  2. It looks in LD_LIBRARY_PATH. This is skipped if the executable is setuid/setgid for security reasons.
  3. It looks in the DT_RUNPATH section of the executable unless the setuid/setgid bits are set (for security reasons).
  4. It looks in the cache file /etc/ld/so/cache (disabled with the -z nodeflib linker option).
  5. It looks in the default directories /lib then /usr/lib (disabled with the -z nodeflib linker option).

What is position independent code? PIC is code that works no matter where in memory it is placed. Because several different programs can all use one instance of your shared library, the library cannot store things at fixed addresses, since the location of that library in memory will vary from program to program. ↩

GCC first searches for libraries in /usr/local/lib, then in /usr/lib. Following that, it searches for libraries in the directories specified by the -L parameter, in the order specified on the command line. ↩

Источник

C: создание и применение shared library в Linux

Библиотека — это файл, содержащий скопилированный код из нескольких объектных файлов в один файл библиотеки, который может содержать функции используемые другими программами.

Библиотеки могут быть статичными (static) и динамическими или разделяемыми (dynamic, shared).

Ниже — краткий пример создания и применения shared library на C в Linux.

Доступ к общей библиотеке может осуществляться по нескольким именам:

  • имя, используемое «линкером» /usr/bin/ld (linker name), в виде слова lib + имя библиотеки + расширение .so , например — libpthread.so
  • Полное имя (fully qualified name или soname), в виде lib + name + .so + версия (например — libpthread.so.1 )
  • реальное имя — полное имя файла, содержащего версию библиотеки, в виде lib + имя + .so + версия + минорная версия и опционально — версия релиза (например — libpthread.so.1.1 )

Версия для общей бибилиотеки меняется в случае, когда изменения в коде этой бибилиотеки делают её несовместимой с предыдущими версиями, например — если из библиотеки была убрана какая-то функция ( libpthread.so.1 )

Минорная версия меняется, если изменения не затронули совметимость библиотеки, например — какой-то фикс в одной из функций. В таком случае версия останется прежней, а изменится только минорная часть ( libpthread.so.1.1 ).

Такое соглашение об именах версий библиотек позволяет существование разных версий одной библиотеки в одной системе.

Программа, которая будет линковаться с этой бибилиотекой, не будет привязана к определённому файлу с последней версией библиотеки. Вместо этого, после установки последней версии — все связанные программы будут использовать её.

Создание библиотеки

Создадим простой файл libhello.c с одной функцией:

Создаём заголовочный файл библиотеки libhello.h с прототипом функции:

Приступаем к сборке библиотеки.

Создаём объектный файл, указав опцию PIC (Position Independent Code), Warning ( -Wall — warning all), -g для добавления дебаг-информации и -c — что бы создать только файл библиотеки, без вызова линкера:

Проверяем — теперь у нас имеется объектный файл .o :

Источник

Как создавать, собирать, устанавливать и использовать пакеты с программами и библиотеками для UNIX-подобных систем

Речь пойдёт о программах и библиотеках для UNIX-подобных систем, распространяемых в виде исходного кода (в том числе в виде тарболлов), написанных обычно на C и C++ (хотя этот же порядок работы может применяться к софту на любом языке). Многие вещи в этой статье написаны применительно конкретно к GNU/Linux, хотя многое из статьи может быть обобщено и на другие UNIX-подобные ОС.

Под словом «пакет» я понимаю в этой статье пакет с исходными текстами, причём не пакет конкретного дистрибутива GNU/Linux, а просто пакет, исходящий от оригинальных авторов софта (UPD от 2017-02-09: кроме тех случаев, где из контекста ясно, что слово «пакет» употреблено в другом смысле).

В этой статье я разберу следующие вопросы:

  • Вот скачал программу или библиотеку. Как её собрать и установить? Как воспользоваться библиотекой?
  • Что такое префикс (prefix) установки? В чём разница между сборкой и установкой? Куда обычно устанавливают программы?

Я разберу только совсем базовые вещи. Те, которые типичные участники сообщества свободного ПО, программирующие на C и C++ под UNIX-подобные системы, обычно уже знают. Как создавать тарболлы (на примере «голого» make) и как устанавливать чужие тарболлы. Advanced советы по созданию «хороших» пакетов я не дам. «Продвинутые» вещи читайте в документации систем сборки, в замечательной статье «Upstream guide» от Debian (в её конце есть ещё куча ссылок о создании «хороших» пакетов). Многое в этой статье можно было сделать по-другому, моя цель: дать хотя бы один способ, не пытаться объять необъятное.

Предупрежу: начало будет совсем простым, но ближе к концу будет всё поинтереснее.

И ещё одно предупреждение. Я написал эту статью наскоро для одного человека. А после написания подумал, мол, раз уж написал, выложу уж на Хабр, чтоб не пропала. Поэтому в статье есть недочёты типа нечёткого структуирования статьи, фраз типа «не осуществляет сама инклудивание» и пр. Я это исправлять не буду. Может когда-нибудь в следующей жизни, когда руки дойдут. Выбор был между тем, чтобы публиковать так или не публиковать вовсе.

Итак, начнём мы с того, что создадим пакет с программой Hello, world. Для начала определимся с системой сборки.

Собственно, сама сборка пакета обычно осуществляется с помощью программы make (хотя это не единственный путь). Конфиг для make обычно лежит в файле под названием Makefile.

Есть несколько вариантов: просто использовать make либо использовать совместно с make некую высокоуровневую систему сборки (обычно autotools или cmake), которая будет, собственно, генерировать конфиги для make.

Мы в нашем примере будет использовать только make для простоты. Итак, создаём hello.c:

Внимание! Работает на GNU/Linux. Работа под macOS не гарантируется! Этот Makefile не является портируемым вообще на все UNIX-подобные системы! Подробнее будет дальше.

здесь означает символ табуляции.

Это далеко не единственный способ написать этот Makefile. Вообще, цель всей моей статьи — дать некую базу. Что ещё можно в этом Makefile изменить, вы можете потом узнать из других источников.

Также в этой статье я предполагаю, что вы знаете уж совсем базовые вещи про make. Я имею в виду, что вы знаете, что Makefile — это описание дерева зависимостей, и что он нужен, чтобы собрать ровно те файлы, которые нуждаются в сборке. В этой статье я расскажу про использование make при создании пакетов, про «виртуальные» цели make такие как install и all. Ещё я предполагаю, что вы уже знаете совсем базовые вещи про то, как запускать компилятор из командной строки, т. е. вы знаете, что такое cc -o hello hello.c .

Итак, теперь давайте разбирать наш Makefile.

Для начала скажу следующее. Допустим, пользователь скачал пакет. Ему нужно сперва собрать его, т. е. получить бинарники в каталоге с самим пакетом (либо в случае out of tree build получить их в неком другом каталоге, что не меняет сути), а затем установить, т. е. скопировать полученные бинарники в место их окончательного хранения в системе.

То есть смотрите. Вы залогинены под юзером user. Ваш домашний каталог /home/user. Вы скачали пакет, распаковали его, скажем в /home/user/Desktop/foo. Далее вы его собрали. Собрали в этой же папке, /home/user/Desktop/foo. Либо, если речь идёт об out of tree build, вы создали ещё одну папку /home/user/Desktop/foo-build и собрали в ней. Теперь, после сборки вы решаете установить её в /usr/local. Вот тут уже вам нужны права root. Вы при помощи sudo устанавливаете эту программу в /usr/local.

Пара слов о размещении каталогов в системе. Разберу лишь некоторые каталоги. Более подробную информацию можно найти в Filesystem Hierarchy Standard (поддержан многими дистрибутивами GNU/Linux) и по команде «man 7 hier» в GNU/Linux и, возможно, других ОС. Во всяком случае я расскажу, как они были устроены, скажем, лет пять назад (т. е. где-то в 2012-м году), всякие новые веяния типа недавнего нововведения в Fedora «давайте-ка мы всё переместим в /usr» я рассматривать не буду.

  • /bin — это бинарники (т. е. испоняемые файлы), важные для работы системы на ранних стадиях загрузки.
  • /sbin — это то же самое с тем отличием, что эти бинарники обычно может запускать только root.
  • /lib — бинарные файлы библиотек, нужные для программ из /bin и /sbin
  • /usr — «вторая иерархия файлов», т. е. это как бы «ещё один /»
  • /usr/bin — то же, что /bin, но на этот раз это бинарники, некритичные для загрузки
  • /usr/sbin — то же, что /sbin, но опять-таки, некритичные для загрузки
  • /usr/lib — бинарные файлы библиотек, которые не нужны для программ из /bin и /sbin
  • /usr/include — хедеры библиотек
  • /usr/local — «третья иерархия файлов», она опять содержит /usr/local/bin, /usr/local/sbin, /usr/local/lib и /usr/local/include, на этот раз эти каталоги предназначены для «локального администратора». Что это значит? Это значит, что (в случае дистрибутивов GNU/Linux с пакетным менеджером) каталоги, о которых речь шла до этого, находятся под контролем пакетного менеджера. А вот каталоги внутри /usr/local находятся в распоряжении «локального администратора», т. е. вас. Если вы хотите поставить что-то из сорцов, в обход менеджера пакетов, то можно поставить туда.
Читайте также:  Управление курсором без мыши windows

Теперь по поводу prefix. Когда вы устанавливаете какой-нибудь пакет, вы должны указать ему так называемый prefix, т. е. каталог, в который всё будет установлено. В этом каталоге будет создан подкаталог bin, в него будет установлены бинарники, будет создан подкаталог lib, туда будут установлены бинарные файлы библиотек, и т. д.

То есть, например, вы устанавливаете пакет, указывая ему prefix /usr/local. Тогда бинарники пойдут в /usr/local/bin, бинарные файлы библиотек — в /usr/local/lib и так далее.

Теперь вернусь к разбору Makefile. PREFIX — это и есть тот prefix, про который я сейчас говорил. В качестве дефолтного префикса (пользователь сможет его переопределить) у нас указан /usr/local — хороший дефолтный выбор. Обычно его всегда указывают в качестве дефолтного префикса при создании пакетов (к сожалению, некоторые пакеты этого всё же не делают, дальше будет подробнее). Далее идёт CC. Это стандартное название для переменной make, в которую кладут компилятор, в данном случае cc. cc — это в свою очередь обычно используемая команда для запуска компилятора по умолчанию в данной системе. Это может быть gcc или clang. На некоторых системах команда cc может отсутствовать. CFLAGS — стандартное название для переменной с флагами компиляции.

Если просто набрать make, то будет выполнена та цель, которая идёт в Makefile первой. Обычная практика в том, чтобы называть её all. И такая цель обычно собирает весь проект, но ничего не устанавливает. Эта цель обычно «виртуальная», т. е. у нас нет файла под названием all. Т. к. наша задача собрать лишь один бинарник hello, то мы просто делаем all зависящим от hello. Далее идёт описание цели сборки hello. Его можно было в принципе разбить на два этапа: сборка hello.o из hello.c и сборка hello из hello.o. Я так делать не стал для простоты.

Далее идёт install, установка. Это тоже виртуальная цель. Она зависит от all. Это сделано на случай, если пользователь сразу наберёт «make install», без «make». В этой цели мы сперва создаём папку, куда будем устанавливать, т. е. как и следовало ожидать, $(PREFIX)/bin, а затем устанавливаем в неё утилитой install.

Что делает install? Это почти то же, что и cp. Точные отличия я и сам не знаю. Для установки программ нужно использовать install, а не cp.

Бинарник устанавливается в $(PREFIX)/bin, потому что именно так и нужно делать. Бинарники идут в подкаталог bin в prefix, бинарные файлы библиотек — в lib в PREFIX и т. д.

Здесь я предполагаю, что у вас на системе установлен так называемый BSD install. В GNU/Linux’е он есть. В некоторых системах его может не быть. Может быть какой-нибудь другой install, который в этой ситуации не сработает. Именно это я имел в виду, когда сказал, что работа в разных ОС не гарантируется.

Здесь я не рассматривал DESTDIR, который, между прочим, крайне рекомендуется использовать в Makefile. Я даже не рассматривал цель clean.

Окей, давайте теперь создадим окончательный тарболл, так называют файл с расширением .tar.gz, .tar.xz и так далее. Поместите файлы hello.c и Makefile в папку hello-1.0 (принято указывать номер версии при создании тарболла). Затем установите в качестве текущего каталога тот, который содержит hello-1.0 и наберите, например:

Это создаст архив, внутри которого есть каталог hello-1.0, который содержит hello.c и Makefile. Таков способ распространения пакетов.

C++-вариант пакета. Исходник будет тем же, его нужно будет назвать hello.cpp. Makefile будет таким:

Обратите внимание, что стандартное название переменной для флагов к компилятору C++ — это CXXFLAGS, а не CPPFLAGS. CPPFLAGS же — это название переменной для флагов к препроцессору C.

Теперь разберём, как устанавливать пакеты из сорцов (любые, программы и библиотеки). Независимо от системы сборки. Этот алгоритм будет пригоден в том числе для тарболла, который мы создали только что. Допустим, что пакет, который нужно собрать, тоже называется hello.

Первый шаг: скачать и зайти в папку с сорцами. Тут два варианта: скачиваем из системы контроля версий (я разберу для примера git) или скачиваем тарболл.

Первый вариант. git. Делаем clone:

Это создаст папку hello. Без номера версии, т. к. мы скачали из системы контроля версий. Делаем cd в созданную папку, т. е. cd hello .

Второй вариант. Тарболл. Скачиваем тарболл. Потом набираем команду, скажем, tar -xf hello-1.0.tar.xz . Это создаст папку, например, hello-1.0 . Обычно, если создатель пакета сделал всё правильно, имя папки будет содержать номер версии. Потом делаем cd в полученную папку, например, cd hello-1.0 .

Теперь нужно собрать. Будем считать для простоты, что мы не будем делать out of tree build (если автор пакета требует out of tree build, там обычно будут написаны инструкции, как это сделать). А значит, собирать мы будем в этой же папке, где сорцы. Т. е. в этой папке, в которую мы сейчас сделали cd.

Дальнейшие действия зависят от системы сборки, выбранной в проекте. Но независимо от системы сборки мы в процессе сборки должны будем указать prefix. Причём обычно его нужно будет указать именно на этапе сборки, а не на этапе установки, т. к. часто prefix захардкоживается внутрь бинарника. А это значит, что после установки программы в определённое место её нельзя просто так взять и передвинуть.

Я дам здесь примерные инструкции, работающие в большинстве случаев. Точные инструкции вы увидите у автора проекта, там могут быть разные нюансы.

Обязательно указывайте prefix при сборке (что бы там не писал в инструкции автор). Если вы не укажите, то будет выбран дефолтный. Обычно это /usr/local, и это достаточно хороший выбор. А если нет? Что если автор пакета указал какой-то другой дефолтный префикс? Вы установите непонятно куда. В частности libqglviewer использует в качестве дефолтного префикса /usr, что совершенно неправильно (я отправил автору баг репорт). Итак, совершенно всегда указывайте префикс. Читайте инструкции, которые автор указывает на своём сайте и прикидывайте, куда впихнуть prefix.

Итак, какие могут быть системы сборки. Во-первых, может быть просто make. Такой вариант с голым make встречается редко. Один из немногих пакетов с «голым» make — bzip ( www.bzip.org ). В случае с нашим hello-1.0.tar.xz, который мы создали, у нас именно такой вариант.

Итак, собирать в случае голого make нужно так:

(Конкретно в случае с bzip на этапе сборки указывать PREFIX не надо. Но теоретически можно представить себе пакет, который захардкоживает PREFIX внутрь бинарника. Поэтому в общем случае PREFIX нужен.)

Следующий вариант — это autotools. В этом случае собираем так:

Следующий вариант — cmake. Собираем так (обратите внимание на точку в конце команды cmake):

Откуда точка в конце? Дело в том, что cmake’у нужно передавать путь к сорцам. А поскольку у нас не out of tree build, мы собираем здесь же. Сорцы находятся там же, где находимся мы. Поэтому точка, т. е. текущий каталог.

В случае с autotools и cmake команда, генерирующая Makefile (т. е. ./configure или cmake) записывает prefix в конфиги к make, поэтому в команде make (и в команде make install, про которую речь пойдёт далее) указывать prefix уже не нужно.

Читайте также:  Запуск windows powershell с параметрами

Итак, собрали одним из этих способов. Что дальше? Теперь нужно установить.

В случае голого make это делается так:

Нужно будет указать тот же prefix, который вы указывали при сборке.

В случае autotools и cmake так:

В случае, если для записи в prefix вам нужен sudo, то вот эту команду для установки нужно будет набирать с sudo. Вообще, сборка всегда осуществляется с обычными правами, а вот установка осуществляется с теми правами, которые нужны, чтобы записать в prefix.

Окей, теперь посмотрим, что мы натворили. Допустим, что ставили мы не что-нибудь, а тот hello-1.0.tar.xz, который создавали до этого. Допустим также, что prefix, который мы указали, был /foo. Тогда в нашей системе появятся папки /foo, /foo/bin (если их не было до этого) и файл /foo/bin/hello. Что произошло? Указанная в командной строке при сборке переменная PREFIX=/foo переопределила заданную в Makefile PREFIX=/usr/local. В результате указанные в Makefile команды mkdir -p и install стали такими:

В результате бинарник положился в /foo/bin.

Теперь хочу ещё немного поговорить про префиксы. Какие префиксы вообще есть?

Префикс /. Вряд ли когда-нибудь вам придётся его выбирать. Он используется для программ, критичных для ранних стадий загрузки ОС (т. е. критичные элементы для загрузки находятся в /bin, /lib и т. д.) (впрочем, даже если вам нужно установить программу в /, её сперва устанавливают в /usr, т. е. собирают и устанавливают с префиксом /usr, а потом перемещают необходимое в / [т. е. перемещают из /usr/bin в /bin, скажем], во всяком случае именно так поступают авторы Linux From Scratch 7.10 с пакетом, скажем, bash).

Префикс /usr. Стандартный префикс, используемый обычно для программ, установленных через менеджер пакетов. То есть если вы установили программу через менеджер пакетов, она ведёт себя так, словно она собрана и установлена на вашей системе с префиксом /usr. Самому устанавливать пакеты с префиксом /usr нельзя.

Префикс /usr/local. Отличный префикс для установки туда программ самостоятельно. Хорош тем, что /usr/local/bin есть в дефолтном PATH (во всяком случае в дебиане). То есть сразу после установки программы вы сможете просто запускать программу по названию. Потому что бинарник лежит в /usr/local/bin, а /usr/local/bin есть в PATH. Плох тем, что там все программы лежат смешанно. Вот допустим, вы установили библиотеку foo, а потом библиотеку bar. Обе в этот prefix. Тогда дерево может выглядеть так (в совсем упрощённом виде):

Видите? Всё смешано. Нет единой папки, которая бы содержала «всё, связанное с foo» и другой папки, которая бы содержала «всё, связанное с bar». (Хотя это ваше дело, считать, что это действительно плохо или нет). Понятное дело, что такая же проблема присутствует при любой установке разных пакетов в один префикс. То есть префикс /usr страдает от того же: пакеты «размазаны» по системе (здесь уже речь идёт о пакетах, поставленных через менеджер пакетов, т. е. тех, которые, собственно, составляют систему). Собственно, это и есть одно из бросающихся отличий большинства UNIX-подобных систем от Windows. В Windows каждая программа находится в своей папке в Program Files. В большинстве UNIX-подобных систем она «размазана» по системе. (UPD от 2017-02-09: в Windows программы на самом деле тоже «размазаны», скажем, по реестру, просто это не так бросается в глаза.) Существуют дистибутивы GNU/Linux, «решающие» эту проблему, например, GoboLinux. Там каждый пакет в своём каталоге, как в Windows.

Префиксы вида /opt/XXX. Папку /opt предполагается использовать следующим образом: в ней нужно создавать подкаталоги, называть их названиями пакетов и использовать эти подкаталоги как префиксы. При таком подходе указанная выше проблема /usr/local (если считать её проблемой) исчезает. Каждый пакет будет установлен в свой каталог. Приведённый выше пример с foo и bar будет выглядеть так (я бы посоветовал в названии подкаталогов в /opt указывать ещё и номер версии):

Недостаток у такого решения тоже есть. Вам придётся самому добавлять все эти бесчисленные каталоги /opt/foo-1.0/bin (для каждого пакета) в PATH.

Префиксы, соответствующие домашним каталогам. Т. е., скажем, /home/user. Советую в случае, когда хочется поставить «только для себя», т. е. только для одного юзера. Или когда нет прав root. Возможно, ваши конфиги, поставляемые с ОС уже настроены таким образом, чтобы помещать

/bin в PATH при условии, что такой каталог есть. Так что PATH будет настроен как надо.

Каждый префикс может содержать в себе свои bin, sbin, lib, include и т. д.

Итак, что из этого выбрать? Если нужно поставить на всей системе, то я бы посоветовал /opt/XXX. Я сам так обычно ставлю.

Теперь о сборке, установке библиотеки и её использовании. Собирается и ставится библиотека так же, как и любой другой пакет, я это уже рассказывал выше. Так что перейдём сразу к использованию. Вот мы установили библиотеку в некий префикс, допустим, /foo. Теперь в /foo/include появились хедеры этой библиотеки, а в /foo/lib — бинарные файлы библиотеки (.so — динамическая библиотека, либо .a — статическая, либо и то, и то).

Допустим, нужно собрать некий файл a.c с этой библиотекой. Как это сделать?

Во-первых, вверху файла нужно написать #include, соответствующий подключаему хедеру. Ну а собирать нужно так:

Давайте разбираться. Для начала скажу, что I (большая английская и) и l (маленькая английская эль) — это две разные буквы. Не перепутайте их в приведённых командах.

Первая команда производит компиляцию, то есть создаёт a.o на основе a.c. Вторая — линковку, то есть окончательный бинарник на основании a.o.

В первой команде мы указали -I/foo/include. Это указание той папки, где нужно искать хедеры. Путь из этой опции соединится с файлом, указанным в #include. То есть если в командной строке указано -I/foo/include, а в файле написано #include , то получится /foo/include/foo.h, он-то и будет заинклуден.

Здесь опция -I/foo/include не осуществляет сама инклудивание. Она лишь указывает папку, где нужно искать, поэтому нужен ещё и #include. То есть нужен и -I/foo/include, и #include, одного из них недостаточно.

Линковка. -L/foo/lib — это указание папки, где нужно искать бинарные файлы библиотеки, т. е. файлы .so и .a. -lfoo — это указание на то, что нужно, собственно, прилинковать эту библиотеку к результирующему бинарнику. Имя библиотеки, указанное в опции -lfoo, соединится с папкой, указанной в -L/foo/lib и получится /foo/lib/libfoo (в начало названия файла вставляется слово «lib»), затем сюда прибавится .so (или .a) и опционально номер версии и получится /foo/lib/libfoo.so или, скажем, /foo/lib/libfoo.so.1. Это и будет тем именем .so-файла, который будет искаться.

Так же, как и при компиляции (a.o из a.c), нужны обе опции -L/foo/lib и -lfoo. -L/foo/lib указывает, где искать. А -lfoo даёт окончательную команду на прилинковку.

Вместо -lfoo можно прямо написать целиком путь до файла библиотеки, который нужно слинковать, например, /foo/lib/libfoo.so.1. Тогда опция -L/foo/lib не нужна. Получится так:

Библиотеку (будь то полный путь к библиотеке или опция вида -lfoo) нужно указывать после «своих» объектных файлов, в данном случае a.o. (Может, и не нужно, но на всякий случай лучше так делать.)

Можно соединить две наши команды в одну, тогда будет что-то такое:

В случае, если библиотека установлена с префиксом /usr (то есть просто установлена через менеджер пакетов), то опции -I и -L не нужны, считайте, что -I/usr/include и -L/usr/lib у вас как бы уже есть. То же, возможно, относится к /usr/local.

Если существует некая библиотека под названием foo, то она обычно запаковывается для дебиан в пакеты с названиями libfoo (или libfoo1, libfoo2) и libfoo-dev. libfoo содержит файлы .so, а libfoo-dev — хедеры. То есть хитрые разработчики дебиан умеют из одного пакета сделать несколько. На сборочных машинах дебиана пакет собирается и расфасовывается в несколько пакетов.

Если вы установите у себя на машине libfoo и libfoo-dev, то результат будет как если бы вы сами собрали пакет foo из исходных кодов с префиксом /usr. У вас на системе будут, скажем, файлы:

Напомню, что список файлов в данном пакете можно посмотреть по команде, скажем, dpkg -L libfoo . Ну а найти пакет по файлу с помощью dpkg -S /usr/include/foo.h .

Источник

Оцените статью