# Linux

# Systemd

# Modify systemd Config Without Touching Upstream Unit File

## 1. Introduction

Most of the popular, modern Linux distributions use *systemd* to manage services. Services like *sshd* or *getty* come with unit files provided by their vendors. Because it isn’t recommended to edit this configuration, we need a different way to modify the service settings.

<div class="bd-anchor" id="bkmrk-"></div>*systemd* supports two ways to achieve this goal. **We can extend the configuration to change the selected parameters only.** The rest of the upstream configuration still applies. In this way, we can benefit from service updates. **On the contrary, overriding the configuration shadows the upstream one completely.**

In this tutorial, we’ll learn about both of these ways to alter service settings.

## 2. Extending the Configuration

Because we’ll work with the ssh server [*sshd*](https://man7.org/linux/man-pages/man8/sshd.8.html) as an example, we need to install it. On a Fedora-based system, we use [*dnf*](https://man7.org/linux/man-pages/man8/dnf.8.html):

```bash
$ sudo dnf install openssh-server
```

**We can extend the vendor’s configuration by creating and filling a file *override. conf* in the */etc/systemd/system/&lt;service\_name&gt;.service.d* directory.** Note that the directory name consists of the service name followed by the *d* suffix. If this directory doesn’t exist, we need to create it.

While editing the *override.conf* file, we should obey the same rules as for the regular unit file. As an example, let’s change the [*RestartSec*](https://www.freedesktop.org/software/systemd/man/systemd.service.html) setting for the *sshd* daemon with an entry in the *Service* section:

```bash
$ cat /etc/systemd/system/sshd.service.d/override.conf
[Service]
RestartSec=50
```

For the changes to take effect, we need to reload services with [*systemctl*](https://www.baeldung.com/linux/differences-systemctl-service):

```bash
$ sudo systemctl daemon-reload
```

Then, we can restart the *sshd* daemon:

```bash
$ sudo systemctl restart sshd.service
```

Finally, let’s check the service status:

```bash
$ systemctl status sshd
● sshd.service - OpenSSH server daemon
     Loaded: loaded (/usr/lib/systemd/system/sshd.service; enabled; vendor preset: disabled)
    Drop-In: /etc/systemd/system/sshd.service.d
             └─override.conf
     Active: active (running) since Mon 2023-07-31 18:29:25 CEST; 4min 45s ago
# ...
```

The *Drop-In* entry indicates that our change is taken into account.

We can make our work easier by using the *edit* verb of the *systemctl* command. It’ll create the services directory *sshd.service.d* and allow editing the *override.conf* file:

```bash
$ sudo systemctl edit sshd.service
```

![systemctl editor ready for preparing the drop in file](https://www.baeldung.com/wp-content/uploads/sites/2/2023/08/systemctl-editor-ready-for-preparing-the-drop-in-file.png)

We’re provided with the commented-out upstream configuration of the service for a better overview.

### 2.1. Multiple Drop-in Files and Naming Convention

*override.conf* is nothing but a default name given by the *systemctl edit*. We can use an arbitrary name because the corresponding service is denoted by the name of the folder. **Moreover, we can create multiple overriding configuration files.** Therefore, we should name them so that their names reflect their purposes.

<div class="bd-anchor" id="bkmrk--3"></div>**The files are applied in lexigraphic order**. We can take advantage of it by starting the file names with numbers, such as *10\_*, *20\_*, and so on. Then, if the same setting is modified in a few files, the one with the highest number takes precedence.

As an example, let’s modify *RestartSec* for *sshd* twice. Let’s set it to 50 seconds in the *10\_RestartSec\_50.conf*:

<div class="code-block code-block-4" id="bkmrk--4" style="margin: 8px 0; clear: both;">  
</div>```bash
$ cat /etc/systemd/system/sshd.service.d/10_RestartSec_50.conf
[Service]
RestartSec=50
```

In the *99\_RestartSec\_100.conf* file, we set it to 100 seconds:

```bash
$ cat /etc/systemd/system/sshd.service.d/99_RestartSec_100.conf
[Service]
RestartSec=100
```

Subsequently, we need to **reload services and restart *sshd***. Finally, let’s check the status of *sshd*:

```bash
$ systemctl status sshd
● sshd.service - OpenSSH server daemon
     Loaded: loaded (/usr/lib/systemd/system/sshd.service; enabled; vendor preset: disabled)
    Drop-In: /etc/systemd/system/sshd.service.d
             └─10_RestartSec_50.conf, 99_RestartSec_100.conf
# ...
```

We see that both drop-ins are listed. Let’s dot the ‘i’ and inspect the *RestartUSec* parameter, which takes value after *RestartSec*:

```bash
$ systemctl show sshd --property=RestartUSec
RestartUSec=1min 40s
```

## 3. Overriding the Configuration

Instead of modifying the existing configuration, we can completely cut ourselves off from the upstream unit file. **We need to create a new configuration file *&lt;service\_name&gt;.service* in the */etc/systemd/system* folder, which will replace the vendor’s version.** Let’s do it right away with the *edit –full* command to *systemctl*:

```bash
$ sudo systemctl edit --full sshd.service
```

This time, an editor with no commented-out copy of settings shows up:

![systemctl editor ready for overwriting](https://www.baeldung.com/wp-content/uploads/sites/2/2023/08/systemctl-editor-ready-for-overwriting.png)

**We can change anything we want**. In our case, let’s modify *RestartSec* again:

```bash
[Service]
RestartSec=50
```

Next, we need to reload the daemons and restart the service. Finally, let’s check its status:

```bash
$ systemctl status sshd.service
● sshd.service - OpenSSH server daemon
     Loaded: loaded (/etc/systemd/system/sshd.service; enabled; vendor preset: disabled)
     Active: active (running) since Tue 2023-08-01 16:39:04 CEST; 6min ago
       Docs: man:sshd(8)
# ...
```

Now, we don’t have the *Drop-In* entry, as the unit file is replaced instead of being extended. We’re informed about it in the *Loaded* line, which shows the unit file path.

## 4. Detecting the Service Modification

**To determine the status of the service in terms of its modification, let’s use the [*system-delta*](https://man7.org/linux/man-pages/man1/systemd-delta.1.html) command.** Throughout this tutorial, we’ll use the syntax:

```bash
$ systemd-delta [OPTIONS]
```

First, let’s issue the command without options. Consequently, it lists all services that were changed in one way or another. Assuming that we’ve modified the *sshd* service configuration with a drop-in file, we’ll find in the output:

```bash
$ systemd-delta
# ...
[EXTENDED]   /usr/lib/systemd/system/sshd.service → /etc/systemd/system/sshd.service.d/override.conf
# ...

```

The *EXTENDED* type tells us about the modification and leads to the drop-in configuration file.

On the other hand, if the custom configuration is created and shadows the original one, we’ll obtain a different result:

```bash
$ systemd-delta
# ...
[OVERRIDDEN] /etc/systemd/system/sshd.service → /usr/lib/systemd/system/sshd.service
# ...
```

Now, the *OVERRIDDEN* entry points to the custom configuration file.

In the overridden case, we’re provided with the diff-like details of the change:

```bash
$ systemd-delta
# ...
--- /usr/lib/systemd/system/sshd.service        2022-01-20 23:51:44.000000000 +0100
+++ /etc/systemd/system/sshd.service    2023-08-01 16:37:59.049605208 +0200
@@ -11,7 +11,7 @@
 ExecReload=/bin/kill -HUP $MAINPID
 KillMode=process
 Restart=on-failure
-RestartSec=42s
+RestartSec=50s
```

### 4.1. More on the Modification Type

**The *systemd-delta* command recognizes more ways of service modification.** We can use a type name to query services with the *–type* option. So, let’s examine some of them. First, to find all unchanged services, we should use the type *unchanged*:

```bash
$ systemd-delta --type unchanged
[UNCHANGED]  /etc/sysctl.d/99-sysctl.conf
[UNCHANGED]  /usr/lib/sysctl.d/10-default-yama-scope.conf
# ...
```

Next, let’s list all overridden configurations. Additionally, we’re going to suppress the diff output with the *–diff=false* option:

<div class="code-block code-block-6" id="bkmrk--6" style="margin: 8px 0; clear: both;">  
</div>```bash
$ systemd-delta --type overridden --diff=false
[OVERRIDDEN] /etc/systemd/system/instsvcdrv.service → /usr/lib/systemd/system/instsvcdrv.service
[OVERRIDDEN] /etc/systemd/system/sshd.service → /usr/lib/systemd/system/sshd.service

2 overridden configuration files found.
```

The services that are modified by the overriding drop-in file can be found in the type *extended*:

```bash
$ systemd-delta --type extended
[EXTENDED]   /usr/lib/systemd/system/systemd-hostnamed.service → /usr/lib/systemd/system/systemd-hostnamed.service.d/disable-privatedevices.conf
[EXTENDED]   /usr/lib/systemd/system/systemd-logind.service → /usr/lib/systemd/system/systemd-logind.service.d/10-grub2-logind-service.conf
# ...

9 overridden configuration files found.
```

The other types are: *masked*, *equivalent*, and *redirected*.

## 5. Restoring the Original Configuration

**Deleting the custom configuration is pretty simple — all we need is to remove the overriding files and reload services.** So, in the drop-in case, we should remove the files from the */etc/systemd/system/&lt;service\_name&gt;.service.d* directory. When the configuration is replaced, we should remove the *&lt;service\_name&gt;.service* from the */etc/systemd/system* directory.

<div class="bd-anchor" id="bkmrk--7"></div>Finally, let’s issue the commands to reload the config and restart the service:

```bash
$ sudo systemctl daemon-reload
$ sudo systemctl restart <service_name>.service
```

## 6. Working With Templates

The [service template](https://www.baeldung.com/linux/systemd-multiple-parameters) is a service unit file that can take a parameter. In this way, we can create multiple, different instances of the same service. The name of the service instance looks like:

```bash
<service_name>@<argument>.service
```

Let’s take a look at [*getty*](https://www.baeldung.com/linux/pty-vs-tty), a well-known template service. Its task is to bring up a text pseudo-terminal TTY. The terminal identifier is the template argument. So, let’s open a pseudo-terminal with *Alt+Ctrl+F3*. Afterward, let’s check the active services to find a *getty* instance:

```bash
$ systemctl --type=service --state=active | grep getty
  getty@tty3.service                                    loaded active running Getty on tty3
```

Next, we’re going to find the corresponding unit file:

```bash
$ sudo systemctl list-unit-files --state=enabled | grep getty
getty@.service                     enabled enabled
```

Note the at sign *‘@’* indicating a template unit file of this service.

<div class="code-block code-block-7" id="bkmrk--8" style="margin: 8px 0; clear: both;">  
</div>**We can modify both the template and instances of the service.** When creating a drop-in files directory or a unit file for the template, we should follow the service name with a sole *@*. For service instances, *@* is followed additionally by the template parameter.

As an example, to edit the *getty* template, let’s issue:

```bash
$ sudo systemctl edit getty@
```

In the case of the *getty* instance, we should use:

```bash
$ sudo systemctl edit getty@tty5
```

### 6.1. Working Example

**As an example, let’s change the way the user logs in to the pseudo-terminal.** We’re going to get an automatic login for user *joe* in *tty5*. Thus, we should change the corresponding instance *getty@tty5* of the service. Let’s do that with *systemctl edit*:

```bash
$ sudo systemctl edit getty@tty5
```

In the editor, let’s find the commented-out *ExecStart* entry with the calling of the [*agetty*](https://man7.org/linux/man-pages/man8/agetty.8.html) command:

```bash
# ExecStart=-/sbin/agetty -o '-p -- \\u' --noclear - $TERM
```

By adding the *-a joe* option, we allow automatic login for *joe.* Now, the new *Service* section looks like:

```bash
[Service]
ExecStart=
ExecStart=-/sbin/agetty -a joe -o '-p -- \\u' --noclear - $TERM
```

**The important part is to reset *ExecStart* before setting it to a new value**. We should do that because multiple definitions of this parameter are not allowed — unless it’s a *oneshot* service.

Now, let’s save the modifications and change the default file name *override.conf* to something more meaningful, like *joe\_auto\_login.conf*. At last, let’s reload daemons and press *Alt+Ctrl+F5*. We’ll jump into the pseudo-terminal with the login name set to *joe*.

<div class="code-block code-block-8" id="bkmrk--9" style="margin: 8px 0; clear: both;">  
</div>Finally, let’s check the service status:

```bash
$ systemctl status getty@tty5
● getty@tty5.service - Getty on tty5
     Loaded: loaded (/usr/lib/systemd/system/getty@.service; disabled; vendor preset: enabled)
    Drop-In: /etc/systemd/system/getty@tty5.service.d
             └─joe_auto_login.conf
# ...
```

## 7. Conclusion

In this article, we studied two ways to modify the *systemd* services configuration, without touching the upstream unit file. First, we discovered the difference between extending and overriding the configuration. Then, we got into the details of both approaches.

<div class="flex-col clearfix" id="bkmrk-we-learned-about-the" role="main"><article class="clearfix post-63730 post type-post status-publish format-standard has-post-thumbnail hentry category-administration tag-systemctl tag-systemd" id="bkmrk-we-learned-about-the-1" role="article"><section class="post-content clearfix"><div class="bd-anchor" id="bkmrk--10"></div>We learned about the structure and naming convention of files that extend the configuration. Then, we searched for the altered services according to the way of their modification.

Finally, we took a look at the template services. As an example, we allowed automatic login to a selected pseudo-terminal by adjusting the *getty* instance.

</section></article></div><div class="sidebar flex-col" id="bkmrk--11" role="complementary"><div class="sticky-widgets" style="visibility: visible; height: 100%;"><div class="baeldung-widgets widget widget_text" data-height-limit="0" data-sticky-weight="1" data-stickyness="sticky" id="bkmrk--12" style="margin-top: 0em; margin-bottom: 0em; height: 4365px;"><div class="textwidget" style="position: sticky; top: 105px;"><div align="center" id="bkmrk--13"></div></div></div><div class="baeldung-widgets widget widget_text" data-height-limit="0" data-sticky-weight="1" data-stickyness="sticky" id="bkmrk--14" style="margin-top: 0em; margin-bottom: 0em; height: 4365px;"><div class="textwidget" style="position: sticky; top: 105px;"><div align="center" id="bkmrk--15"></div></div></div></div></div>

# Systemd service

В этой статье мы подробнее посмотрим на юниты **SystemD** с типом **Service**. Разберём параметры из юнита **ssh.service** и не только.

## <span id="bkmrk-%D0%AE%D0%BD%D0%B8%D1%82%D1%8B-systemd-%D1%82%D0%B8%D0%BF%D0%B0-s-1">Юниты SystemD типа service</span>

В **SystemD** все сервисы которые можно запускать или останавливать описываются в специальных юнитах **service**. Это значит, что в таких юнитах описывается вся информация, которая поможет запустить сервис (службу).

Наверно юниты SystemD следовало изучать после изучения процессов. И скорее всего в будущем я поменяю очерёдность в оглавлении. Но сейчас я постарался описать службы (юниты типа service) с минимальным объяснением процессов.

Как вам известно, юниты — это обычные файлы. В юнитах типа **service** описывается способ запуска исполняемых файлов (бинарных или скриптов).

В **SystemD** есть специальная утилита, которая позволяет управлять службами — **systemctl**. Вот некоторые её подкоманды:

- **systemctl start &lt;unit.service&gt;** — запуск службы. При этом происходит какая-то подготовка и запуск одного или нескольких исполняемых файлов. А когда запускаются исполняемые файлы, то в системе стартуют соответствующие процессы. Из этого следует, что после запуска службы в системе запустится один или несколько процессов, которые будут работать в одной службе.
- **systemctl stop &lt;unit.service&gt;** — остановка службы. Эта команда должна остановить службу. При этом должны остановиться все связанные со службой процессы. Хотя процессы не обязательно должны завершиться, это зависит от того, что написано в юните.
- **systemctl status &lt;unit.service&gt;** — статус службы. С помощью этой команды можно узнать статус службы. А именно запущена ли она или нет, какие в неё работают процессы и какой из них главный процесс. А также можно посмотреть последние логи службы.
- **systemctl reload &lt;unit.service&gt;** — перечитывание конфигурации. Эта команда заставит работающие процессы перечитать свои конфиги. Хотя на самом деле, она выполнит то, что написано в юните.
- **systemctl restart &lt;unit.service&gt;** — перезапуск службы. То есть остановка и последующее включение службы.
- **systemctl enable &lt;unit.service&gt;** — включение автозапуска. Эта команда включает автозапуск для службы, а вот когда будет срабатывать автозапуск, зависит от написанного в юните.
- **systemctl disable &lt;unit.service&gt;** — выключение автозапуска. А эта команда выключает автозапуск у службы.
- **systemctl cat &lt;unit.service&gt;** — вывод содержимого файла юнита. То есть делает тоже самое, что и команда **cat /путь/unit.service**.

Мне кажется лучше всего изучать написание юнитов с помощью просмотра уже подготовленных юнитов. Давайте посмотрим на один такой юнит — **ssh.service**.

## <span id="bkmrk-%D0%AE%D0%BD%D0%B8%D1%82-systemd-%E2%80%94-ssh.s-1">Юнит SystemD — ssh.service</span>

### <span id="bkmrk-%D0%A1%D1%82%D0%B0%D1%82%D1%83%D1%81-%D1%81%D0%BB%D1%83%D0%B6%D0%B1%D1%8B-1">Статус службы</span>

Для начала с помощью команды **systemctl status ssh** посмотрим статус этой службы:

```bash
alex@deb:~$ sudo systemctl status ssh
● ssh.service - OpenBSD Secure Shell server
Loaded: loaded (/lib/systemd/system/ssh.service; enabled; vendor preset: enabled)
Active: active (running) since Thu 2022-07-21 13:59:59 MSK; 5min ago
Docs: man:sshd(8)
man:sshd_config(5)
Process: 10614 ExecStartPre=/usr/sbin/sshd -t (code=exited, status=0/SUCCESS)
Main PID: 10615 (sshd)
Tasks: 1 (limit: 2340)
Memory: 1.1M
CPU: 76ms
CGroup: /system.slice/ssh.service
└─10615 sshd: /usr/sbin/sshd -D [listener] 0 of 10-100 startups
```

<div class="enlighter-default enlighter-v-standard enlighter-t-atomic enlighter-l-generic enlighter-hover " id="bkmrk-"><div class="enlighter-code"><div class="enlighter"><div class=""><div><span class="enlighter-text">  
</span></div></div></div></div></div>В этом выводе можно увидеть файл юнита — **/lib/systemd/system/ssh.service**. Также видно что включена автозагрузка службы — **enabled**. И фраза **vendor preset: enabled** — означает что служба поставляется со включенной автозагрузкой. Другими словами, при установке пакета ssh, сразу включится автозапуск этой службы.

### <span id="bkmrk-%D0%A1%D0%BE%D0%B4%D0%B5%D1%80%D0%B6%D0%B8%D0%BC%D0%BE%D0%B5-%D1%8E%D0%BD%D0%B8%D1%82%D0%B0-1">Содержимое юнита</span>

Давайте теперь посмотрим на содержимое юнита. Это можно сделать с помощью двух разных команд:

```bash
alex@s-deb:~$ cat /lib/systemd/system/ssh.service
*** Этот вывод я пропущу, так как он аналогичен выводу следующей команды ***


alex@s-deb:~$ systemctl cat ssh.service
# /lib/systemd/system/ssh.service
[Unit]
Description=OpenBSD Secure Shell server
Documentation=man:sshd(8) man:sshd_config(5)
After=network.target auditd.service
ConditionPathExists=!/etc/ssh/sshd_not_to_be_run


[Service]
EnvironmentFile=-/etc/default/ssh
ExecStartPre=/usr/sbin/sshd -t
ExecStart=/usr/sbin/sshd -D $SSHD_OPTS
ExecReload=/usr/sbin/sshd -t
ExecReload=/bin/kill -HUP $MAINPID
KillMode=process
Restart=on-failure
RestartPreventExitStatus=255
Type=notify
RuntimeDirectory=sshd
RuntimeDirectoryMode=0755


[Install]
WantedBy=multi-user.target
Alias=sshd.service
```

Здесь мы видим 3 блока:

- **\[Unit\]** — здесь можно описать юнит, и указать ограничения на его запуск;
- **\[Service\]** — сюда добавляется информация, которая используется для запуска, работы и остановки службы;
- **\[Install\]** — в этом блоке описываются дополнительные условия для запуска или автозапуска службы.

#### <span id="bkmrk-%D0%91%D0%BB%D0%BE%D0%BA-%5Bunit%5D-1">Блок \[Unit\]</span>

Параметры:

- **Description** — описание юнита;
- **Documentation** — источники документации;
- **After** — запускать юнит после запуска перечисленных юнитов, при этом не требуется чтобы эти юниты были запущены, этот параметр влияет только на очерёдность;
- **Requires** — в нашем примере нет этой опции, но про неё следует знать. Эта опция требует чтобы перечисленные юниты работали. Если они не будут работать, то и наш юнит не запустится;
- **ConditionPathExists** — эта опция проверяет наличия указанного файла. Восклицательный знак перед именем файла означает, что юнит запустится только в том случае, если этого файла в системе нет. Получается чтобы запретить запуск службы **ssh** достаточно создать файл **/etc/ssh/sshd\_not\_to\_be\_run**.

#### <span id="bkmrk-%D0%91%D0%BB%D0%BE%D0%BA-%5Bservice%5D-1">Блок \[Service\]</span>

Параметры:

- **EnvironmentFile** — переменные окружения для службы и её процессов будут браться из указанного файла. Минус перед файлом (как в нашем случае **-/etc/default/ssh**) означает что файл может отсутствовать и это не приведёт к ошибке при запуске службы. Это можно использовать как файл с дополнительными настройками.
- **ExecStartPre** — здесь указывается дополнительная команда, которая выполняется перед запуском службы. Это можно использовать, например для подготовки окружения перед запуском.
- **ExecStart** — команда, которая запускает службу. Именно в этом параметре нужно указать главный исполняемый файл (утилиту или скрипт), ради которого мы создаём службу.
- **ExecReload** — здесь нужно указать которая выполнится при выполнении **systemctl reload &lt;unit.service&gt;**. А если утилита не умеет перечитывать свою конфигурацию, без перезагрузки, то можно опустить этот параметр. Параметров **ExecStartPre**, **ExecStart**, **ExecReload** может быть несколько, если нужно выполнить несколько команд.
- **KillMode** — указывает, как процессы службы должны завершаться. Существует несколько вариантов: 
    - **control-group** — сигнал остановки будет послан всем процессам службы;
    - **mixed** — первый сигнал остановки будет послан главному процессу, а второй всем остальным процессам;
    - **process** — сигнал остановки будет послан только главному процессу, все остальные процессы (если они есть) останутся работать;
    - **none** — никому не будет отправлен сигнал остановки, служба будет выключена, но все процессы продолжат работать (настоятельно не рекомендуется);
- **Restart** — в этом параметре можно указать, в каких случаях нужно перезагружать службу: 
    - **on-failure** — при ошибке;
    - **on-success** — при успехе (при корректном завершении службы);
    - **no** — никогда (по умолчанию);
    - **always** — всегда, то есть если ваша служба по каким-то причинам останавливается корректно, то этот параметр её перезапустит. Но это не сработает, если вы выполните (**systemctl stop &lt;unit.service&gt;**).
- **RestartPreventExitStatus** — если служба вылетит с указанным кодом завершения (в нашем случае 255), то она больше не перезапускается, даже если установлен **Restart=on-failure**. Про коды завершения я напишу позже.
- **Type** — тип юнита — очень важный параметр. Отвечает за способ запуска службы и её процессов. Бывают следующие типы: 
    - **simple** или **exec** — они используются для запуска программы как службы. Этот тип стоит использовать, когда запускаемая программа не умеет сама запускаться в фоновом режиме. То есть запускается на переднем плане. Прервать выполнение такой программы можно с помощью комбинации клавиш **Ctrl+C**. При этом **simple** отрабатывает быстрее, но не следит за запуском исполняемого файла, указанного в **ExecStart**. А **exec** дожидается запуска исполнимого файла, и только после этого служба считается запущенной. Желательно использовать **exec** вместо **simple**, так как в **simple** служба может оказаться успешно запущенной, даже если исполнимый файл не смог запустится;
    
    
    - **forking** — этот тип следует использовать если запускаемое приложение умеет само запускаться в фоновом режиме. Можно использовать этот тип, если вы запускаете с помощью скрипта несколько процессов, которые умеют запускаться в фоне. То есть в **ExecStart** указан скрипт, а в скрипте запускаются фоновые процессы. В этом случае сам скрипт выполнится и завершится, а служба будет управлять запущенными фоновыми процессами;
    
    
    - **oneshot** — если подразумевается разовый запуск утилиты или скрипта, то подойдет этот тип. Служба такого типа никогда не будет запущенной. Вы запускаете службу, скрипт выполняется и служба останавливается;
    
    
    - **dbus** — если служба использует соединение D-Bus, то можно использовать этот тип службы. Когда служба запустится, то получит имя на шине D-Bus. Если это имя освободится, то служба будет считаться неисправной и все процессы службы начнут завершатся. Про D-Bus также будет написана отдельная статья;
    
    
    - **notify** — поведение похоже на **exec**, но дополнительно ожидается, что служба отправит сигнал готовности после своего запуска;
    - **idle** — система отложит выполнение двоичного файла службы до окончания запуска остальных (более срочных) задач, максимум на 5 секунд. В остальном поведение аналогично **simple**.
- **RuntimeDirectory** — после запуска службы будет создана указанная директория. В нашем случае — **/run/sshd/**;
- **RuntimeDirectoryMode** — директория будет создана с указанными правами. В нашем случае — **755 (rwx r-x r-x)**. Про систему прав в Linux я уже писал [здесь](https://sysadminium.ru/standard_linux_file_permissions/).

#### <span id="bkmrk-%D0%91%D0%BB%D0%BE%D0%BA-%5Binstall%5D-1">Блок \[Install\]</span>

Параметры:

- **WantedBy** — если мы включим автозагрузку этой службы (с помощью команды **systemctl enable &lt;имя службы&gt;**), то она должна запуститься при загрузке мультипользовательского режима (**multi-user.target**). Про таргеты и загрузочные таргеты будет следующая статья.
- **Alias** — псевдоним службы. Указание псевдонима приведёт к тому, что к службе можно будет обратиться ещё и по имени псевдонима. Например, вместо **ssh.service** можно использовать **sshd.service** (**systemctl status sshd.service**).

## <span id="bkmrk-%D0%94%D0%BE%D0%BF%D0%BE%D0%BB%D0%BD%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D1%8B%D0%B5-%D0%BE%D0%BF%D1%86%D0%B8%D0%B8-1">Дополнительные опции</span>

В юните **SystemD** — **ssh.service** использовались далеко не все из возможных опций. Вот некоторые опции, которые можно использовать для создания своих служб (в блоке **\[Service\]**):

- **User** — с помощью этого параметра можно указать пользователя (username или uid), от чьего имени будут запущены процессы службы. То есть этот пользователь будет запускать исполняемый файл, указанный в **ExecStart**.
- **ExecStop** — команда, которую нужно выполнить при остановке службы (systemctl stop &lt;unit.service&gt;).
- **KillSignal** — здесь можно указать сигнал остановки процессов. А если этот параметр не указать, то будет использован сигнал **SIGTERM**. Про сигналы, которые можно посылать процессам, я напишу позже.
- **LimitNOFILE** — указывается максимальное число открытых файлов, которые смогут открыть процессы службы.
- **LimitCPU** — можем задать максимальное количество процессорного времени в секундах. Когда лимит будет израсходован, служба завершится с ошибкой.
- **LimitRSS** — лимит потребления памяти в байтах. При достижении лимита служба завершится с ошибкой.
- **LimitNPROC** — максимальное число процессов в службе.
- **Nice** — уровень любезности запускаемых процессов службой. Любезность (**Nice**) — это параметр обратный приоритету. Чем выше Nice, тем ниже приоритет у процесса, и тем меньше он нагружает процессор. Может быть от -20 (самый приоритетный) до 19 (наименьший приоритет).

## <span id="bkmrk-%D0%98%D1%82%D0%BE%D0%B3-1">Итог</span>

Это не все опции, на самом деле их очень много. Для тех кто хочет во всём этом разобраться дам несколько ссылок на официальную документацию **SystemD** (на английском):

- [Настройка юнитов](https://www.freedesktop.org/software/systemd/man/systemd.unit.html) — описывают сами юниты, их типы, их расположение и приоритет. А также здесь описываются параметры, которые можно использовать в блоке **\[Unit\]** и **\[Install\]**.
- [Управление выполнением юнитов (не только служб)](https://www.freedesktop.org/software/systemd/man/systemd.exec.html) — описывают параметры, с помощью которых мы можем контролировать запускаемые процессы. Эти параметры принадлежат блоку **\[Service\]**. Это лимиты, пользователи, параметры безопасности, переменные окружения и тому подобное.

# Мониторинг

# Инструменты мониторинга

Обзор практически всех \*top утилит под linux (atop, iotop, htop, foobartop и т.д.).

# top

Все мы знаем top — самую простую и самую распространённую утилиту из этого списка. Показывает примерно то же, что утилита vmstat, плюс рейтинг процессов по потреблению памяти или процессора. Совсем ничего не знает про загрузку сети или дисков. Позволяет минимальный набор операций с процессом: renice, kill (в смысле отправки сигнала, убийство — частный случай). По имени top суффикс "-top" получили и все остальные подобные утилиты в этом обзоре.

# atop

![](https://habrastorage.org/r/w1560/getpro/habr/post_images/c67/7ba/cf9/c677bacf9500d1df4c1890a41c00e784.png)  
Atop имеет два режима работы — сбор статистики и наблюдение за системой в реальном времени. В режиме сбора статистики atop запускается как демон и раз в N времени (обычно 10 мин) скидывает состояние в двоичный журнал. Потом по этому журналу atop'ом же (ключ -r и имя лог-файла) можно бегать вперёд-назад кнопками T и t, наблюдая показания atop'а с усреднением за 10 минут в любой интересный момент времени.  
  
В отличие от top отлично знает про существование блочных устройств и сетевых интерфейса, способен показывать их загрузку в процентах (на 10G, правда, процентов не получается, но хотя бы показывается количество мегабит).  
  
Незаменимое средство для поиска источников лагов на сервере, так как сохраняет не только статистику загрузки системы, но и показатели каждого процесса — то есть «долистав» до нужного момента времени можно увидеть, кто этот счастливый момент с LA &gt; 30 создал. И что именно было причиной — IO программ, своп (нехватка памяти), процесор или что-то ещё. Помимо большего количества информации ещё способен двумя цветами подсказывать, какие параметры выходят за разумные пределы.  
<a name="habracut"></a>

# htop

![](https://habrastorage.org/r/w1560/getpro/habr/post_images/e0c/ac6/116/e0cac6116d7ba8cec2497da217f60cb1.png)  
В отличие от atop, htop не собирает статистику и просто показывает текущее состояние. Второе яркое отличие — нортоноподобная панелька с подсказками кнопок снизу и возможность «навигации» по списку процессов.  
  
Поддерживает выделение процессов и выполнения над ними групповых операций (впрочем, с форк бомбой им не справиться).   
  
Общей статистике по системе показыват мало (зато с шкалой а-ля прогресс-бар), зато имеет обширнейшие инструменты для анализа процессов, включая баловство с скедулингом (приоритеты, affinity), просмотр списка открытых файлов, strace и массу мелких, но приятных фич, таких как контекстный поиск по имени процесса, режим «слежения» за процессом, быстрые операции с процессом и т.д.

# iotop

![](https://habrastorage.org/r/w1560/getpro/habr/post_images/cf0/58b/897/cf058b8972f998d7ed7a92c61c93de32.png)  
Специализированная утилита для анализа потребления дисковой полосы. К сожалению, не показывает иопсы (это вообще возможно?) Благодаря показу kernel threads способен указать на kcopy/swapper/kflush как источник хруста винта (чего не может atop). Заодно показывает общую загруженность IO системы в Мб/с (чего так же не может atop). Довольно быстрый.

# iftop

![](https://habrastorage.org/r/w1560/getpro/habr/post_images/aff/6e2/774/aff6e2774a78c55b38e01e5e1d8d3f4a.png)  
Опять же, специализированная утилита, позволяющая наблюдать за трафиком в реальном времени. Требует очень рутовых прав и pcap, т.к. работает почти аналогичо tcpdump'у. Показывает загруженность интерфейса (поддерживается только один интерфейс в одной копии), направления трафика и интенсивность трафика.  
  
На средне-загруженном сервере, который внезапно начал жрать инет позволяет очень быстро найти направление, в которое идёт больше всего трафика (в любую сторону). К сожалению, не показывает распределение трафика по процессам.

# powertop

![](https://habrastorage.org/r/w1560/getpro/habr/post_images/210/659/a98/210659a98f4a19f8ce77d7aece816300.png)  
Специализированная (ага, снова) утилита от intel для мониторинга потребления мощности (электрической мощности!) разными процессами. В реальности никаких ваттов не показывает, а показывает время, затраченное на обслуживание процессором. Уникальна тем, что показывает прерывания и прочие события ядра на одном уровне с процессами (что позволяет находить нетривиальные ситуации перегрузки сервера и даже определять, от какой железки это исходит). При равной нагрузке позволяет оценить, чей драйвер шустрее работает.  
  
Кроме того, во вкладке device stats позволяет оценить степень загруженности устройства (точнее, драйвера устройства). Для сетевых карт (включая виртуальные, типа tun) показывает pps (packets per seconds).   
  
Во вкладке idle status показывает распределение состояний процессора (C1, C2, C3), что весьма полезно при выяснении «батарейка дохлая или что-то не так с системой?» на ноутбуках.

# itop

![](https://habrastorage.org/r/w1560/getpro/habr/post_images/6ac/597/80a/6ac59780a133ff445ffe50644eaadb34.png)  
Ну очень специализированная утилита для мониторинга прерываний (настоящих прерываний, идентификацией их по номеру).

# kerneltop

Наверное, должен показывать что-то интересное про ядро. У меня дома ему не понравился System.map, а на сервере — отсутствие /proc/profile.

# dnstop

![](https://habrastorage.org/r/w1560/getpro/habr/post_images/318/2e1/72b/3182e172bab3ca6af1a51deeb7134bbc.png)  
Специализированный анализатор DNS-трафика на интерфейсе. Наверное, был бы очень полезен при починке DNS на контроллере домена, к сожалению, Active Directory на линуксе работает не очень хорошо.  
  
Безусловно полезно для нахождения засранца, загоняющего бинд в неприличный LA.

# jnettop

Почти клон iftop, однако имеет забавный режим, когда может слушать соседей и показывать top по их трафику. Не очень работает в эпоху коммутаторов.  
  
Ещё умеет агрегацию адресов (так, чтобы показывать их трафик одной строкой).

# sntop

Замечательная штука для скринсейвера или публичного монитора — по конфигу рассылает пинги и показывает, если какой-то хост лёг. В принципе, в рабочих условиях при настроенном конфиге позволяет быстро оценить состояние не очень большого парка серверов (запустили — и сразу красным видно, кто лежит).

# latencytop

Я бы с интересом на него посмотрел, но оно требует особого конфига ядра…

# xrestop

![](https://habrastorage.org/r/w1560/getpro/habr/post_images/f28/36e/73f/f2836e73fe00cdf7a0d0b9783b5fadcb.png)  
Монитор потребления ресурсов X-сервера разными приложениями, которые к нему подключились. (Обнаружил, что хром жрёт ресурсов х-сервера много больше, чем опера).

# slabtop

![](https://habrastorage.org/r/w1560/getpro/habr/post_images/557/db0/c9f/557db0c9f8659ee630325c25e31e4342.png)  
Специализируется на структурах данных [SLAB](http://en.wikipedia.org/wiki/Slab_allocation) ядра, фактически, показывает использование памяти в более тонких категориях, чем «свободно/занято/кеш». View only, несколько режимов сортировки.

# Software specific

## apachetop

Не столь полезен, как хотелось бы, показывает статистику ответов на запросы. Никакой магии, просто читает логи апача.

## sqtop

Топ по логам сквида. С учётом скорости их роста, сначала генерирует промежуточную статистику, а потом уже даёт по ней шариться.

## pg\_top

Пакет в debian почему-то называется ptop. Мониторит загрузку postgresql.

## mytop

  
![](https://habrastorage.org/r/w1560/getpro/habr/post_images/c91/a83/c58/c91a83c586d562f8fb48606fc244a2db.png)  
Мониторит mysql. Есть альтернативный mtop, делает примерно то же, но заброшен и (в дебиане) выпилен в районе lenny -&gt; squeeze.

## xentop

![](https://habrastorage.org/r/w1560/getpro/habr/post_images/7f0/fde/812/7f0fde81204ebb1899b5955a0546af12.png)  
Мониториг доменов xen'а. Интересен тем, что показывает не только память-процессор, но и дисковые операции с сетью. К сожалению, никакого управления, view only. Единственный из всех top'ов, не влазящий в 80 столбцов при выводе.  
  
Список топов, которые я глазами не посмотрел:  
  
hatop — мониторинг haproxy  
virt-top — мониторинг за работой libvirt  
mctop — мониторинг состояния memcached  
perf-top — нашёл [документацию](http://rswiki.csie.org/lxr/http/source/tools/perf/Documentation/perf-top.txt?v=linux-2.6-pvops.git;a=sparc64), самого perf-top в виде тарбола или пакета не нашёл.

# offTOP

  
  
Сначала ложные топы кратко:  
gkrelmtop — плагин под gkrelm (монитор производительности в гуе) — оффтопик.  
ntop — нарушает text-based традицию интерактивных программ и ставится как сайт для апача.  
libgtop — библиотека для мониторигна производительности  
nload — консольный монитор сетевой активности, в отличие от обычных top'ов не выводит рейтинг, а рисует текстовый график.  
  
А потом настоящие топы без уважительного суффикса 'top' в названии.

## nethogs

![](https://habrastorage.org/r/w1560/getpro/habr/post_images/5f6/263/f63/5f6263f63191823c6ce2dec31a63d201.png)  
Отображает сетевой трафик от конкретных приложений.

## iptstate

![](https://habrastorage.org/r/w1560/getpro/habr/post_images/d51/5cd/647/d515cd6473758efcd62a1093a61b02f6.png)  
Монитор contrack из iptables, показывает активные трансляции с возможностью их прибить. Фактически, близок к цисковому `sh ip nat tra`, но удобнее.

# Сборка RPM пакетов

# Руководство по сборке RPM пакетов

## Вступление

Руководство по упаковке RPM пакетов:

<div id="bkmrk-%D0%9A%D0%B0%D0%BA-%D0%BF%D0%BE%D0%B4%D0%B3%D0%BE%D1%82%D0%BE%D0%B2%D0%B8%D1%82%D1%8C-%D0%B8%D1%81%D1%85%D0%BE"><div class="sect1"><div class="sectionbody"><div class="dlist"><dl><dt class="hdlist1">Как подготовить исходный код для упаковки в RPM.</dt><dd>Для тех, кто не имеет опыта разработки программного обеспечения. [Подготовка программного обеспечения для упаковки](https://rpm-packaging-guide-ru.github.io/#preparing-software-for-packaging).

</dd><dt class="hdlist1">Как упаковать исходный код в RPM.</dt><dd>Это для разработчиков программного обеспечения, которым необходимо упаковать программное обеспечение в RPMs. [Программное обеспечение для упаковки](https://rpm-packaging-guide-ru.github.io/#packaging-software).

</dd><dt class="hdlist1">Расширенные сценарии упаковки.</dt><dd>Это справочный материал для упаковщиков RPM, работающих с расширенными сценариями упаковки RPM. [Дополнительные материалы](https://rpm-packaging-guide-ru.github.io/#advanced-topics).

</dd></dl></div></div></div></div>### PDF Версия

Вы также можете скачать [Руководство по сборке RPM пакетов.pdf](https://docs.nix-adm.ru/attachments/3)

### Файловая структура

Перед тем, как приступить к сборке, нужно создать структуру каталогов, необходимую RPM, находящуюся в Вашем «домашнем» каталоге:

<div id="bkmrk-%D0%92%D1%8B%D1%85%D0%BE%D0%B4%D0%BD%D1%8B%D0%B5-%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D0%B5-%D0%BA%D0%BE%D0%BC%D0%B0"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="paragraph">  
</div><div class="ulist">- Выходные данные команд и содержимое текстовых файлов, включая исходный код, размещаются в блоках:
    
    ```
    $ tree ~/rpmbuild/
    /home/user/rpmbuild/
    |-- BUILD
    |-- BUILDROOT
    |-- RPMS
    |   |-- i586
    |   |-- x86_64
    |   `-- noarch
    |-- SOURCES
    |-- SPECS
    `-- SRPMS
    
    [command output trimmed]
    ```
    
    ```
    Name:           bello
    Version:
    Release:        1%{?dist}
    Summary:
    
    [file contents trimmed]
    ```
    
    ```
    #!/usr/bin/env python
    
    print("Hello World")
    ```
- Темы, представляющие интерес, или словарные термины упоминаются либо как ссылки на соответствующую документацию или веб-сайт выделены **жирным** шрифтом, либо *курсивом*. Первые упоминания некоторых терминов ссылаются на соответствующую документацию.
- Названия утилит, команд и других элементов, обычно встречающихся в коде, написаны `моноширинным` шрифтом.

</div></div><div class="sect2">  
</div></div></div></div>### Вклад в руководство

Вы можете внести свой вклад в это руководство, отправив сообщение о проблеме или запрос на извлечение на [ GitHub репозиторий](https://github.com/redhat-developer/rpm-packaging-guide).

Обе формы вклада высоко ценятся и приветствуются.

Не стесняйтесь подавать заявку на решение проблемы с обратной связью, отправлять запрос на извлечение [на GitHub](https://github.com/redhat-developer/rpm-packaging-guide) или и то, и другое!

## Необходимые пакеты

Чтобы следовать этому руководству, Вам понадобятся пакеты, упомянутые ниже.

<div id="bkmrk-%D0%9F%D1%80%D0%B8%D0%BC%D0%B5%D1%87%D0%B0%D0%BD%D0%B8%D0%B5-%D0%9D%D0%B5%D0%BA%D0%BE%D1%82%D0%BE%D1%80%D1%8B%D0%B5"><div class="sect1"><div class="sectionbody"><div class="paragraph">  
</div><div class="admonitionblock note"><table><tbody><tr><td class="icon"><div class="title">Примечание</div></td><td class="content">Некоторые из этих пакетов устанавливаются по умолчанию в [Fedora](https://getfedora.org/), [CentOS](https://www.centos.org/) и [RHEL](https://www.redhat.com/en/technologies/linux-platforms). Они подробно перечислины, чтобы показать, какие инструменты используются в этом руководстве.</td></tr></tbody></table>

</div><div class="paragraph">  
</div></div></div></div>В Fedora, CentOS 8, и RHEL 8:

<div id="bkmrk-"><div class="sect1"><div class="sectionbody"><div class="paragraph">  
</div><div class="listingblock"><div class="content">  
</div></div></div></div></div>```
$ dnf install gcc rpm-build rpm-devel rpmlint make python bash coreutils diffutils patch rpmdevtools
```

<div id="bkmrk--1"><div class="sect1"><div class="sectionbody"><div class="listingblock"><div class="content">  
</div></div><div class="paragraph">  
</div></div></div></div>В CentOS 7 и RHEL 7:

<div id="bkmrk--2"><div class="sect1"><div class="sectionbody"><div class="paragraph">  
</div><div class="listingblock"><div class="content">  
</div></div></div></div></div>```
$ yum install gcc rpm-build rpm-devel rpmlint make python bash coreutils diffutils patch rpmdevtools
```

<div id="bkmrk--3"><div class="sect1"><div class="sectionbody"><div class="listingblock"><div class="content">  
</div></div></div></div><div class="sect1">  
</div></div>## Зачем упаковывать программное обеспечение с помощью RPM?

Менеджер пакетов RPM (RPM) - это система управления пакетами, которая работает на Red Hat Enterprise Linux, CentOS и Fedora. RPM упрощает распространение, управление и обновление программного обеспечения, создаваемого для Red Hat Enterprise Linux, CentOS и Fedora. Многие поставщики программного обеспечения распространяют своё программное обеспечение через обычный архив (например, tarball). Однако, упаковка программного обеспечения в пакеты RPM имеет ряд преимуществ. Эти преимущества описаны ниже.

С помощью RPM Вы можете:

<div id="bkmrk-%D0%A3%D1%81%D1%82%D0%B0%D0%BD%D0%B0%D0%B2%D0%BB%D0%B8%D0%B2%D0%B0%D1%82%D1%8C%2C-%D0%BF%D0%B5%D1%80%D0%B5%D1%83"><div class="sect1"><div class="sectionbody"><div class="paragraph">  
</div><div class="dlist"><dl><dt class="hdlist1">Устанавливать, переустанавливать, удалять, обновлять и проверять пакеты</dt><dd>Пользователи могут использовать стандартные средства управления пакетами (например, Yum или PackageKit) для установки, переустановки, удаления, обновления и проверки ваших пакетов RPM.

</dd><dt class="hdlist1">Использовать базу данных установленных пакетов для запроса и проверки пакетов</dt><dd>Поскольку RPM поддерживает базу данных установленных пакетов и их файлов, пользователи могут легко запрашивать и проверять пакеты в своей системе.

</dd><dt class="hdlist1">Использовать метаданные для описания пакетов, инструкций по их установке и т. д.</dt><dd>Каждый пакет RPM содержит метаданные, описывающие компоненты пакета, версию, выпуск, размер, URL-адрес проекта, инструкции по установке и так далее

</dd><dt class="hdlist1">Упаковывать нетронутые исходные коды программного обеспечения в исходные и двоичные пакеты</dt><dd>RPM позволяет вам брать нетронутые исходные коды программного обеспечения и упаковывать их в исходные и двоичные пакеты для ваших пользователей. В исходных пакетах у Вас есть первозданные исходные тексты вместе с любыми использованными исправлениями, а также полные инструкции по сборке. Такая конструкция облегчает обслуживание пакетов по мере выпуска новых версий вашего программного обеспечения.

</dd><dt class="hdlist1">Добавлять пакеты в репозитории Yum</dt><dd>Вы можете добавить свой пакет в репозиторий Yum, который позволяет клиентам легко находить и развертывать ваше программное обеспечение.

</dd><dt class="hdlist1">Установить цифровую подпись Ваших упаковок</dt><dd>Используя ключ подписи GPG, Вы можете подписать посылку цифровой подписью, чтобы пользователи могли проверить подлинность упаковки.

</dd></dl></div></div></div><div class="sect1">  
</div></div>## Ваш Первый пакет RPM

Создание пакета RPM может быть сложным. Вот полный рабочий файл спецификации RPM, в котором несколько вещей пропущены и упрощены.

<div id="bkmrk--4"><div class="sect1"><div class="sectionbody"><div class="paragraph">  
</div><div class="listingblock"><div class="content">  
</div></div></div></div></div>```
Name:       hello-world
Version:    1
Release:    1
Summary:    Most simple RPM package
License:    FIXME

%description
This is my first RPM package, which does nothing.

%prep
# we have no source, so nothing here

%build
cat > hello-world.sh <<EOF
#!/usr/bin/bash
echo Hello world
EOF

%install
mkdir -p %{buildroot}/usr/bin/
install -m 755 hello-world.sh %{buildroot}/usr/bin/hello-world.sh

%files
/usr/bin/hello-world.sh

%changelog
# let's skip this for now
```

<div id="bkmrk--5"><div class="sect1"><div class="sectionbody"><div class="listingblock"><div class="content">  
</div></div><div class="paragraph">  
</div></div></div></div>Сохраните этот файл как `hello-world.spec`.

Теперь используйте эти команды:

<div id="bkmrk--6"><div class="sect1"><div class="sectionbody"><div class="paragraph">  
</div><div class="listingblock"><div class="content">  
</div></div></div></div></div>```
$ rpmdev-setuptree
$ rpmbuild -ba hello-world.spec
```

<div id="bkmrk--7"><div class="sect1"><div class="sectionbody"><div class="listingblock"><div class="content">  
</div></div><div class="paragraph">  
</div></div></div></div>Команда `rpmdev-setuptree` создает несколько рабочих каталогов. Поскольку эти каталоги постоянно хранятся в $HOME, эту команду не нужно использовать снова.

Команда `rpmbuild` создает фактический пакет rpm. Вывод этой команды может быть похож на:

<div id="bkmrk--8"><div class="sect1"><div class="sectionbody"><div class="paragraph">  
</div><div class="listingblock"><div class="content">  
</div></div></div></div></div>```
... [SNIP]
Wrote: /home/mirek/rpmbuild/SRPMS/hello-world-1-1.src.rpm
Wrote: /home/mirek/rpmbuild/RPMS/x86_64/hello-world-1-1.x86_64.rpm
Executing(%clean): /bin/sh -e /var/tmp/rpm-tmp.wgaJzv
+ umask 022
+ cd /home/mirek/rpmbuild/BUILD
+ /usr/bin/rm -rf /home/mirek/rpmbuild/BUILDROOT/hello-world-1-1.x86_64
+ exit 0
```

<div id="bkmrk--9"><div class="sect1"><div class="sectionbody"><div class="listingblock"><div class="content">  
</div></div><div class="paragraph">  
</div></div></div></div>Файл `/home/mirek/rpmbuild/RPMS/x86_64/hello-world-1-1.x86_64.rpm` является Вашим первым пакетом RPM. Его можно установить в систему и протестировать.

## Подготовка программного обеспечения для упаковки

Эта глава посвящена исходному коду и созданию программного обеспечения, которые являются необходимой основой для RPM-упаковщика.

### Что такое Исходный код?

**Исходный код**  - это понятные для человека инструкции к компьютеру, в которых описывается, как выполнить вычисления. Исходный код выражается с помощью [языка программирования](https://ru.wikipedia.org/wiki/%D0%AF%D0%B7%D1%8B%D0%BA_%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F) .

В этом руководстве представлены три версии программы `Hello World` , каждая из которых написана на разных языках программирования. Программы, написанные на этих трех разных языках, упаковываются по разному и охватывают три основных варианта использования RPM упаковщиком ПО.

<div id="bkmrk-%D0%9F%D1%80%D0%B8%D0%BC%D0%B5%D1%87%D0%B0%D0%BD%D0%B8%D0%B5-%D0%A1%D1%83%D1%89%D0%B5%D1%81%D1%82%D0%B2%D1%83%D1%8E"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="paragraph">  
</div><div class="admonitionblock note"><table><tbody><tr><td class="icon"><div class="title">Примечание</div></td><td class="content">Существуют тысячи языков программирования. В этом документе представлены только три из них, но их достаточно для концептуального обзора.</td></tr></tbody></table>

</div><div class="paragraph">  
</div></div></div></div></div>Программа `Hello World`, написанная на [bash](https://www.gnu.org/software/bash/):

`bello`

<div id="bkmrk--10"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="paragraph">  
</div><div class="listingblock"><div class="content">  
</div></div></div></div></div></div>```
#!/bin/bash

printf "Hello World\n"
```

<div id="bkmrk--11"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="listingblock"><div class="content">  
</div></div><div class="paragraph">  
</div></div></div></div></div>`Hello World`, написанная на [Python](https://www.python.org/):

`pello.py`

<div id="bkmrk--12"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="paragraph">  
</div><div class="listingblock"><div class="content">  
</div></div></div></div></div></div>```
#!/usr/bin/env python

print("Hello World")
```

<div id="bkmrk--13"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="listingblock"><div class="content">  
</div></div><div class="paragraph">  
</div></div></div></div></div>`Hello World`, написанная на [C](https://en.wikipedia.org/wiki/C_%28programming_language%29) :

`cello.c`

<div id="bkmrk--14"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="paragraph">  
</div><div class="listingblock"><div class="content">  
</div></div></div></div></div></div>```
#include <stdio.h>

int main(void) {
    printf("Hello World\n");
    return 0;
}
```

<div id="bkmrk--15"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="listingblock"><div class="content">  
</div></div><div class="paragraph">  
</div></div></div></div></div>Целью каждой из трех программ является вывод строки `Hello World` в командной строке.

<div id="bkmrk-%D0%9F%D1%80%D0%B8%D0%BC%D0%B5%D1%87%D0%B0%D0%BD%D0%B8%D0%B5-%D0%97%D0%BD%D0%B0%D0%BD%D0%B8%D0%B5-%D1%82%D0%BE"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="paragraph">  
</div><div class="admonitionblock note"><table><tbody><tr><td class="icon"><div class="title">Примечание</div></td><td class="content">Знание того, как программировать, не обязательно для упаковщика программного обеспечения, но полезно.</td></tr></tbody></table>

</div></div><div class="sect2">  
</div></div></div></div>### Как создаются программы

Существует множество методов, с помощью которых читаемый человеком исходный код становится машинным кодом - инструкциями, которым компьютер следует для фактического выполнения программы. Однако все методы можно свести к этим трем:

<div id="bkmrk-%D0%9F%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B0-%D0%B8%D0%B7%D0%BD%D0%B0%D1%87%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="paragraph">  
</div><div class="olist arabic">1. Программа изначально скомпилирована.
2. Программа интерпретируется с помощью необработанной интерпретации.
3. Программа интерпретируется путем байтовой компиляции.

</div><div class="sect3">  
</div></div></div></div></div>#### Изначально скомпилированный код

**Изначально скомпилированное** программное обеспечение - это программное обеспечение, написанное на языке программирования, которое компилируется в машинный код с результирующим двоичным исполняемым файлом. Такое программное обеспечение можно запускать автономно.

Пакеты RPM, созданные таким образом, зависят от [архитектуры](https://ru.wikipedia.org/wiki/%D0%9C%D0%B8%D0%BA%D1%80%D0%BE%D0%B0%D1%80%D1%85%D0%B8%D1%82%D0%B5%D0%BA%D1%82%D1%83%D1%80%D0%B0) Это означает, что если вы скомпилируете такое программное обеспечение на компьютере, использующем 64-разрядный (x86\_64) процессор AMD или Intel, оно не будет выполняться на 32-разрядном (x86) процессоре AMD или Intel. В названии результирующего пакета будет указана архитектура.

#### Интерпретируемый код

Некоторые языки программирования, такие как [bash](https://www.gnu.org/software/bash/) или [Python](https://www.python.org/), не компилируются в машинный код. Вместо этого исходный код их программ выполняется шаг за шагом, без предварительных преобразований, [языковым интерпретатором](https://en.wikipedia.org/wiki/Interpreter_%28computing%29) или языковой виртуальной машиной.

Программное обеспечение, написанное полностью на интерпретируемых языках программирования, не зависит от [архитектуры](https://en.wikipedia.org/wiki/Microarchitecture) . Следовательно, результирующий пакет RPM будет иметь строку `noarch` в своем названии.

Существует два типа интерпретируемых языков: языки, код которых **исполняется напрямую** и языки, код которых предварительно компилируется в **байт-код**. Процесс сборки программ для этих двух типов отличается.

##### Программы с прямой интерпретацией

Программы на языке с прямой интерпретацией вообще не нужно компилировать, они выполняются непосредственно интерпретатором.

##### Программы, скомпилированные в байт-код

Программы, скомпилированные в байт-код, которые затем исполняются вируальной машиной соответствующего языка.

<div id="bkmrk-%D0%9F%D1%80%D0%B8%D0%BC%D0%B5%D1%87%D0%B0%D0%BD%D0%B8%D0%B5-%D0%9D%D0%B5%D0%BA%D0%BE%D1%82%D0%BE%D1%80%D1%8B%D0%B5-1"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="paragraph">  
</div><div class="admonitionblock note"><table><tbody><tr><td class="icon"><div class="title">Примечание</div></td><td class="content">Некоторые языки предоставляют выбор: они могут быть напрямую интерпритированы или скомпилированы в байт-код.</td></tr></tbody></table>

</div></div></div></div><div class="sect2">  
</div></div></div></div>### Сборка программного обеспечения из исходного кода

В этом разделе объясняется сборка программного обеспечения на основе его исходного кода.

<div id="bkmrk-%D0%94%D0%BB%D1%8F-%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%BD%D0%BE%D0%B3%D0%BE-%D0%BE%D0%B1%D0%B5"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="paragraph">  
</div><div class="ulist">- Для программного обеспечения, написанного на компилируемых языках, исходный код проходит процесс **сборки**, создавая машинный код. Этот процесс, обычно называемый **компиляцией** или **переводом**, различается для разных языков. Полученное в результате сборки программное обеспечение может быть **запущено** или "**выполнено**", что заставит компьютер выполнять задачу, поставленную программистом.
- Для программного обеспечения, написанного на прямо интерпретируемых языках, исходный код не компилируется, а выполняется напрямую.
- Для программного обеспечения, написанного на интерпретируемых языках с компиляцией в байт-код, исходный код компилируется в байт-код, который затем выполняется виртуальной машиной соответствующего языка.

</div><div class="sect3">  
</div></div></div></div></div>#### Изначально скомпилированный код

В этом примере вы создадите `cello.c` программу, написанную на языке [C](https://en.wikipedia.org/wiki/C_%28programming_language%29) в исполняемый файл.

`cello.c`

<div id="bkmrk--16"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="paragraph">  
</div><div class="listingblock"><div class="content">  
</div></div></div></div></div></div></div>```
#include <stdio.h>

int main(void) {
    printf("Hello World\n");
    return 0;
}
```

<div id="bkmrk--17"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="listingblock"><div class="content">  
</div></div><div class="sect4">  
</div></div></div></div></div></div>##### Ручная сборка

Вызовите компилятор [C](https://en.wikipedia.org/wiki/C_%28programming_language%29) из коллекции компиляторов GNU ([GCC](https://gcc.gnu.org/)) чтобы скомпилировать исходный код в бинарный файл:

<div id="bkmrk--18"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="paragraph">  
</div><div class="listingblock"><div class="content">  
</div></div></div></div></div></div></div></div>```
gcc -g -o cello cello.c
```

<div id="bkmrk--19"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="listingblock"><div class="content">  
</div></div><div class="paragraph">  
</div></div></div></div></div></div></div>Запустите бинарный файл `cello`.

<div id="bkmrk--20"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="paragraph">  
</div><div class="listingblock"><div class="content">  
</div></div></div></div></div></div></div></div>```
$ ./cello
Hello World
```

<div id="bkmrk--21"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="listingblock"><div class="content">  
</div></div><div class="paragraph">  
</div></div></div></div></div></div></div>Вот и все. Вы создали и запустили изначально скомпилированное программное обеспечение из исходного кода.

##### Автоматическая сборка

Вместо того, чтобы создавать исходный код вручную, вы можете автоматизировать сборку. Это обычная практика, используемая в крупномасштабном программном обеспечении. Автоматизация сборки осуществляется путем создания `Makefile` и последующим запуском утилиты [GNU `make`](http://www.gnu.org/software/make/).

Чтобы настроить автоматическую сборку, создайте файл с именем `Makefile` в том же каталоге, что и`cello.c`:

`Makefile`

<div id="bkmrk--22"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="paragraph">  
</div><div class="listingblock"><div class="content">  
</div></div></div></div></div></div></div></div>```
cello:
        gcc -g -o cello cello.c

clean:
        rm cello
```

<div id="bkmrk--23"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="listingblock"><div class="content">  
</div></div><div class="paragraph">  
</div></div></div></div></div></div></div>Теперь, чтобы собрать программу, просто запустите `make`:

<div id="bkmrk--24"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="paragraph">  
</div><div class="listingblock"><div class="content">  
</div></div></div></div></div></div></div></div>```
$ make
make: 'cello' is up to date.
```

<div id="bkmrk--25"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="listingblock"><div class="content">  
</div></div><div class="paragraph">  
</div></div></div></div></div></div></div>Посколько сборка уже создана, `make clean` очистит её, а затем снова запустит `make`:

<div id="bkmrk--26"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="paragraph">  
</div><div class="listingblock"><div class="content">  
</div></div></div></div></div></div></div></div>```
$ make clean
rm cello

$ make
gcc -g -o cello cello.c
```

<div id="bkmrk--27"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="listingblock"><div class="content">  
</div></div><div class="paragraph">  
</div></div></div></div></div></div></div>Опять же, попытка сборки после другой сборки ничего не даст:

<div id="bkmrk--28"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="paragraph">  
</div><div class="listingblock"><div class="content">  
</div></div></div></div></div></div></div></div>```
$ make
make: 'cello' is up to date.
```

<div id="bkmrk--29"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="listingblock"><div class="content">  
</div></div><div class="paragraph">  
</div></div></div></div></div></div></div>Наконец, программа выполнится:

<div id="bkmrk--30"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="paragraph">  
</div><div class="listingblock"><div class="content">  
</div></div></div></div></div></div></div></div>```
$ ./cello
Hello World
```

<div id="bkmrk--31"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="listingblock"><div class="content">  
</div></div><div class="paragraph">  
</div></div></div></div></div></div></div>Теперь Вы скомпилировали программу как вручную, так и с помощью инструмента сборки.

#### Интерпретируемый код

Следующие два примера демонстрируют компиляцию в байт-код программы, написанной на [Python](https://www.python.org/) и прямую интерпретацию программы, написанной на [bash](https://www.gnu.org/software/bash/).

<div id="bkmrk-%D0%9F%D1%80%D0%B8%D0%BC%D0%B5%D1%87%D0%B0%D0%BD%D0%B8%D0%B5-%D0%92-%D0%B4%D0%B2%D1%83%D1%85-%D0%BF%D1%80"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="paragraph">  
</div><div class="admonitionblock note"><table><tbody><tr><td class="icon"><div class="title">Примечание</div></td><td class="content">В двух приведенных ниже примерах`#!` строка в верхней части файла называется [shebang](https://en.wikipedia.org/wiki/Shebang_%28Unix%29) и не является частью исходного кода.

[shebang](https://en.wikipedia.org/wiki/Shebang_%28Unix%29) позволяет использовать текстовый файл в качестве исполняемого файла: загрузчик системной программы анализирует строку, содержащую **shebang**, чтобы получить путь к бинарному исполняемому файлу, который затем используется в качестве интерпретатора языка программирования.

</td></tr></tbody></table>

</div><div class="sect4">  
</div></div></div></div></div></div>##### Компиляция байт-кода

В этом примере Вы скомпилируете в байт-код `pello.py` - программу, написанную на Python, который затем выполняется виртуальной машиной Python. Исходный код Python также может быть напрямую интерпретирован, но исполнение байт-кода быстрее. Следовательно, RPM упаковщики предпочитают упаковывать скомпилированный байт-код для распространения среди конечных пользователей.

`pello.py`

<div id="bkmrk--32"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="paragraph">  
</div><div class="listingblock"><div class="content">  
</div></div></div></div></div></div></div></div>```
#!/usr/bin/env python

print("Hello World")
```

<div id="bkmrk--33"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="listingblock"><div class="content">  
</div></div><div class="paragraph">  
</div></div></div></div></div></div></div>Процедура программ в байт-код отличается для разных языков. Это зависит от языка, виртуальной машины языка, а также инструментов и процессов, используемых с этим языком.

<div id="bkmrk-%D0%9F%D1%80%D0%B8%D0%BC%D0%B5%D1%87%D0%B0%D0%BD%D0%B8%D0%B5-python-%D1%87%D0%B0"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="paragraph">  
</div><div class="admonitionblock note"><table><tbody><tr><td class="icon"><div class="title">Примечание</div></td><td class="content">[Python](https://www.python.org/) часто компилируется в байт-код, но не так, как описано здесь. Следующая процедура направлена не на то, чтобы соответствовать стандартам сообщества, а на то, чтобы быть простой. Для получения практических рекомендаций по Python см. раздел [Упаковка и распространение программного обеспечения](https://docs.python.org/2/library/distribution.html).</td></tr></tbody></table>

</div><div class="paragraph">  
</div></div></div></div></div></div></div>Компиляция `pello.py` в байт-код:

<div id="bkmrk--34"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="paragraph">  
</div><div class="listingblock"><div class="content">  
</div></div></div></div></div></div></div></div>```
$ python -m compileall pello.py

$ file pello.pyc
pello.pyc: python 2.7 byte-compiled
```

<div id="bkmrk--35"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="listingblock"><div class="content">  
</div></div><div class="paragraph">  
</div></div></div></div></div></div></div>Выполните байт-код в `pello.pyc`:

<div id="bkmrk--36"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="paragraph">  
</div><div class="listingblock"><div class="content">  
</div></div></div></div></div></div></div></div>```
$ python pello.pyc
Hello World
```

<div id="bkmrk--37"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="listingblock"><div class="content">  
</div></div></div><div class="sect4">  
</div></div></div></div></div></div>##### Напрямую интепретированный код

В этом примере Вы будете интерпретировать программу `bello` написанную на встроенном языке оболочки [bash](https://www.gnu.org/software/bash/).

`bello`

<div id="bkmrk--38"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="paragraph">  
</div><div class="listingblock"><div class="content">  
</div></div></div></div></div></div></div></div>```
#!/bin/bash

printf "Hello World\n"
```

<div id="bkmrk--39"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="listingblock"><div class="content">  
</div></div><div class="paragraph">  
</div></div></div></div></div></div></div>Для программ, написанных на языках сценариев оболочки, таких как *bash*, используется прямая интерпретация. Следовательно, Вам нужно только сделать файл с исходным кодом исполняемым и запустить его:

<div id="bkmrk--40"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="paragraph">  
</div><div class="listingblock"><div class="content">  
</div></div></div></div></div></div></div></div>```
$ chmod +x bello
$ ./bello
Hello World
```

<div id="bkmrk--41"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="listingblock"><div class="content">  
</div></div></div></div></div><div class="sect2">  
</div></div></div></div>### Программное обеспечение для исправления ошибок

**Patch** - это исходный код, который исправляет другой исходный код. Он отформатирован как *diff*, потому что представляет разницу между двумя версиями текста. Разница создаётся с помощью утилиты `diff`, которая затем применяется к исходному коду с помощью утилиты [patch](http://savannah.gnu.org/projects/patch/).

<div id="bkmrk-%D0%9F%D1%80%D0%B8%D0%BC%D0%B5%D1%87%D0%B0%D0%BD%D0%B8%D0%B5-%D0%A0%D0%B0%D0%B7%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D1%87"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="paragraph">  
</div><div class="admonitionblock note"><table><tbody><tr><td class="icon"><div class="title">Примечание</div></td><td class="content">Разработчики программного обеспечения часто используют системы контроля версий, такие как [git](https://git-scm.com/), для управления своей кодовой базой. Такие инструменты предоставляют свои собственные методы создания различий или исправления программного обеспечения.</td></tr></tbody></table>

</div><div class="paragraph">  
</div></div></div></div></div>В следующем примере мы создаем исправление из исходного кода с помощью `diff`, а затем используем `patch`. Исправление использует в следующих разделах при создании RPM и работе со .spec-файлом. [Работа со SPEC файлами](https://rpm-packaging-guide-ru.github.io/#working-with-spec-files).

Как исправление связано с упаковкой RPM? В упаковке, вместо того, чтобы просто изменять исходный код, мы сохраняем его и используем на нем исправления.

Чтобы создать патч для `cello.c`:

<div id="bkmrk-%D0%A1%D0%BE%D1%85%D1%80%D0%B0%D0%BD%D0%B8%D0%BC-%D0%B8%D1%81%D1%85%D0%BE%D0%B4%D0%BD%D1%8B%D0%B9-%D0%BA%D0%BE"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="paragraph">  
</div><div class="olist arabic">1. Сохраним исходный код:
    
    ```
    $ cp cello.c cello.c.orig
    ```
    
    Это наиболее распространённый способ сохранить файл исходного кода.
2. Изменим`cello.c`:
    
    ```
    #include <stdio.h>
    
    int main(void) {
        printf("Hello World from my very first patch!\n");
        return 0;
    }
    ```
3. Сгенерируем патч используя утилиту `diff`:
    
    <div class="admonitionblock note"><table><tbody><tr><td class="icon"><div class="title">Примечание</div></td><td class="content">Мы используем несколько общих аргументов для утилиты `diff`. Для получения дополнительной информации о них см. руководство по использованию `diff`.</td></tr></tbody></table>
    
    </div>```
    $ diff -Naur cello.c.orig cello.c
    --- cello.c.orig        2016-05-26 17:21:30.478523360 -0500
    +++ cello.c     2016-05-27 14:53:20.668588245 -0500
    @@ -1,6 +1,6 @@
     #include<stdio.h>
    
     int main(void){
    -    printf("Hello World!\n");
    +    printf("Hello World from my very first patch!\n");
         return 0;
     }
    ```
    
    Строки, начинающиеся с `-` удалятся из исходного кода и заменятся на строки, начинающихся с `+`.
4. Сохраним патч в файл:
    
    ```
    $ diff -Naur cello.c.orig cello.c > cello-output-first-patch.patch
    ```
5. Восстановим исходный код `cello.c`:
    
    ```
    $ cp cello.c.orig cello.c
    ```
    
    Мы сохраняем исходный файл `cello.c`, потому что при создании RPM используется исходный файл, а не измененный. Дополнительные сведения см. в разделе [Работа со SPEC файлами](https://rpm-packaging-guide-ru.github.io/#working-with-spec-files).

</div><div class="paragraph">  
</div></div></div></div></div>Чтобы исправить `cello.c` с помощью `cello-output-first-patch.patch`, перенаправьте патч-файл `patch` коммандой:

<div id="bkmrk--42"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="paragraph">  
</div><div class="listingblock"><div class="content">  
</div></div></div></div></div></div>```
$ patch < cello-output-first-patch.patch
patching file cello.c
```

<div id="bkmrk--43"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="listingblock"><div class="content">  
</div></div><div class="paragraph">  
</div></div></div></div></div>Содержимое `cello.c` теперь отражает изменения:

<div id="bkmrk--44"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="paragraph">  
</div><div class="listingblock"><div class="content">  
</div></div></div></div></div></div>```
$ cat cello.c
#include<stdio.h>

int main(void){
    printf("Hello World from my very first patch!\n");
    return 0;
}
```

<div id="bkmrk--45"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="listingblock"><div class="content">  
</div></div><div class="paragraph">  
</div></div></div></div></div>Чтобы собрать и запустить отредактированную `cello.c`:

<div id="bkmrk--46"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="paragraph">  
</div><div class="listingblock"><div class="content">  
</div></div></div></div></div></div>```
$ make clean
rm cello

$ make
gcc -g -o cello cello.c

$ ./cello
Hello World from my very first patch!
```

<div id="bkmrk--47"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="listingblock"><div class="content">  
</div></div><div class="paragraph">  
</div></div></div></div></div>Вы создали патч, отредактировали программу, собрали отредактированную программу и запустили её.

### Установка Произвольных Артефактов

Большим преимуществом [Linux](https://en.wikipedia.org/wiki/Linux) и других Unix-подобных систем является [Стандарт иерархии файловой системы](https://en.wikipedia.org/wiki/Filesystem_Hierarchy_Standard) . Он указывает, в каком каталоге должны быть расположены файлы. Файлы, установленные из пакетов RPM, должны быть размещены в соответствии с ИФС. Например, исполняемый файл должен находиться в каталоге, который находится в переменной [PATH](https://en.wikipedia.org/wiki/PATH_%28variable%29) .

В контексте этого руководства, *Произвольный артефакт* - это все, что устанавливается из RPM в систему. Для RPM и для системы это может быть скрипт, бинарный файл, скомпилированный из исходного кода пакета, предварительно скомпилированный бинарный файл или любой другой файл.

Мы рассмотрим два популярных способа размещения *произвольных артефактов* в системе: с помощью команды `install` и с помощью команды `make install`.

#### Использование команды install

Иногда с помощью инструментов автоматизации сборки, таких как [GNU make](http://www.gnu.org/software/make/) не является оптимальным - например, если упакованная программа проста. В этих случаях упаковщики часто используют команду `install` (предоставляемая системе [coreutils](http://www.gnu.org/software/coreutils/coreutils.html)), которая помещает артефакт в указанный каталог в файловой системе с указанным набором разрешений.

В приведенном ниже примере будет использоваться файл `bello`, который мы ранее создали в качестве произвольного артефакта, зависящего от нашего метода установки. Обратите внимание, что Вам либо понадобятся разрешения [sudo](http://www.sudo.ws/), либо запустите эту команду от имени root, исключая часть команды `sudo`.

В этом примере `install` помещает файл `bello` в `/usr/bin` с разрешениями, общими для исполняемых скриптов:

<div id="bkmrk--48"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="paragraph">  
</div><div class="listingblock"><div class="content">  
</div></div></div></div></div></div></div>```
$ sudo install -m 0755 bello /usr/bin/bello
```

<div id="bkmrk--49"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="listingblock"><div class="content">  
</div></div><div class="paragraph">  
</div></div></div></div></div></div>Теперь`bello` находится в каталоге, который указан в переменной [$PATH](https://en.wikipedia.org/wiki/PATH_%28variable%29) . Таким образом, Вы можете запустить `bello` из любого каталога, не указывая его путь:

<div id="bkmrk--50"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="paragraph">  
</div><div class="listingblock"><div class="content">  
</div></div></div></div></div></div></div>```
$ cd ~

$ bello
Hello World
```

<div id="bkmrk--51"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="listingblock"><div class="content">  
</div></div></div><div class="sect3">  
</div></div></div></div></div>#### Использование команды make install

Популярным автоматизированным способом установки программного обеспечения в систему является использование команды `make install`. Вы указываете, как установить произвольные артефакты в систему в файле `Makefile`.

<div id="bkmrk-%D0%9F%D1%80%D0%B8%D0%BC%D0%B5%D1%87%D0%B0%D0%BD%D0%B8%D0%B5-%D0%9E%D0%B1%D1%8B%D1%87%D0%BD%D0%BE-ma"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="paragraph">  
</div><div class="admonitionblock note"><table><tbody><tr><td class="icon"><div class="title">Примечание</div></td><td class="content">Обычно `Makefile` пишется разработчиком, а не упаковщиком.</td></tr></tbody></table>

</div><div class="paragraph">  
</div></div></div></div></div></div>Добавьте секцию `install` в `Makefile`:

`Makefile`

<div id="bkmrk--52"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="paragraph">  
</div><div class="listingblock"><div class="content">  
</div></div></div></div></div></div></div>```
cello:
        gcc -g -o cello cello.c

clean:
        rm cello

install:
        mkdir -p $(DESTDIR)/usr/bin
        install -m 0755 cello $(DESTDIR)/usr/bin/cello
```

<div id="bkmrk--53"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="listingblock"><div class="content">  
</div></div><div class="paragraph">  
</div></div></div></div></div></div>Переменная [$(DESTDIR)](https://www.gnu.org/software/make/manual/html_node/DESTDIR.html) является встроенной в [GNU make](http://www.gnu.org/software/make/) и обычно используется для указания установки в каталог, отличный от корневого каталога.

Теперь вы можете использовать `Makefile` не только для сборки программного обеспечения, но и для его установки в систему.

Для сборки и установки программы `cello.c`:

<div id="bkmrk--54"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="paragraph">  
</div><div class="listingblock"><div class="content">  
</div></div></div></div></div></div></div>```
$ make
gcc -g -o cello cello.c

$ sudo make install
install -m 0755 cello /usr/bin/cello
```

<div id="bkmrk--55"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="listingblock"><div class="content">  
</div></div><div class="paragraph">  
</div></div></div></div></div></div>Теперь`cello` находится в каталоге, который указан в переменной [$PATH](https://en.wikipedia.org/wiki/PATH_%28variable%29) . Таким образом, Вы можете запустить `cello` из любого каталога, не указывая его полный путь.

<div id="bkmrk--56"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="paragraph">  
</div><div class="listingblock"><div class="content">  
</div></div></div></div></div></div></div>```
$ cd ~

$ cello
Hello World
```

<div id="bkmrk--57"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="listingblock"><div class="content">  
</div></div><div class="paragraph">  
</div></div></div></div></div></div>Вы установили артефакт сборки в выбранное место в системе.

### Подготовка исходного кода для упаковки

<div id="bkmrk-%D0%9F%D1%80%D0%B8%D0%BC%D0%B5%D1%87%D0%B0%D0%BD%D0%B8%D0%B5-%D0%9A%D0%BE%D0%B4%2C-%D1%81%D0%BE%D0%B7%D0%B4"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="admonitionblock note"><table><tbody><tr><td class="icon"><div class="title">Примечание</div></td><td class="content">Код, созданный в этом разделе, можно найти [здесь](https://github.com/redhat-developer/rpm-packaging-guide/tree/master/example-code).</td></tr></tbody></table>

</div><div class="paragraph">  
</div></div></div></div></div>Разработчики часто распространяют программное обеспечение в виде сжатых архивов исходного кода, которые затем используются для создания пакетов. В этом разделе Вы создадите такие архивы.

<div id="bkmrk-%D0%9F%D1%80%D0%B8%D0%BC%D0%B5%D1%87%D0%B0%D0%BD%D0%B8%D0%B5-%D0%A1%D0%BE%D0%B7%D0%B4%D0%B0%D0%BD%D0%B8%D0%B5-"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="paragraph">  
</div><div class="admonitionblock note"><table><tbody><tr><td class="icon"><div class="title">Примечание</div></td><td class="content">Создание архивов исходного кода обычно выполняется не RPM-упаковщиком, а разработчиком. Упаковщик работает с готовым архивом исходного кода.</td></tr></tbody></table>

</div><div class="paragraph">  
</div></div></div></div></div>Программное обеспечение должно распространяться с [лицензией](https://en.wikipedia.org/wiki/Software_license) . Для примера мы будем использовать лицензию [GPLv3](https://www.gnu.org/licenses/quick-guide-gplv3.html). Текст лицензии помещается в файл `LICENSE` для каждой из примеров программ. Упаковщику RPM необходимо иметь дело с файлами лицензий при упаковке.

Для использования со следующими примерами создайте файл `LICENSE`:

<div id="bkmrk--58"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="paragraph">  
</div><div class="listingblock"><div class="content">  
</div></div></div></div></div></div>```
$ cat /tmp/LICENSE
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
```

<div id="bkmrk--59"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="listingblock"><div class="content">  
</div></div></div><div class="sect2">  
</div></div></div></div>### Создание Tarball с исходным кодом

В приведенных ниже примерах мы помещаем каждую из трех программ `Hello World` в архив, сжатый с помощью [gzip](https://www.gnu.org/software/gzip/). Программное обеспечение часто выпускается таким образом, чтобы позже быть упакованным для распространения.

#### bello

Проект *bello* реализует `Hello World` в [bash](https://www.gnu.org/software/bash/). Реализация содержит только сценарий оболочки `bello`, поэтому результирующий архив `tar.gz` будет содержать только один файл, кроме файла `LICENSE`. Давайте предположим, что это версия программы - `0.1`

Подготовьте проект *bello* для распространения:

<div id="bkmrk-%D0%9F%D0%BE%D0%BC%D0%B5%D1%81%D1%82%D0%B8%D1%82%D0%B5-%D1%84%D0%B0%D0%B9%D0%BB%D1%8B-%D0%B2-%D0%BE%D0%B4"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="paragraph">  
</div><div class="olist arabic">1. Поместите файлы в один каталог:
    
    ```
    $ mkdir /tmp/bello-0.1
    
    $ mv ~/bello /tmp/bello-0.1/
    
    $ cp /tmp/LICENSE /tmp/bello-0.1/
    ```
2. Создайте архив для распространения и переместите его в `~/rpmbuild/SOURCES/`:
    
    ```
    $ cd /tmp/
    
    $ tar -cvzf bello-0.1.tar.gz bello-0.1
    bello-0.1/
    bello-0.1/LICENSE
    bello-0.1/bello
    
    $ mv /tmp/bello-0.1.tar.gz ~/rpmbuild/SOURCES/
    ```

</div></div><div class="sect3">  
</div></div></div></div></div>#### pello

Проект *pello* реализует `Hello World` на [Python](https://www.python.org/). Реализация содержит только программу `pello.py`, так что результирующий архив `tar.gz` будет содержать только один файл, кроме файла `LICENSE`. Предположим, что это версия программы - `0.1.1`

Подготовьте проект *pello* для распространения:

<div id="bkmrk-%D0%9F%D0%BE%D0%BC%D0%B5%D1%81%D1%82%D0%B8%D1%82%D0%B5-%D1%84%D0%B0%D0%B9%D0%BB%D1%8B-%D0%B2-%D0%BE%D0%B4-1"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="paragraph">  
</div><div class="olist arabic">1. Поместите файлы в один каталог:
    
    ```
    $ mkdir /tmp/pello-0.1.1
    
    $ mv ~/pello.py /tmp/pello-0.1.1/
    
    $ cp /tmp/LICENSE /tmp/pello-0.1.1/
    ```
2. Создайте архив для распространения и переместите его в `~/rpmbuild/SOURCES/`:
    
    ```
    $ cd /tmp/
    
    $ tar -cvzf pello-0.1.1.tar.gz pello-0.1.1
    pello-0.1.1/
    pello-0.1.1/LICENSE
    pello-0.1.1/pello.py
    
    $ mv /tmp/pello-0.1.1.tar.gz ~/rpmbuild/SOURCES/
    ```

</div></div><div class="sect3">  
</div></div></div></div></div>#### cello

Проект *cello* реализует `Hello World` на [C](https://ru.wikipedia.org/wiki/%D0%A1%D0%B8_(%D1%8F%D0%B7%D1%8B%D0%BA_%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F)) . Реализация содержит только файлы `cello.c` и `Makefile`, поэтому результирующий архив `tar.gz` будет содержать только два файла, помимо файла `LICENSE`. Давайте предположим, что это версия программы - `1.0`

Обратите внимание, что `patch` не распространяется в архиве вместе с программой. Упаковщик RPM применяет исправление при создании RPM. Патч будет помещен в каталог `~/rpmbuild/SOURCES/` рядом с `.tar.gz` архивом.

Подготовьте проект *cello* для распространения:

<div id="bkmrk-%D0%9F%D0%BE%D0%BC%D0%B5%D1%81%D1%82%D0%B8%D1%82%D0%B5-%D1%84%D0%B0%D0%B9%D0%BB%D1%8B-%D0%B2-%D0%BE%D0%B4-2"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="paragraph">  
</div><div class="olist arabic">1. Поместите файлы в один каталог:
    
    ```
    $ mkdir /tmp/cello-1.0
    
    $ mv ~/cello.c /tmp/cello-1.0/
    
    $ mv ~/Makefile /tmp/cello-1.0/
    
    $ cp /tmp/LICENSE /tmp/cello-1.0/
    ```
2. Создайте архив для распространения и переместите его в `~/rpmbuild/SOURCES/`:
    
    ```
    $ cd /tmp/
    
    $ tar -cvzf cello-1.0.tar.gz cello-1.0
    cello-1.0/
    cello-1.0/Makefile
    cello-1.0/cello.c
    cello-1.0/LICENSE
    
    $ mv /tmp/cello-1.0.tar.gz ~/rpmbuild/SOURCES/
    ```
3. Добавьте патч:
    
    ```
    $ mv ~/cello-output-first-patch.patch ~/rpmbuild/SOURCES/
    ```

</div><div class="paragraph">  
</div></div></div></div></div></div>Теперь исходный код готов к упаковке в RPM.

## Программное обеспечение для упаковки

В этом руководстве объясняется, как упаковывать RPM для дистрибутивов Linux семейства Red Hat, в первую очередь:

<div id="bkmrk-fedora-centos-red-ha"><div class="sect1"><div class="sectionbody"><div class="paragraph">  
</div><div class="ulist">- [Fedora](https://getfedora.org/)
- [CentOS](https://www.centos.org/)
- [Red Hat Enterprise Linux](https://www.redhat.com/en/technologies/linux-platforms) ([RHEL](https://www.redhat.com/en/technologies/linux-platforms))

</div><div class="paragraph">  
</div></div></div></div>Эти дистрибутивы используют формат упаковки [RPM](http://rpm.org/).

Хотя эти дистрибутивы являются целевой средой, данное руководство в основном применимо ко всем дистрибутивам, основанным на [RPM based](https://ru.wikipedia.org/wiki/%D0%A1%D0%BF%D0%B8%D1%81%D0%BE%D0%BA_%D0%B4%D0%B8%D1%81%D1%82%D1%80%D0%B8%D0%B1%D1%83%D1%82%D0%B8%D0%B2%D0%BE%D0%B2_Linux) . Однако инструкции должны быть адаптированы для функций, специфичных для дистрибутива, таких как обязательные элементы установки, рекомендации или макросы.

В этом руководстве не предполагается никаких предварительных знаний об упаковке программного обеспечения для операционных систем Linux или какой-либо другой.

<div id="bkmrk-%D0%9F%D1%80%D0%B8%D0%BC%D0%B5%D1%87%D0%B0%D0%BD%D0%B8%D0%B5-%D0%95%D1%81%D0%BB%D0%B8-%D0%92%D1%8B-%D0%BD"><div class="sect1"><div class="sectionbody"><div class="paragraph">  
</div><div class="admonitionblock note"><table><tbody><tr><td class="icon"><div class="title">Примечание</div></td><td class="content">Если Вы не знаете, что такое программный пакет или дистрибутив GNU/Linux, рассмотрите возможность изучения некоторых статей на темы [Linux](https://ru.wikipedia.org/wiki/Linux) и [Package Managers](https://en.wikipedia.org/wiki/Package_manager).</td></tr></tbody></table>

</div><div class="sect2">  
</div></div></div></div>### RPM Пакеты

В этом разделе рассматриваются основы формата упаковки RPM. Дополнительные сведения смотри в разделе [Дополнительные материалы](https://rpm-packaging-guide-ru.github.io/#advanced-topics).

#### Что такое RPM?

Пакет RPM - это просто файл, содержащий другие файлы и информацию о них, необходимую системе. В частности, пакет RPM состоит из архива [cpio](https://en.wikipedia.org/wiki/Cpio) , который содержит файлы, и заголовка RPM, который содержит метаданные о пакете. Диспетчер пакетов `rpm` использует эти метаданные для определения зависимостей, места установки файлов и другой информации.

Существует два типа пакетов RPM:

<div id="bkmrk-%D0%B8%D1%81%D1%85%D0%BE%D0%B4%D0%BD%D0%B8%D0%BA-rpm-%28srpm%29-"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="paragraph">  
</div><div class="ulist">- исходник RPM (SRPM)
- бинарный RPM

</div><div class="paragraph">  
</div></div></div></div></div></div>SRPMs и бинарные RPMs имеют общий формат файла и инструментарий, но имеют разное содержимое и служат разным целям. SRPM содержит исходный код, при необходимости исправления к нему и файл спецификации, в котором описывается, как встроить исходный код в бинарный RPM. Бинарный RPM содержит двоичные файлы, созданные из исходных текстов и патчей.

#### Инструменты для упаковки RPM

Пакет`rpmdevtools`, установленный на этапе [Необходимые пакеты](https://rpm-packaging-guide-ru.github.io/#prerequisites), предоставляет несколько утилит для упаковки RPM. Чтобы перечислить эти утилиты, запустите:

<div id="bkmrk--60"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="paragraph">  
</div><div class="listingblock"><div class="content">  
</div></div></div></div></div></div></div>```
$ rpm -ql rpmdevtools | grep bin
```

<div id="bkmrk--61"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="listingblock"><div class="content">  
</div></div><div class="paragraph">  
</div></div></div></div></div></div>Для получения дополнительной информации о вышеуказанных утилитах см. их страницы руководства или диалоговые окна справки.

#### Рабочее пространство для упаковки RPM

Чтобы настроить макет каталога, который является рабочей областью упаковки RPM, используйте утилиту `rpmdev-setuptree`:

<div id="bkmrk--62"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="paragraph">  
</div><div class="listingblock"><div class="content">  
</div></div></div></div></div></div></div>```
$ rpmdev-setuptree

$ tree ~/rpmbuild/
/home/user/rpmbuild/
|-- BUILD
|-- RPMS
|-- SOURCES
|-- SPECS
`-- SRPMS`

5 directories, 0 files
```

<div id="bkmrk--63"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="listingblock"><div class="content">  
</div></div><div class="paragraph">  
</div></div></div></div></div></div>Созданные каталоги служат следующим целям:

<div id="bkmrk-%D0%9A%D0%B0%D1%82%D0%B0%D0%BB%D0%BE%D0%B3-%D0%9D%D0%B0%D0%B7%D0%BD%D0%B0%D1%87%D0%B5%D0%BD%D0%B8%D0%B5-b"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="paragraph">  
</div><table class="tableblock frame-all grid-all stretch"><colgroup> <col style="width: 20%;"></col> <col style="width: 80%;"></col> </colgroup><tbody><tr><td class="tableblock halign-left valign-top">Каталог

</td><td class="tableblock halign-left valign-top">Назначение

</td></tr><tr><td class="tableblock halign-left valign-top">BUILD

</td><td class="tableblock halign-left valign-top">Содержит все файлы, которые появляются при создании пакета.

</td></tr><tr><td class="tableblock halign-left valign-top">RPMS

</td><td class="tableblock halign-left valign-top">Бинарные RPM создаются здесь, в подкаталогах для разных архитектур, например, в подкаталогах`x86_64` и `noarch`.

</td></tr><tr><td class="tableblock halign-left valign-top">SOURCES

</td><td class="tableblock halign-left valign-top">Здесь упаковщик помещает сжатые архивы исходного кода и патчи. Команда `rpmbuild` ищет их здесь.

</td></tr><tr><td class="tableblock halign-left valign-top">SPECS

</td><td class="tableblock halign-left valign-top">Упаковщик помещает сюда файлы спецификаций.

</td></tr><tr><td class="tableblock halign-left valign-top">SRPMS

</td><td class="tableblock halign-left valign-top">Когда rpmbuild используется для сборки SRPM вместо бинарного RPM, результирующий SRPM создается здесь.

</td></tr></tbody></table>

</div><div class="sect3">  
</div></div></div></div></div>#### Что такое SPEC файл?

Файл спецификации можно рассматривать как "рецепт", который утилита `rpmbuild` использует для фактической сборки RPM. Он сообщает системе сборки, что делать, определяя инструкции в серии разделов. Разделы определены в *Преамбуле* и в *Основной части*. *Преамбула* содержит ряд элементов метаданных, которые используются в *Основной части*. Тело содержит основную часть инструкций.

##### Пункты преамбулы

В этой таблице перечислены элементы, используемые в разделе преамбулы файла спецификации RPM:

<div id="bkmrk-spec-%D0%94%D0%B8%D1%80%D0%B5%D0%BA%D1%82%D0%B8%D0%B2%D0%B0-%D0%9E%D0%BF%D1%80%D0%B5%D0%B4"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="paragraph">  
</div><table class="tableblock frame-all grid-all stretch"><colgroup> <col style="width: 30%;"></col> <col style="width: 70%;"></col> </colgroup><tbody><tr><td class="tableblock halign-left valign-top">SPEC Директива

</td><td class="tableblock halign-left valign-top">Определение

</td></tr><tr><td class="tableblock halign-left valign-top">`Name`

</td><td class="tableblock halign-left valign-top">Базовое имя пакета, которое должно совпадать с именем файла спецификации.

</td></tr><tr><td class="tableblock halign-left valign-top">`Version`

</td><td class="tableblock halign-left valign-top">Версия upstream-кода.

</td></tr><tr><td class="tableblock halign-left valign-top">`Release`

</td><td class="tableblock halign-left valign-top">Релиз пакета используется для указания номера сборки пакета при данной версии upstream-кода. Как правило, установите начальное значение равным 1%{?dist} и увеличивайте его с каждым новым выпуском пакета. Сбросьте значение до 1 при создании новой версии программного обеспечения.

</td></tr><tr><td class="tableblock halign-left valign-top">`Summary`

</td><td class="tableblock halign-left valign-top">Краткое, в одну строку, описание пакета.

</td></tr><tr><td class="tableblock halign-left valign-top">`License`

</td><td class="tableblock halign-left valign-top">Лицензия на упаковываемое программное обеспечение. Для пакетов, распространяемых в дистрибутивах сообщества, таких как [Fedora](https://getfedora.org/), это должна быть лицензия с открытым исходным кодом, соответствующая рекомендациям по лицензированию конкретного дистрибутива.

</td></tr><tr><td class="tableblock halign-left valign-top">`URL`

</td><td class="tableblock halign-left valign-top">Полный URL-адрес для получения дополнительной информации о программе. Чаще всего это веб-сайт upstream-проекта для упаковываемого программного обеспечения.

</td></tr><tr><td class="tableblock halign-left valign-top">`Source0`

</td><td class="tableblock halign-left valign-top">Путь или URL-адрес к сжатому архиву исходного кода (не исправленный, исправления обрабатываются в другом месте). Этот раздел должен указывать на доступное и надежное хранилище архива, например, на upstream-страницу, а не на локальное хранилище упаковщика. При необходимости можно добавить дополнительные исходные директивы, каждый раз увеличивая их количество, например: Source1, Source2, Source3 и так далее.

</td></tr><tr><td class="tableblock halign-left valign-top">`Patch0`

</td><td class="tableblock halign-left valign-top">Название первого исправления, которое при необходимости будет применено к исходному коду. При необходимости можно добавить дополнительные директивы PatchX, увеличивая их количество каждый раз, например: Patch1, Patch2, Patch3 и так далее.

</td></tr><tr><td class="tableblock halign-left valign-top">`BuildArch`

</td><td class="tableblock halign-left valign-top">Если пакет не зависит от архитектуры, например, если он полностью написан на интерпретируемом языке программирования, установите для этого значение `BuildArch: noarch`. Если этот параметр не задан, пакет автоматически наследует архитектуру компьютера, на котором он построен, например `x86_64`.

</td></tr><tr><td class="tableblock halign-left valign-top">`BuildRequires`

</td><td class="tableblock halign-left valign-top">Разделённый запятыми или пробелами список пакетов, необходимых для сборки программы, написанной на скомпилированном языке. Может быть несколько записей `BuildRequires`, каждая в отдельной строке в SPEC файле.

</td></tr><tr><td class="tableblock halign-left valign-top">`Requires`

</td><td class="tableblock halign-left valign-top">Разделённый запятыми или пробелами список пакетов, необходимых программному обеспечению для запуска после установки. Может быть несколько записей `Requires`, каждая в отдельной строке в SPEC файле.

</td></tr><tr><td class="tableblock halign-left valign-top">`ExcludeArch`

</td><td class="tableblock halign-left valign-top">Если часть программного обеспечения не может работать на определенной архитектуре процессора, Вы можете исключить эту архитектуру здесь.

</td></tr></tbody></table>

<div class="paragraph">  
</div></div></div></div></div></div></div>Директивы `Name`, `Version` и `Release` содержат имя файла пакета RPM. Разработчики пакетов RPM и системные администраторы часто называют эти три директивы **N-V-R** или **NVR**, поскольку имена файлов пакетов RPM имеют формат `NAME-VERSION-RELEASE`.

Вы можете получить пример `NAME-VERSION-RELEASE`, выполнив запрос с использованием `rpm` для конкретного пакета:

<div id="bkmrk--64"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="paragraph">  
</div><div class="listingblock"><div class="content">  
</div></div></div></div></div></div></div></div>```
$ rpm -q python
python-2.7.5-34.el7.x86_64
```

<div id="bkmrk--65"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="listingblock"><div class="content">  
</div></div><div class="paragraph">  
</div></div></div></div></div></div></div>Здесь `python` - это имя пакета, `2.7.5` - версия, а `34.el7` - релиз. Последний маркер `x86_64` - сведения об архитектуре. В отличие от NVR, маркер архитектуры не находится под прямым управлением RPM упаковщика, а определяется средой сборки `rpmbuild`. Исключением из этого правила является архитектурно-независимый пакет `noarch`.

##### Составляющие основной части

В этой таблице перечислены элементы, используемые в разделе Body (Тело, основная часть) файла спецификации RPM:

<div id="bkmrk-spec-%D0%94%D0%B8%D1%80%D0%B5%D0%BA%D1%82%D0%B8%D0%B2%D0%B0-%D0%9E%D0%BF%D1%80%D0%B5%D0%B4-1"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="paragraph">  
</div><table class="tableblock frame-all grid-all stretch"><colgroup> <col style="width: 20%;"></col> <col style="width: 80%;"></col> </colgroup><tbody><tr><td class="tableblock halign-left valign-top">SPEC Директива

</td><td class="tableblock halign-left valign-top">Определение

</td></tr><tr><td class="tableblock halign-left valign-top">`%description`

</td><td class="tableblock halign-left valign-top">Полное описание программного обеспечения, входящего в комплект поставки RPM. Это описание может занимать несколько строк и может быть разбито на абзацы.

</td></tr><tr><td class="tableblock halign-left valign-top">`%prep`

</td><td class="tableblock halign-left valign-top">Команда или серия команд для подготовки программного обеспечения к сборке, например, распаковка архива в Source0. Эта директива может содержать сценарий оболочки.

</td></tr><tr><td class="tableblock halign-left valign-top">`%build`

</td><td class="tableblock halign-left valign-top">Команда или серия команд для фактической сборки программного обеспечения в машинный код (для скомпилированных языков) или байт-код (для некоторых интерпретируемых языков).

</td></tr><tr><td class="tableblock halign-left valign-top">`%install`

</td><td class="tableblock halign-left valign-top">Команда или серия команд для копирования требуемых артефактов сборки из `%builddir` (где происходит сборка) в`%buildroot` каталог (который содержит структуру каталогов с файлами, подлежащими упаковке). Обычно это означает копирование файлов из `~/rpmbuild/BUILD` в `~/rpmbuild/BUILDROOT` и создание необходимых каталогов `~/rpmbuild/BUILDROOT`. Это выполняется только при создании пакета, а не при установке пакета конечным пользователем. Подробности см. в разделе [Работа со SPEC файлом](https://rpm-packaging-guide-ru.github.io/#working-with-spec-files).

</td></tr><tr><td class="tableblock halign-left valign-top">`%check`

</td><td class="tableblock halign-left valign-top">Команда или серия команд для тестирования программного обеспечения. Обычно включает в себя такие вещи, как модульные тесты.

</td></tr><tr><td class="tableblock halign-left valign-top">`%files`

</td><td class="tableblock halign-left valign-top">Список файлов, которые будут установлены в системе конечного пользователя.

</td></tr><tr><td class="tableblock halign-left valign-top">`%changelog`

</td><td class="tableblock halign-left valign-top">Запись изменений, произошедших с пакетом между различными `Version` или `Release` сборками.

</td></tr></tbody></table>

</div><div class="sect4">  
</div></div></div></div></div></div>##### Дополнительные элементы

Файл спецификации также может содержать дополнительные элементы. Например, файл спецификации может содержать *скриптлеты\_ и* триггеры\_\_. Они вступают в силу в разные моменты процесса установки в системе конечного пользователя (не в процессе сборки).

Дополнительную информацию см. [Триггеры и скриптлеты](https://rpm-packaging-guide-ru.github.io/#triggers-and-scriptlets).

#### BuildRoots

В контексте упаковки RPM "buildroot" - это среда [chroot](https://en.wikipedia.org/wiki/Chroot) Это означает, что артефакты сборки размещаются здесь с использованием той же иерархии файловой системы, что и в системе конечного пользователя, при этом "buildroot" выступает в качестве корневого каталога. Размещение артефактов сборки должно соответствовать стандарту иерархии файловой системы конечного пользователя.

Файлы в "buildroot" позже помещаются в архив [cpio](https://en.wikipedia.org/wiki/Cpio) , который становится основной частью RPM. Когда RPM устанавливается в системе конечного пользователя, эти файлы извлекаются в корневой каталог, сохраняя правильную иерархию.

<div id="bkmrk-%D0%9F%D1%80%D0%B8%D0%BC%D0%B5%D1%87%D0%B0%D0%BD%D0%B8%D0%B5-%D0%9D%D0%B0%D1%87%D0%B8%D0%BD%D0%B0%D1%8F-%D1%81"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="paragraph">  
</div><div class="admonitionblock note"><table><tbody><tr><td class="icon"><div class="title">Примечание</div></td><td class="content">Начиная с выпуска Red Hat Enterprise Linux 6, программа `rpmbuild` имеет свои собственные значения макросов по умолчанию. Поскольку переопределение этих значений по умолчанию приводит к ряду проблем, Red Hat не рекомендует определять собственное значение этого макроса. Вы можете использовать макрос `%{buildroot}` с параметрами по умолчанию из каталога `rpmbuild`.

</td></tr></tbody></table>

</div></div><div class="sect3">  
</div></div></div></div></div>#### RPM Макросы

[Макрос RPM](https://rpm-software-management.github.io/rpm/manual/macros.html) - это прямая замена текста, которая может быть условно назначена на основе необязательной оценки оператора при использовании определенной встроенной функциональности. Это означает, что Вы можете заставить RPM выполнять замены текста за Вас.

Это полезно, например, при многократной ссылке на *Version* упакованного программного обеспечения в файле спецификации. Вы определяете *Version* только один раз - в макросе `%{version}`. Затем используйте `%{version}` во всем файле спецификации. Каждый раз, во время определения строки `%{version}`, в конечном итоге будет подставлена *Version* программы, которую вы определили ранее.

<div id="bkmrk-%D0%9F%D1%80%D0%B8%D0%BC%D0%B5%D1%87%D0%B0%D0%BD%D0%B8%D0%B5-%D0%95%D1%81%D0%BB%D0%B8-%D0%92%D1%8B-%D0%B2"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="paragraph">  
</div><div class="admonitionblock note"><table><tbody><tr><td class="icon"><div class="title">Примечание</div></td><td class="content">Если Вы видите незнакомый макрос, Вы можете узнать о нём с помощью:

```
$ rpm --eval %{_MACRO}
```

Например:

```
$ rpm --eval %{_bindir}
/usr/bin

$ rpm --eval %{_libexecdir}
/usr/libexec
```

</td></tr></tbody></table>

</div><div class="paragraph">  
</div></div></div></div></div></div>Распространённым макросом является `%{?dist}`, который обозначает “тег распространения”. Он сигнализирует, какой дистрибутив используется для сборки.

Например:

<div id="bkmrk--66"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="paragraph">  
</div><div class="listingblock"><div class="content">  
</div></div></div></div></div></div></div>```
# On a RHEL 7.x machine
$ rpm --eval %{?dist}
.el7

# On a Fedora 23 machine
$ rpm --eval %{?dist}
.fc23
```

<div id="bkmrk--67"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="listingblock"><div class="content">  
</div></div><div class="paragraph">  
</div></div></div></div></div></div>Больше информации о макросах см. в разделе [Подробнее о макросах](https://rpm-packaging-guide-ru.github.io/#more-on-macros).

#### Работа со SPEC файлами

Большая часть упаковки программного обеспечения в RPMs - это редактирование файла спецификации. В этом разделе мы обсудим, как создать и изменить SPEC файл.

Чтобы упаковать новое программное обеспечение, Вам необходимо создать новый файл спецификации. Вместо того, чтобы писать его вручную с нуля, используйте утилиту `rpmdev-newspec`. Она создаёт незаполненный файл спецификации, и Вы заполняете необходимые директивы и поля.

В этом руководстве мы используем три примера реализации программы 'Hello World!', созданной при подготовке [программного обеспечения для упаковки](https://rpm-packaging-guide-ru.github.io/#preparing-software-for-packaging):

<div id="bkmrk-bello-0.1.tar.gz-pel"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="paragraph">  
</div><div class="ulist">- [bello-0.1.tar.gz](https://github.com/redhat-developer/rpm-packaging-guide/raw/master/example-code/bello-0.1.tar.gz)
- [pello-0.1.1.tar.gz](https://github.com/redhat-developer/rpm-packaging-guide/raw/master/example-code/pello-0.1.1.tar.gz)
- [cello-1.0.tar.gz](https://github.com/redhat-developer/rpm-packaging-guide/raw/master/example-code/cello-1.0.tar.gz)
    
    <div class="ulist">
    - [cello-output-first-patch.patch](https://raw.githubusercontent.com/redhat-developer/rpm-packaging-guide/master/example-code/cello-output-first-patch.patch)
    
    </div>

</div><div class="paragraph">  
</div></div></div></div></div></div>Переместите их в `~/rpmbuild/SOURCES`.

Создайте SPEC файл для каждой из трёх программ:

<div id="bkmrk-%D0%9F%D1%80%D0%B8%D0%BC%D0%B5%D1%87%D0%B0%D0%BD%D0%B8%D0%B5-%D0%9D%D0%B5%D0%BA%D0%BE%D1%82%D0%BE%D1%80%D1%8B%D0%B5-2"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="paragraph">  
</div><div class="admonitionblock note"><table><tbody><tr><td class="icon"><div class="title">Примечание</div></td><td class="content">Некоторые текстовые редакторы, ориентированные на программистов, предварительно заполняют новый `.spec` файл с их собственным шаблоном спецификации. `rpmdev-newspec` предоставляет независимый от редактора метод, именно поэтому он используется в этом руководстве.</td></tr></tbody></table>

</div><div class="listingblock"><div class="content">  
</div></div></div></div></div></div></div>```
$ cd ~/rpmbuild/SPECS

$ rpmdev-newspec bello
bello.spec created; type minimal, rpm version >= 4.11.

$ rpmdev-newspec cello
cello.spec created; type minimal, rpm version >= 4.11.

$ rpmdev-newspec pello
pello.spec created; type minimal, rpm version >= 4.11.
```

<div id="bkmrk--68"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="listingblock"><div class="content">  
</div></div><div class="paragraph">  
</div></div></div></div></div></div>`~/rpmbuild/SPECS/` каталог теперь имеет три SPEC файла с именами `bello.spec`, `cello.spec`, и `pello.spec`.

Изучите файлы. Директивы в них представляют собой директивы, описанные в разделе [Что такое SPEC файл](https://rpm-packaging-guide-ru.github.io/#what-is-a-spec-file). В следующих разделах Вы заполните эти файлы спецификаций.

<div id="bkmrk-%D0%9F%D1%80%D0%B8%D0%BC%D0%B5%D1%87%D0%B0%D0%BD%D0%B8%D0%B5-%D0%A3%D1%82%D0%B8%D0%BB%D0%B8%D1%82%D0%B0-r"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="paragraph">  
</div><div class="admonitionblock note"><table><tbody><tr><td class="icon"><div class="title">Примечание</div></td><td class="content">Утилита `rpmdev-newspec` не использует рекомендации или соглашения, характерные для какого-либо конкретного дистрибутива Linux. Однако этот документ предназначен для Fedora, CentOS и RHEL, поэтому В ы заметите, что:

<div class="ulist">- Используйте `rm $RPM_BUILD_ROOT` при сборке на *CentOS* (версии, предшествующие версии 7.0) или на [Fedora](https://getfedora.org/) (версии, предшествующие версии 18).
- Мы предпочитаем использовать обозначение `%{buildroot}` вместо `$RPM_BUILD_ROOT` при обращении к Buildroot RPM для обеспечения согласованности со всеми другими определенными или предоставленными макросами во всем файле спецификации..

</div></td></tr></tbody></table>

</div><div class="paragraph">  
</div></div></div></div></div></div>Ниже приведены три примера. Каждый из них полностью описан, так что вы можете перейти к конкретному, если он соответствует вашим потребностям в упаковке. Или прочтите их все, чтобы полностью изучить упаковку различных видов программного обеспечения.

<div id="bkmrk-%D0%98%D0%BC%D1%8F-%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D1%8B-%D0%9E%D0%B1%D1%8A%D1%8F%D1%81%D0%BD"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="paragraph">  
</div><table class="tableblock frame-all grid-all stretch"><colgroup> <col style="width: 15%;"></col> <col style="width: 85%;"></col> </colgroup><tbody><tr><td class="tableblock halign-left valign-top">Имя программы

</td><td class="tableblock halign-left valign-top">Объяснение примера

</td></tr><tr><td class="tableblock halign-left valign-top">bello

</td><td class="tableblock halign-left valign-top">Программа, написанная на необработанном интерпретируемом языке программирования. Пример демонстрирует, когда исходный код не нужно собирать, а нужно только установить. Если необходимо упаковать предварительно скомпилированный бинарный файл, Вы также можете использовать этот метод.

</td></tr><tr><td class="tableblock halign-left valign-top">pello

</td><td class="tableblock halign-left valign-top">Программа, написанная на интерпретируемом языке программирования с последующей байт-компиляцией. Пример демонстрирует байт-компиляцию исходного кода и установку байт-кода - результирующих, предварительно оптимизированных файлов.

</td></tr><tr><td class="tableblock halign-left valign-top">cello

</td><td class="tableblock halign-left valign-top">Программа, написанная на изначально скомпилированном языке программирования. Пример демонстрирует общий процесс компиляции исходного кода в машинный код и установки результирующих исполняемых файлов.

</td></tr></tbody></table>

<div class="sect4">  
</div></div></div></div></div></div>##### bello

Первый SPEC файл создан для bash скрипта `bello` из раздела [Подготовка программного обеспечения для упаковки](https://rpm-packaging-guide-ru.github.io/#preparing-software-for-packaging).

Убедитесь, что у вас есть:

<div id="bkmrk-%D0%9F%D0%B5%D1%80%D0%B5%D0%BC%D0%B5%D1%81%D1%82%D0%B8%D1%82%D0%B5-%D0%B8%D1%81%D1%85%D0%BE%D0%B4%D0%BD%D1%8B%D0%B9"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="paragraph">  
</div><div class="olist arabic">1. Переместите исходный код `bello` в `~/rpmbuild/SOURCES/`. См. [Работа со SPEC файлом](https://rpm-packaging-guide-ru.github.io/#working-with-spec-files).
2. Теперь создайте пустой SPEC файл`~/rpmbuild/SPECS/bello.spec`. Файл будет иметь следующее содержание:
    
    ```
    Name:           bello
    Version:
    Release:        1%{?dist}
    Summary:
    
    License:
    URL:
    Source0:
    
    BuildRequires:
    Requires:
    
    %description
    
    %prep
    %setup -q
    
    %build
    %configure
    make %{?_smp_mflags}
    
    %install
    rm -rf $RPM_BUILD_ROOT
    %make_install
    
    %files
    %doc
    
    %changelog
    * Tue May 31 2016 Adam Miller <maxamillion@fedoraproject.org>
    -
    ```

</div><div class="paragraph">  
</div></div></div></div></div></div></div>Теперь измените `~/rpmbuild/SPECS/bello.spec` для создания RPMs пакета `bello`:

<div id="bkmrk-%D0%97%D0%B0%D0%BF%D0%BE%D0%BB%D0%BD%D0%B8%D1%82%D0%B5-%D0%BF%D0%BE%D0%BB%D1%8Fname%2C-"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="paragraph">  
</div><div class="olist arabic">1. Заполните поля`Name`, `Version`, `Release`, и `Summary` :
    
    <div class="ulist">
    - Поле `Name` уже было указано в качестве аргумента для `rpmdev-newspec`.
    - Установите `Version` в соответствии с “upstream” версией исходного кода `bello`, `0.1`.
    - `Release` автоматически установит `1%{?dist}`, что изначально равно `1`. Увеличивайте это значение при каждом обновлении пакета без изменения `Version`, например, при добавлениии патча. Сбросьте `Release` до `1`, когда произойдёт новый выпуск новой версии программы. Например, если будет выпущена bello версии `0.2`. Макрос *disttag* более подробно описан в части про [RPM Макросы](https://rpm-packaging-guide-ru.github.io/#rpm-macros).
    - `Summary` - это краткое, однострочное объяснение того, что представляет собой это программное обеспечение.
        
        После Ваших изменений первый раздел SPEC файла примет следующий вид:
        
        ```
        Name:           bello
        Version:        0.1
        Release:        1%{?dist}
        Summary:        Hello World example implemented in bash script
        ```
    
    </div>
2. Заполните поля `License`, `URL`, и `Source0`:
    
    <div class="ulist">
    - Поле `License` это [Лицензия на программное обеспечение](https://en.wikipedia.org/wiki/Software_license) связанная с исходным кодом из upstream-выпуска.
        
        Для корректного заполнения поля `License`, обратитесь к: [Fedora Руководство по лицензированию](https://fedoraproject.org/wiki/Licensing:Main)
    
    </div>Например, используйте `GPLv3+`.
    
    +
    
    <div class="ulist">
    - Поле URL - это URL-адрес страницы upstream-программного обеспечения. Для примера, используем `<a class="bare" href="https://example.com/bello">https://example.com/bello</a>`. В данном поле рекомендуется использовать макрос %{name}, тогда адрес примет следующий вид: `<a class="bare" href="https://example.com/%{name}">https://example.com/%{name}</a>`.
    - Поле `Source0` содержит URL-адрес upstream-исходного кода программного обеспечения. Он должен быть напрямую связан с версией программного обеспечения, которое упаковывается. В этом примере мы можем использовать `<a class="bare" href="https://example.com/bello/releases/bello-0.1.tar.gz">https://example.com/bello/releases/bello-0.1.tar.gz</a>`. Используйте макросы %{name} и %{version} для учета изменений в версии. В результате адрес примет вид: `<a class="bare" href="https://example.com/%{name}/releases/%{name}-%{version}.tar.gz">https://example.com/%{name}/releases/%{name}-%{version}.tar.gz</a>`.
        
        После Ваших изменений первая секция SPEC файла примет вид:
        
        ```
        Name:           bello
        Version:        0.1
        Release:        1%{?dist}
        Summary:        Hello World example implemented in bash script
        
        License:        GPLv3+
        URL:            https://example.com/%{name}
        Source0:        https://example.com/%{name}/release/%{name}-%{version}.tar.gz
        ```
    
    </div>
3. Заполните директивы `BuildRequires` и `Requires` и подключите директиву `BuildArch`:
    
    <div class="ulist">
    - `BuildRequires`- определяет зависимости для пакета во время сборки. Для `bello` нет этапа сборки, потому что bash - это интерпретируемый язык программирования, и файлы просто устанавливаются в их расположение в системе. Просто удалите эту директиву.
    - `Requires` задает зависимости для пакета во время выполнения, то-есть, необходимые пакеты для работы программы. Для выполнения скрипта `bello` требуется только оболочка `bash`, поэтому укажите bash в этой директиве.
    - Поскольку это программное обеспечение, написанное на интерпретируемом языке программирования без скомпилированных расширений, добавьте директиву `BuildArch` со значением`noarch`. Это говорит RPM о том, что этот пакет не нужно привязывать к архитектуре процессора, на которой он построен.
        
        После Ваших изменений первая секция SPEC файла примет вид:
        
        ```
        Name:           bello
        Version:        0.1
        Release:        1%{?dist}
        Summary:        Hello World example implemented in bash script
        
        License:        GPLv3+
        URL:            https://example.com/%{name}
        Source0:        https://example.com/%{name}/release/%{name}-%{version}.tar.gz
        
        Requires:       bash
        
        BuildArch:      noarch
        ```
    
    </div>
4. Заполните поля `%description`, `%prep`, `%build`, `%install`, `%files`, and `%license`. Эти директивы являются заголовками секций, поскольку они определяют многостроковые, скриптовые или состоящие из нескольких инструкций задачи.
    
    <div class="ulist">
    - `%description` - это более длинное и полное описание программного обеспечения, чем `Summary`, содержащее один или несколько абзацев. В нашем примере мы будем использовать только краткое описание.
    - В разделе `%prep` указывается, как подготовить среду сборки. Обычно это включает в себя расширение сжатых архивов исходного кода, применение исправлений и, возможно, анализ информации, предоставленной в исходном коде, для использования в следующей части SPEC файла. В этом разделе мы просто используем встроенный макрос `%setup -q`.
    - Секция `%build` определяет, как на самом деле создавать программное обеспечение, которое мы упаковываем. Однако, поскольку `bash` не нужно создавать, просто удалите то, что было предоставлено шаблоном, и оставьте этот раздел пустым.
    - Секция `%install` содержит инструкции для `rpmbuild` о том, как установить программное обеспечение после его сборки в каталог `BUILDROOT`. Этот каталог представляет собой пустой базовый каталог [chroot](https://en.wikipedia.org/wiki/Chroot) ,который напоминает корневой каталог конечного пользователя. Здесь мы должны создать любые каталоги, которые будут содержать установленные файлы.
        
        Поскольку для установки `bello` нам нужно только создать каталог назначения и установить туда исполняемый `bash` скрипт, мы будем использовать команду `install`. Макросы RPM позволяют нам делать это без жесткого кодирования путей.
        
        Секция `%install` после Ваших изменений должен выглядеть следующим образом:
        
        ```
        %install
        
        mkdir -p %{buildroot}/%{_bindir}
        
        install -m 0755 %{name} %{buildroot}/%{_bindir}/%{name}
        ```
    - В секци `%files` указывается список файлов, предоставляемых этим RPM, и их полный путь в системе конечного пользователя. Следовательно, путь устанавливаемого файла `bello` - это `/usr/bin/bello`, или, с помощью макросов RPM, `%{_bindir}/%{name}`.
        
        В этом разделе Вы можете указать роль различных файлов с помощью встроенных макросов. Это полезно для запроса метаданных с помощью команд `rpm`. Например, чтобы указать, что файл LICENSE является файлом лицензии на программное обеспечение, мы используем макрос %license.
        
        После изменения, секция`%files` примет следующий вид:
        
        ```
        %files
        %license LICENSE
        %{_bindir}/%{name}
        ```
    
    </div>
5. Последняя секция, `%changelog`, представляет собой список записей с отметкой даты для каждой версии выпуска пакета. Они регистрируют изменения упаковки, а не изменения программного обеспечения. Примеры изменений упаковки: добавление исправления, изменение процедуры сборки в `%build`.
    
    Следуйте следующему формату для первой строки:
    
    `* Day-of-Week Month Day Year Name Surname <email> - Version-Release`
    
    Следуйте данным правилам для фактической записи изменений:
    
    <div class="openblock"><div class="content"><div class="ulist">
    - Каждая запись об изменении может содержать несколько элементов - по одному для каждого изменения
    - Каждый элемент начинается с новой строки.
    - Каждый элемент начинается с символа `-`.
    
    </div></div></div>Пример записи с отметкой даты
    
    ```
    %changelog
    * Tue May 31 2016 Adam Miller <maxamillion@fedoraproject.org> - 0.1-1
    - First bello package
    - Example second item in the changelog for version-release 0.1-1
    ```

</div><div class="paragraph">  
</div></div></div></div></div></div></div>Вы написали целый файл спецификации **bello**. Послный SPEC файл **bello** теперь выглядит так:

<div id="bkmrk--69"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="paragraph">  
</div><div class="listingblock"><div class="content">  
</div></div></div></div></div></div></div></div>```
Name:           bello
Version:        0.1
Release:        1%{?dist}
Summary:        Hello World example implemented in bash script

License:        GPLv3+
URL:            https://www.example.com/%{name}
Source0:        https://www.example.com/%{name}/releases/%{name}-%{version}.tar.gz

Requires:       bash

BuildArch:      noarch

%description
The long-tail description for our Hello World Example implemented in
bash script.

%prep
%setup -q

%build

%install

mkdir -p %{buildroot}/%{_bindir}

install -m 0755 %{name} %{buildroot}/%{_bindir}/%{name}

%files
%license LICENSE
%{_bindir}/%{name}

%changelog
* Tue May 31 2016 Adam Miller <maxamillion@fedoraproject.org> - 0.1-1
- First bello package
- Example second item in the changelog for version-release 0.1-1
```

<div id="bkmrk--70"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="listingblock"><div class="content">  
</div></div><div class="paragraph">  
</div></div></div></div></div></div></div>В следующем разделе рассказывается о том, как собрать RPM.

##### pello

Наш второй SPEC будет для примера, написанного на языке программирования [Python](https://www.python.org/), который Вы скачали (или создали имитированный upstream- выпуск в разделе [Подготовка программного обеспечения](https://rpm-packaging-guide-ru.github.io/#preparing-software-for-packaging)) и разместили его исходный код в `~/rpmbuild/SOURCES/`. Давайте продолжим и откроем файл `~/rpmbuild/SPECS/pello.spec`, и начнём заполнять некоторые поля.

Прежде чем мы начнем идти по этому пути, нам нужно рассмотреть кое-что несколько уникальное в интерпретируемом программном обеспечении с последующей компиляцией в байт-код. Поскольку мы будем использовать компиляцию в байт-код, [shebang](https://en.wikipedia.org/wiki/Shebang_%28Unix%29) больше не применим, поскольку результирующий файл не будет содержать эту запись. Общепринятой практикой является либо использование сценария оболочки без компиляции в байт-код, который будет вызывать исполняемый файл, либо наличие небольшого фрагмента кода [Python](https://www.python.org/) , который не скомпилирован в байт-код, в качестве “точки входа” в выполнение программы. Это может показаться глупым для нашего небольшого примера, но для больших программных проектов со многими тысячами строк кода увеличение производительности при предварительной компиляции в байт-код является значительным.

<div id="bkmrk-%D0%9F%D1%80%D0%B8%D0%BC%D0%B5%D1%87%D0%B0%D0%BD%D0%B8%D0%B5-%D0%A1%D0%BE%D0%B7%D0%B4%D0%B0%D0%BD%D0%B8%D0%B5--1"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="paragraph">  
</div><div class="admonitionblock note"><table><tbody><tr><td class="icon"><div class="title">Примечание</div></td><td class="content">Создание скрипта для вызова байт-скомпилированного кода или наличие небайт-скомпилированной точки входа в программное обеспечение - это то, к чему разработчики upstream программного обеспечения чаще всего обращаются перед выпуском своего программного обеспечения в мир, однако это не всегда так, и это упражнение призвано помочь решить, что делать в таких ситуациях. Для получения дополнительной информации о том, как обычно выпускается и распространяется код [Python](https://www.python.org/), пожалуйста, обратитесь к следующей документации: [Упаковка и распространение программного обеспечения](https://docs.python.org/2/library/distribution.html).</td></tr></tbody></table>

</div><div class="paragraph">  
</div></div></div></div></div></div></div>Мы создадим небольшой сценарий оболочки для вызова нашего байт-скомпилированного кода, который станет точкой входа в наше программное обеспечение. Мы сделаем это как часть самого нашего файла спецификации, чтобы продемонстрировать, как вы можете создавать сценарии действий внутри SPEC файла. Мы рассмотрим эти особенности позже в разделе `%install`.

Давайте продолжим и откроем файл `~/rpmbuild/SPECS/pello.spec` и начнем заполнять некоторые поля.

Ниже приведен шаблон вывода, который мы получили из`rpmdev-newspec`.

<div id="bkmrk--71"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="paragraph">  
</div><div class="listingblock"><div class="content">  
</div></div></div></div></div></div></div></div>```
Name:           pello
Version:
Release:        1%{?dist}
Summary:

License:
URL:
Source0:

BuildRequires:
Requires:

%description

%prep
%setup -q

%build
%configure
make %{?_smp_mflags}

%install
rm -rf $RPM_BUILD_ROOT
%make_install

%files
%doc

%changelog
* Tue May 31 2016 Adam Miller <maxamillion@fedoraproject.org>
-
```

<div id="bkmrk--72"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="listingblock"><div class="content">  
</div></div><div class="paragraph">  
</div></div></div></div></div></div></div>Как и в первом примере, давайте начнем с первого набора директив, которые `rpmdev-newspec`сгруппировал в верхней части файла: `Name`, `Version`, `Release`, `Summary`. Поле `Name` уже заполнено, так как мы передали его в командной строке при использовании команды `rpmdev-newspec`.

Давайте установим `Version` соответствующую версии “upstream” релиза исходного кода *pello* , которая, как мы видим, равна `0.1.1`, как указано в примире кода, который мы загрузили (или создали в разделе [Подготовка программного обеспечения](https://rpm-packaging-guide-ru.github.io/#preparing-software-for-packaging) section).

В поле `Release` уже установлено значение `1%{?dist}` которое изначально равно `1`, и должно увеличиваться каждый раз, когда пакет обнавляется по какой-либо причине, например, включает новый патч для устранения проблемы, но не имеет новой версии upstream-выпуска. Когда происходит новый upstream-выпуск (например, была выпущена версия pello `0.1.2`) тогда `Release` должен быть сброшен до значения `1`. *disttag*`%{?dist}` выглядит знакоммо по описанию макросов из [RPM Макросы](https://rpm-packaging-guide-ru.github.io/#rpm-macros) в предыдущем разделе.

Поле `Summary` должно представлять собой краткое, в одну строку, объяснение того, что представляет собой это программное обеспечение.

После Ваших изменений первый раздел SPEC файла примет следующий вид:

<div id="bkmrk--73"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="paragraph">  
</div><div class="listingblock"><div class="content">  
</div></div></div></div></div></div></div></div>```
Name:           pello
Version:        0.1.1
Release:        1%{?dist}
Summary:        Hello World example implemented in Python
```

<div id="bkmrk--74"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="listingblock"><div class="content">  
</div></div><div class="paragraph">  
</div></div></div></div></div></div></div>Теперь давайте перейдем ко второму набору директив, которые `rpmdev-newspec` сгруппировал вместе в нашем SPEC файле: `License`, `URL`, `Source0`.

Поле `License` - это [Лицензия на программное обеспечение](https://en.wikipedia.org/wiki/Software_license) , связанная с исходным кодом из upstream выпуска. Точный формат обозначения лицензии в вашем файле SPEC будет варьироваться в зависимости от того, каким конкретным рекомендациям по дистрибутиву [Linux](https://en.wikipedia.org/wiki/Linux), использующему RPM, Вы следуете. Мы будем использовать стандарты обозначения из [Fedora Руководство по лицензированию](https://fedoraproject.org/wiki/Licensing:Main), поэтому это поле будет содержать лицензию `GPLv3+`

Поле `URL` - это веб-сайт upstream программного обеспечения. Это не ссылка на скачивание исходного кода, а фактический веб-сайт проекта, продукта или компании, где кто-то может найти больше информации о конкретной части программного обеспечения. Поскольку это просто пример, мы будем использовать адрес `<a class="bare" href="https://example.com/pello">https://example.com/pello</a>`. Однако, мы применим макрос RPM `%{name}` для корректности оформления.

Поле `Source0` - это место, откуда должен быть загружен upstream исходный код программного обеспечения. Этот URL-адрес должен содержать прямую ссылку на конкретную версию выпуска исходного кода, которую мы упаковываем. Еще раз, поскольку это пример, мы будем использовать ссылку на следующий архив: `<a class="bare" href="https://example.com/pello/releases/pello-0.1.1.tar.gz">https://example.com/pello/releases/pello-0.1.1.tar.gz</a>`.

Мы должны отметить, что в этом примере URL-адреса есть жёстко закодированные значения, которые можно изменить в будущем, и потенциально они даже могут измениться, например, версия выпуска `0.1.1`. Мы можем упростить это, если потребуется обновить только одно поле в SPEC файле и разрешить его повторное использование. Мы будем использовать макросы `<a class="bare" href="https://example.com/%{name}/releases/%{name}-%{version}.tar.gz">https://example.com/%{name}/releases/%{name}-%{version}.tar.gz</a>` вместо ссылок из примеров раннее.

После ваших изменений верхняя часть Вашего SPEC файла должна выглядеть следующим образом:

<div id="bkmrk--75"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="paragraph">  
</div><div class="listingblock"><div class="content">  
</div></div></div></div></div></div></div></div>```
Name:           pello
Version:        0.1.1
Release:        1%{?dist}
Summary:        Hello World example implemented in Python

License:        GPLv3+
URL:            https://example.com/%{name}
Source0:        https://example.com/%{name}/release/%{name}-%{version}.tar.gz
```

<div id="bkmrk--76"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="listingblock"><div class="content">  
</div></div><div class="paragraph">  
</div></div></div></div></div></div></div>У нас есть секции `BuildRequires` и `Requires`, каждая из которых определяет что-то, что требуется для пакета. Однако , `BuildRequires` должен сообщать `rpmbuild` о том, что необходимо Вашему пакету во время **сборки**, а`Requires` - это то, что необходимо Вашему пакету во время **установки**.

В этом примере нам понадобится пакет `python` для выполнения процесса сборки с компиляцией в байт-код. Этот пакет понадобится во время выполнения скомпилированного байт-кода, поэтому нам необходимо определить `python` как требуемый пакет в директиве `Requires`. Нам также понадобится пакет `bash` для выполнения небольшого сценария точки входа, который мы будем использовать здесь.

<div id="bkmrk--77"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="paragraph">  
</div><div class="literalblock"><div class="content">  
</div></div></div></div></div></div></div></div>```
Поскольку эта программа написана на интерпритируемом языке программирования без изначально скомпилированных расширений, нужно добавить секцию ``BuildArch``. В ней задано значение noarch, чтобы сообщить RPM, что этот пакет не нужно привязывать к архитектуре процессора, на которой он построен.
```

<div id="bkmrk--78"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="literalblock"><div class="content">  
</div></div><div class="paragraph">  
</div></div></div></div></div></div></div>После Ваших изменений верхняя часть Вашего SPEC файла должна выглядеть следующим образом:

<div id="bkmrk--79"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="paragraph">  
</div><div class="listingblock"><div class="content">  
</div></div></div></div></div></div></div></div>```
Name:           pello
Version:        0.1.1
Release:        1%{?dist}
Summary:        Hello World example implemented in Python

License:        GPLv3+
URL:            https://example.com/%{name}
Source0:        https://example.com/%{name}/release/%{name}-%{version}.tar.gz

BuildRequires:  python
Requires:       python
Requires:       bash

BuildArch:      noarch
```

<div id="bkmrk--80"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="listingblock"><div class="content">  
</div></div><div class="paragraph">  
</div></div></div></div></div></div></div>Следующие директивы можно рассматривать как “заголовки разделов”, поскольку они являются директивами, которые могут определять многостроковые, скриптовые или состоящие из нескольких инструкций задачи. Мы пройдемся по ним одна за другой, как и по предыдущим пунктам.

Секция `%description` - это более длинное и полное описание программного обеспечения, чем `Summary`, содержащее один или несколько абзацев. В нашем примере мы будем использовать только краткое описание. Эта секция не будет содержать глубокое описание, но при желании раздел может быть целым абзацем или более.

Секция `%prep` - это место, где мы *подготавливаем* нашу среду сборки или рабочее пространство для сборки. Чаще всего здесь происходит расширение сжатых архивов исходного кода, применение исправлений и, возможно, анализ информации, предоставленной в исходном коде, которая необходима в следующей части SPEC файла. В этом разделе мы просто будем использовать предоставленный макрос `%setup -q`.

Секция `%build`- это раздел, где мы рассказываем системе, как на самом деле собирать программное обеспечение, которое мы упаковываем. Здесь мы выполним компиляцию нашего программного обеспечения в байт-код. Для тех, кто читал раздел [Подготовка программного обеспечения](https://rpm-packaging-guide-ru.github.io/#preparing-software-for-packaging), эта часть примера должна показаться знакомой.

Секция `%build` нашего SPEC файла должна выглядеть следующим образом:

<div id="bkmrk--81"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="paragraph">  
</div><div class="listingblock"><div class="content">  
</div></div></div></div></div></div></div></div>```
%build

python -m compileall pello.py
```

<div id="bkmrk--82"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="listingblock"><div class="content">  
</div></div><div class="paragraph">  
</div></div></div></div></div></div></div>Секция `%install` - это раздел, отвечающий за инструктирование `rpmbuild`, устанавливающее наше ранее созданное программное обеспечение в `BUILDROOT`, который фактически является базовым каталогом [chroot](https://en.wikipedia.org/wiki/Chroot) , в котором ничего нет, и нам нужно будет создать любые пути или иерархии каталогов, которые нам понадобятся, чтобы установить наше программное обеспечение в определенных местах. Однако наши макросы RPM помогают нам выполнить эту задачу без необходимости жестко кодировать пути.

Ранее мы обсуждали, что, поскольку мы потеряем контекст файла со строкой [shebang](https://en.wikipedia.org/wiki/Shebang_%28Unix%29) в нём при компиляции в байт-код, нам нужно будет создать простой сценарий-оболочку для выполнения этой задачи. Есть много вариантов того, как это сделать, включая, но не ограничиваясь этим, создание отдельного скрипта и использование его в качестве отдельной директивы `SourceX`, а также вариант, который мы собираемся показать в этом примере, который заключается в сборке файла в строке в SPEC файле. Причина, по которой мы показываем примерный вариант, заключается в том, чтобы просто продемонстрировать, что сам файл спецификации доступен для сценариев. Мы собираемся создать небольшой “сценарий-оболочку”, который будет выполнять скомпилированный байт-код [Python](https://www.python.org/), используя [here document](https://en.wikipedia.org/wiki/Here_document) . Нам также нужно будет установить скомпилированный байт-код в каталог библиотеки в системе, чтобы к нему можно было получить доступ.

<div id="bkmrk-%D0%9F%D1%80%D0%B8%D0%BC%D0%B5%D1%87%D0%B0%D0%BD%D0%B8%D0%B5-%D0%9D%D0%B8%D0%B6%D0%B5-%D0%92%D1%8B-%D0%B7"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="paragraph">  
</div><div class="admonitionblock note"><table><tbody><tr><td class="icon"><div class="title">Примечание</div></td><td class="content">Ниже Вы заметите, что мы жестко кодируем путь к библиотеке. Существуют различные методы, позволяющие избежать необходимости делать это, многие из которых рассматриваются в [\[дополнительных разделах\]](https://rpm-packaging-guide-ru.github.io/#%D0%B4%D0%BE%D0%BF%D0%BE%D0%BB%D0%BD%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D1%8B%D1%85%20%D1%80%D0%B0%D0%B7%D0%B4%D0%B5%D0%BB%D0%B0%D1%85), в разделе [Подрбнее о макросах](https://rpm-packaging-guide-ru.github.io/#more-on-macros), и специфичны для языка программирования, на котором было написано упаковываемое программное обеспечение. В этом примере мы жестко закодировали путь для простоты, чтобы не охватывать слишком много тем одновременно.</td></tr></tbody></table>

</div><div class="paragraph">  
</div></div></div></div></div></div></div>Секция `%install` после Ваших изменений должна выглядеть следующим образом:

<div id="bkmrk--83"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="paragraph">  
</div><div class="listingblock"><div class="content">  
</div></div></div></div></div></div></div></div>```
%install

mkdir -p %{buildroot}/%{_bindir}
mkdir -p %{buildroot}/usr/lib/%{name}

cat > %{buildroot}/%{_bindir}/%{name} <<-EOF
#!/bin/bash
/usr/bin/python /usr/lib/%{name}/%{name}.pyc
EOF

chmod 0755 %{buildroot}/%{_bindir}/%{name}

install -m 0644 %{name}.py* %{buildroot}/usr/lib/%{name}/
```

<div id="bkmrk--84"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="listingblock"><div class="content">  
</div></div><div class="paragraph">  
</div></div></div></div></div></div></div>Секция `%files` - это место, где мы предоставляем список файлов, которые предоставляет этот RPM и где они должны находиться в системе, на которую установлен RPM. Обратите внимание, что это относится не к `%{buildroot}`, а к полному пути к файлам, поскольку ожидается, что они будут существовать в конечной системе после установки. Таким образом, список устанавливаемого файла `pello` будет: `%{_bindir}/pello`. Нам также нужно будет предоставить список `%dir`, чтобы определить, что этот пакет “владеет” каталогом библиотеки, который мы создали, а также всеми файлами, которые мы разместили в нём.

Кроме того, в этом разделе Вам иногда понадобится встроенный макрос для предоставления контекста файла. Это может быть полезно для системных администраторов и конечных пользователей, которые могут захотеть запросить систему о конечном пакете с помощью `rpm`. Встроенный макрос, который мы будем использовать здесь, - это `%license`, который сообщит `rpmbuild`, что это файл лицензии на программное обеспечение в метаданных манифеста файла пакета.

Секция `%files` после Ваших изменений должен выглядеть следующим образом:

<div id="bkmrk--85"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="paragraph">  
</div><div class="listingblock"><div class="content">  
</div></div></div></div></div></div></div></div>```
%files
%license LICENSE
%dir /usr/lib/%{name}/
%{_bindir}/%{name}
/usr/lib/%{name}/%{name}.py*
```

<div id="bkmrk--86"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="listingblock"><div class="content">  
</div></div><div class="paragraph">  
</div></div></div></div></div></div></div>Последняя секция, `%changelog`, представляет собой список записей с отметками о дате, которые соотносятся с конкретной версией-выпуском пакета. Это не журнал изменений в программном обеспечении от выпуска к выпуску, а конкретно изменения в упаковке. Например, если программное обеспечение в пакете нуждалось в исправлении или было необходимо внести изменения в процедуру сборки, указанную в секции`%build`, эта информация будет размещена здесь. Каждая запись изменения может содержать несколько элементов, и каждый элемент должен начинаться с новой строки и символа `-`. Ниже приведен наш пример записи:

<div id="bkmrk--87"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="paragraph">  
</div><div class="listingblock"><div class="content">  
</div></div></div></div></div></div></div></div>```
%changelog
* Tue May 31 2016 Adam Miller <maxamillion@fedoraproject.org> - 0.1.1-1
- First pello package
- Example second item in the changelog for version-release 0.1.1-1
```

<div id="bkmrk--88"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="listingblock"><div class="content">  
</div></div><div class="paragraph">  
</div></div></div></div></div></div></div>Обратите внимание на приведенный выше формат: отметка даты будет начинаться с символа `*`, за которым следует календарный день недели, месяц, день месяца, год, затем контактная информация для упаковщика RPM. Оттуда у нас есть символ`-` перед выпуском версии, что является часто используемым, но не строго регламентированным. Затем, наконец, Версия-Релиз.

Вот и все! Мы написали целый файл спецификаций для **pello**! В следующем разделе мы расскажем, как создать RPM!

Полный файл спецификации теперь должен выглядеть следующим образом:

<div id="bkmrk--89"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="paragraph">  
</div><div class="listingblock"><div class="content">  
</div></div></div></div></div></div></div></div>```
Name:           pello
Version:        0.1.1
Release:        1%{?dist}
Summary:        Hello World example implemented in python

License:        GPLv3+
URL:            https://www.example.com/%{name}
Source0:        https://www.example.com/%{name}/releases/%{name}-%{version}.tar.gz

BuildRequires:  python
Requires:       python
Requires:       bash

BuildArch:      noarch

%description
The long-tail description for our Hello World Example implemented in
Python.

%prep
%setup -q

%build

python -m compileall %{name}.py

%install

mkdir -p %{buildroot}/%{_bindir}
mkdir -p %{buildroot}/usr/lib/%{name}

cat > %{buildroot}/%{_bindir}/%{name} <<-EOF
#!/bin/bash
/usr/bin/python /usr/lib/%{name}/%{name}.pyc
EOF

chmod 0755 %{buildroot}/%{_bindir}/%{name}

install -m 0644 %{name}.py* %{buildroot}/usr/lib/%{name}/

%files
%license LICENSE
%dir /usr/lib/%{name}/
%{_bindir}/%{name}
/usr/lib/%{name}/%{name}.py*

%changelog
* Tue May 31 2016 Adam Miller <maxamillion@fedoraproject.org> - 0.1.1-1
  - First pello package
```

<div id="bkmrk--90"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="listingblock"><div class="content">  
</div></div></div><div class="sect4">  
</div></div></div></div></div></div>##### cello

Наш третий SPEC файл будет для нашего примера на языке [C](https://en.wikipedia.org/wiki/C_%28programming_language%29) , для которого мы ранее создали имитированную версию upstream (или вы скачали) и разместили его исходный код в `~/rpmbuild/SOURCES/`.

Давайте откроем файл `~/rpmbuild/SPECS/cello.spec` и начнём заполнять некоторые поля.

Ниже приведен шаблон вывода, который мы получили от `rpmdev-newspec`.

<div id="bkmrk--91"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="paragraph">  
</div><div class="listingblock"><div class="content">  
</div></div></div></div></div></div></div></div>```
Name:           cello
Version:
Release:        1%{?dist}
Summary:

License:
URL:
Source0:

BuildRequires:
Requires:

%description

%prep
%setup -q

%build
%configure
make %{?_smp_mflags}

%install
rm -rf $RPM_BUILD_ROOT
%make_install

%files
%doc

%changelog
* Tue May 31 2016 Adam Miller <maxamillion@fedoraproject.org>
-
```

<div id="bkmrk--92"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="listingblock"><div class="content">  
</div></div><div class="paragraph">  
</div></div></div></div></div></div></div>Как и в предыдущих примерах, давайте начнем с первого набора директив, которые `rpmdev-newspec` сгруппировал в верхней части файла: `Name`, `Version`, `Release`, `Summary`. The `Name` уже указано, потому что мы предоставили эту информацию в командной строке для `rpmdev-newspec`.

Давайте установим в поле `Version` значение, соответствующее “upstream” версии исходного кода *cello*, которая, как мы видим, равна `1.0`, как указано в примере кода, который мы загрузили (или создали в секции [Подготовка программного обеспечения](https://rpm-packaging-guide-ru.github.io/#preparing-software-for-packaging)).

В `Release` уже установлено значение `1%{?dist}` числовое значение, которое изначально равно `1`, должно увеличиваться каждый раз, когда пакет обновляется по какой-либо причине, например, включает новый патч для устранения проблемы, но не имеет новой версии upstream выпуска. Когда происходит новый upstream выпуск (например, была выпущена версия cello `2.0`), тогда значение `Release` должно быть сброшено до`1`. *disttag*`%{?dist}` выглядит знакоммо по описанию макросов из [RPM Макросы](https://rpm-packaging-guide-ru.github.io/#rpm-macros) в предыдущем разделе.

`Summary` должно представлять собой краткое, в одну строку, объяснение того, что представляет собой это программное обеспечение.

После ваших изменений первый раздел SPEC файла должен выглядеть следующим образом:

<div id="bkmrk--93"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="paragraph">  
</div><div class="listingblock"><div class="content">  
</div></div></div></div></div></div></div></div>```
Name:           cello
Version:        1.0
Release:        1%{?dist}
Summary:        Hello World example implemented in C
```

<div id="bkmrk--94"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="listingblock"><div class="content">  
</div></div><div class="paragraph">  
</div></div></div></div></div></div></div>Теперь давайте перейдем ко второму набору директив, которые `rpmdev-newspec` сгруппировал вместе в нашем SPEC файле: `License`, `URL`, `Source0`. Однако, мы добавим одну директиву в эту группу, поскольку она тесно связана с `Source0` , и это наш `Patch0` в котором будет указан первый патч, который нам нужен для нашего программного обеспечения.

Поле `License` - это [Лицензия на программное обеспечение](https://en.wikipedia.org/wiki/Software_license), связанная с исходным кодом из upstream выпуска. Точный формат обозначения лицензии в вашем SPEC файле будет варьироваться в зависимости от того, каким конкретным рекомендациям по дистрибутиву [Linux](https://en.wikipedia.org/wiki/Linux), использующим RPM, Вы следуете. Мы будем использовать стандарты обозначения из [Fedora. Руководство по лицензированию](https://fedoraproject.org/wiki/Licensing:Main), поэтому это поле будет содержать лицензию `GPLv3+`

Поле `URL` - это веб-сайт upstream программного обеспечения. Это не ссылка на скачивание исходного кода, а фактический веб-сайт проекта, продукта или компании, где кто-то может найти больше информации об этой конкретной части программного обеспечения. Поскольку это просто пример, мы будем использовать адрес. `<a class="bare" href="https://example.com/%D1%81ello">https://example.com/сello</a>`. Однако, мы применим макрос RPM `%{name}` для корректности оформления.

Поле `Source0` - это место, откуда должен быть загружен upstream исходный код программного обеспечения. Этот URL-адрес должен содержать прямую ссылку на конкретную версию выпуска исходного кода, которую мы упаковываем. Еще раз, поскольку это пример, мы будем использовать ссылку на следующий архив: `<a class="bare" href="https://example.com/cello/releases/cello-1.0.tar.gz">https://example.com/cello/releases/cello-1.0.tar.gz</a>`

Мы должны отметить, что в этом примере URL-адреса есть жестко закодированные значения, которые можно изменить в будущем и потенциально они даже могут измениться, например, версия выпуска `1.0`. Мы можем упростить это, если потребуется обновить только одно поле в SPEC файле и разрешить его повторное использование. Мы будем использовать макросы `<a class="bare" href="https://example.com/%{name}/releases/%{name}-%{version}.tar.gz">https://example.com/%{name}/releases/%{name}-%{version}.tar.gz</a>`, вместо ссылок из примеров раннее.

Следующий пункт - предоставить список для файла `.patch` который мы создали ранее, чтобы мы могли применить его к коду позже в секции`%prep`. Нам понадобится список `Patch0: cello-output-first-patch.patch`.

После Ваших изменений верхняя часть SPEC файла должна выглядеть следующим образом:

<div id="bkmrk--95"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="paragraph">  
</div><div class="listingblock"><div class="content">  
</div></div></div></div></div></div></div></div>```
Name:           cello
Version:        1.0
Release:        1%{?dist}
Summary:        Hello World example implemented in C

License:        GPLv3+
URL:            https://example.com/%{name}
Source0:        https://example.com/%{name}/release/%{name}-%{version}.tar.gz

Patch0:         cello-output-first-patch.patch
```

<div id="bkmrk--96"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="listingblock"><div class="content">  
</div></div><div class="paragraph">  
</div></div></div></div></div></div></div>У нас есть секции `BuildRequires` и `Requires`, каждая из которых определяет что-то, что требуется для пакета. Однако, `BuildRequires` должен сообщать `rpmbuild`, что необходимо Вашему пакету во время **сборки**, а`Requires` - это то, что необходимо пакету во время **установки**.

В этом примере нам понадобятся пакеты `gcc` и `make` для выполнения процесса сборки и компиляции. Требования времени выполнения, к счастью, обрабатываются `rpmbuild`, потому что эта программа не требует ничего за пределами основных стандартных библиотек [C](https://en.wikipedia.org/wiki/C_%28programming_language%29) , и поэтому нам не нужно будет определять что-либо вручную в качестве `Requires` , и мы можем опустить эту директиву.

После Ваших изменений верхняя часть SPEC Вашего файла должна выглядеть следующим образом:

<div id="bkmrk--97"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="paragraph">  
</div><div class="listingblock"><div class="content">  
</div></div></div></div></div></div></div></div>```
Name:           cello
Version:        0.1
Release:        1%{?dist}
Summary:        Hello World example implemented in C

License:        GPLv3+
URL:            https://example.com/%{name}
Source0:        https://example.com/%{name}/release/%{name}-%{version}.tar.gz

BuildRequires:  gcc
BuildRequires:  make
```

<div id="bkmrk--98"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="listingblock"><div class="content">  
</div></div><div class="paragraph">  
</div></div></div></div></div></div></div>Следующие директивы являются заголовками секций, поскольку они определяют многостроковые, скриптовые или состоящие из нескольких инструкций задачи. Мы пройдемся по ним один за другим, как и по предыдущим пунктам.

Секция `%description` - это более длинное и полное описание программного обеспечения, чем `Summary`, содержащее один или несколько абзацев. В нашем примере мы будем использовать только краткое описание. В нашем примере это секция не будет содержать глубокое описание, но при желании этот раздел может быть целым абзацем и более.

Секция `%prep` - это место, где мы *подготавливаем* нашу среду сборки или рабочее пространство для сборки. Чаще всего здесь происходит расширение сжатых архивов исходного кода, применение исправлений и, возможно, анализ информации, предоставленной в исходном коде, которая необходима в следующей части SPEC файла. В этом разделе мы просто будем использовать предоставленный макрос `%setup -q`.

Секция `%build` это то, где мы рассказываем системе, как на самом деле собирать программное обеспечение, которое мы упаковываем. Поскольку мы написали простой `Makefile` для нашей реализации на [C](https://en.wikipedia.org/wiki/C_%28programming_language%29) , мы можем просто использовать команду [GNU make](http://www.gnu.org/software/make/): `rpmdev-newspec`. Однако нам нужно удалить вызов, `%configure`, поскольку мы не предоставили [configure script](https://en.wikipedia.org/wiki/Configure_script) . Секция `%build` нашего SPEC файла должна выглядеть следующим образом.

<div id="bkmrk--99"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="paragraph">  
</div><div class="listingblock"><div class="content">  
</div></div></div></div></div></div></div></div>```
%build
make %{?_smp_mflags}
```

<div id="bkmrk--100"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="listingblock"><div class="content">  
</div></div><div class="paragraph">  
</div></div></div></div></div></div></div>Секция `%install` - это то, где мы инструктируем `rpmbuild` как установить наше программное обеспечение в `BUILDROOT`, который фактически является базовым каталогом [chroot](https://en.wikipedia.org/wiki/Chroot) , в котором ничего нет, и нам нужно будет создать любые пути или иерархии каталогов, которые нам понадобятся, чтобы установить наше программное обеспечение. Однако наши макросы RPM помогают нам выполнить эту задачу без необходимости жестко кодировать пути.

Еще раз, поскольку у нас есть простой `Makefile` , шаг установки можно легко выполнить, оставив на месте макрос `%make_install` , который снова был предоставлен нам командой `rpmdev-newspec`.

Секция `%install` после Ваших изменений должна принять следующий вид:

<div id="bkmrk--101"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="paragraph">  
</div><div class="listingblock"><div class="content">  
</div></div></div></div></div></div></div></div>```
%install
%make_install
```

<div id="bkmrk--102"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="listingblock"><div class="content">  
</div></div><div class="paragraph">  
</div></div></div></div></div></div></div>Секция `%files` - это место, где мы предоставляем список файлов, которые предоставляет этот RPM, и где они должны находиться в системе. Обратите внимание, что это относится не к `%{buildroot}`, а к полному пути к файлам, поскольку ожидается, что они будут существовать в конечной системе после установки. Таким образом, путь устанавливаемого файла `cello` будет: `%{_bindir}/cello`.

Кроме того, в этом разделе Вам иногда понадобится встроенный макрос для предоставления контекста для файла. Это может быть полезно для системных администраторов и конечных пользователей, которые могут захотеть запросить систему с помощью `rpm` о конечном пакете. Встроенный макрос, который мы будем использовать здесь, это `%license`, который сообщит `rpmbuild`, что это файл лицензии на программное обеспечение в метаданных.

Секция `%files` после Ваших изменений должна выглядеть следующим образом:

<div id="bkmrk--103"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="paragraph">  
</div><div class="listingblock"><div class="content">  
</div></div></div></div></div></div></div></div>```
%files
%license LICENSE
%{_bindir}/%{name}
```

<div id="bkmrk--104"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="listingblock"><div class="content">  
</div></div><div class="paragraph">  
</div></div></div></div></div></div></div>Последняя секция, `%changelog`, представляет собой список записей с отметками о дате, которые соотносятся с конкретной версией-выпуском пакета. Это не журнал изменений в программном обеспечении от выпуска к выпуску, а конкретно изменения в упаковке. Например, если программное обеспечение в пакете нуждалось в исправлении или было необходимо внести изменения в процедуру сборки, указанную в секции`%build`, эта информация будет размещена здесь. Каждая запись изменения может содержать несколько элементов, и каждый элемент должен начинаться с новой строки и символа `-`. Ниже приведен наш пример записи:

<div id="bkmrk--105"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="paragraph">  
</div><div class="listingblock"><div class="content">  
</div></div></div></div></div></div></div></div>```
%changelog
* Tue May 31 2016 Adam Miller <maxamillion@fedoraproject.org> - 0.1-1
- First cello package
```

<div id="bkmrk--106"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="listingblock"><div class="content">  
</div></div><div class="paragraph">  
</div></div></div></div></div></div></div>Обратите внимание на приведенный выше формат, отметка даты будет начинаться с символа `*`, за которым следует календарный день недели, месяц, день месяца, год, затем контактная информация для упаковщика RPM. Оттуда у нас есть символ`-` перед выпуском версии, что является часто используемым, но не строго регламентированным. Затем, наконец, Версия-Релиз.

Вот и все! Мы написали целый файл спецификаций для **cello**!

Полный файл спецификации теперь должен выглядеть следующим образом:

<div id="bkmrk--107"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="paragraph">  
</div><div class="listingblock"><div class="content">  
</div></div></div></div></div></div></div></div>```
Name:           cello
Version:        1.0
Release:        1%{?dist}
Summary:        Hello World example implemented in C

License:        GPLv3+
URL:            https://www.example.com/%{name}
Source0:        https://www.example.com/%{name}/releases/%{name}-%{version}.tar.gz

Patch0:         cello-output-first-patch.patch

BuildRequires:  gcc
BuildRequires:  make

%description
The long-tail description for our Hello World Example implemented in
C.

%prep
%setup -q

%patch0

%build
make %{?_smp_mflags}

%install
%make_install

%files
%license LICENSE
%{_bindir}/%{name}

%changelog
* Tue May 31 2016 Adam Miller <maxamillion@fedoraproject.org> - 1.0-1
- First cello package
```

<div id="bkmrk--108"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="listingblock"><div class="content">  
</div></div><div class="paragraph">  
</div></div></div></div></div></div></div>Пакет `rpmdevtools` предоставляет набор шаблонов файлов спецификаций для нескольких популярных языков в каталоге`/etc/rpmdevtools/`.

### Сборка RPMS

RPMs собираются с помощью команды `rpmbuild`. Различные сценарии и желаемые результаты требуют различных комбинаций аргументов для `rpmbuild`. В этом разделе описываются два основных сценария:

<div id="bkmrk-%D1%81%D0%B1%D0%BE%D1%80%D0%BA%D0%B0-%D0%B8%D1%81%D1%85%D0%BE%D0%B4%D0%BD%D0%BE%D0%B3%D0%BE-rpm"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="paragraph">  
</div><div class="olist arabic">1. сборка исходного RPM
2. сборка бинарного RPM

</div><div class="paragraph">  
</div></div></div></div></div>Команда `rpmbuild` ожидает определенную структуру каталогов и файлов. Это та же структура, что и в утилите `rpmdev-setuptree`. Предыдущие инструкции также подтвердили требуемую структуру.

#### Исходный RPMs

Зачем создавать исходный RPM (SRPM)?

<div id="bkmrk-%D0%A7%D1%82%D0%BE%D0%B1%D1%8B-%D1%81%D0%BE%D1%85%D1%80%D0%B0%D0%BD%D0%B8%D1%82%D1%8C-%D1%82%D0%BE%D1%87%D0%BD"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="paragraph">  
</div><div class="olist arabic">1. Чтобы сохранить точный источник определенного Name-Version-Release RPM, который был развернут в среде. Это включает в себя точный SPEC файл, исходный код и все соответствующие исправления. Это полезно для просмотра истории и для отладки.
2. Чтобы иметь возможность создавать бинарный RPM на другой аппаратной платформе или [архитектуре](https://en.wikipedia.org/wiki/Microarchitecture).

</div><div class="sect4">  
</div></div></div></div></div></div>##### Для сборки SRPM:

```
$ rpmbuild -bs _SPECFILE_
```

<div id="bkmrk--109"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="listingblock"><div class="content">  
</div></div><div class="paragraph">  
</div></div></div></div></div></div></div>Замените *SPECFILE* именем SPEC файла. Параметр `-bs` "исходный код сборки".

Здесь мы собираем SRPMs для `bello`, `pello` и `cello`:

<div id="bkmrk--110"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="paragraph">  
</div><div class="listingblock"><div class="content">  
</div></div></div></div></div></div></div></div>```
$ cd ~/rpmbuild/SPECS/

$ rpmbuild -bs bello.spec
Wrote: /home/admiller/rpmbuild/SRPMS/bello-0.1-1.el7.src.rpm

$ rpmbuild -bs pello.spec
Wrote: /home/admiller/rpmbuild/SRPMS/pello-0.1.1-1.el7.src.rpm

$ rpmbuild -bs cello.spec
Wrote: /home/admiller/rpmbuild/SRPMS/cello-1.0-1.el7.src.rpm
```

<div id="bkmrk--111"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="listingblock"><div class="content">  
</div></div><div class="paragraph">  
</div></div></div></div></div></div></div>Обратите внимание, что SRPMS были помещены в каталог `rpmbuild/SRPMS`, который является частью структуры, ожидаемой `rpmbuild`.

Это все, что нужно для сборки SRPM.

#### Бинарный RPMS

Существует два метода сборки бинарных RPMs:

<div id="bkmrk-%D0%92%D0%BE%D1%81%D1%81%D1%82%D0%B0%D0%BD%D0%BE%D0%B2%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5-%D0%B5%D0%B3%D0%BE-%D0%B8"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="paragraph">  
</div><div class="olist arabic">1. Восстановление его из SRPM с использованием комманды`rpmbuild --rebuild`.
2. Собираем его из файла спецификации с помощью команды `rpmbuild -bb`. Опция `-bb` означает "собрать бинарный файл" (`build binary`).

</div><div class="sect4">  
</div></div></div></div></div></div>##### Восстановление из исходного RPM

Чтобы перестроить `bello`, `pello` и `cello` из исходных RPM (SRPMs), запустите:

<div id="bkmrk--112"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="paragraph">  
</div><div class="listingblock"><div class="content">  
</div></div></div></div></div></div></div></div>```
$ rpmbuild --rebuild ~/rpmbuild/SRPMS/bello-0.1-1.el7.src.rpm
[output truncated]

$ rpmbuild --rebuild ~/rpmbuild/SRPMS/pello-0.1.1-1.el7.src.rpm
[output truncated]

$ rpmbuild --rebuild ~/rpmbuild/SRPMS/cello-1.0-1.el7.src.rpm
[output truncated]
```

<div id="bkmrk--113"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="listingblock"><div class="content">  
</div></div><div class="paragraph">  
</div></div></div></div></div></div></div>Теперь Вы собрали RPM. Несколько заметок:

<div id="bkmrk-%D0%92%D1%8B%D1%85%D0%BE%D0%B4%D0%BD%D1%8B%D0%B5-%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D0%B5%2C-%D0%B3%D0%B5%D0%BD"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="paragraph">  
</div><div class="ulist">- Выходные данные, генерируемые при сборке бинарного RPM, являются подробными, что полезно для отладки. Выходные данные различаются для разных примеров и соответствуют их SPEC файлам.
- Конечные бинарные RPM находятся в `~/rpmbuild/RPMS/YOURARCH`, где `YOURARCH` - это Ваша [архитектура](https://en.wikipedia.org/wiki/Microarchitecture) , или в `~/rpmbuild/RPMS/noarch/`, если пакет не зависит от архитектуры.
- Вызов `rpmbuild --rebuild` включает в себя:
    
    <div class="openblock"><div class="content"><div class="olist arabic">
    1. Установку содержимого RPM - файла спецификации и исходного кода - в каталог `~/rpmbuild/`.
    2. Сборка с использованием установленного содержимого.
    3. Удаление файла спецификации и исходного кода.
    
    </div></div></div>Вы можете сохранить файл спецификации и исходный код после сборки. Для этого у Вас есть два варианта:
    
    <div class="openblock"><div class="content"><div class="ulist">
    - При сборке используйте опцию `--recompile` вместо `--rebuild`.
    - Установите SRPMS с помощью следующих команд:
    
    </div></div></div>```
    $ rpm -Uvh ~/rpmbuild/SRPMS/bello-0.1-1.el7.src.rpm
    Updating / installing...
       1:bello-0.1-1.el7                  ################################# [100%]
    
    $ rpm -Uvh ~/rpmbuild/SRPMS/pello-0.1.1-1.el7.src.rpm
    Updating / installing...
       1:pello-0.1.1-1.el7                ################################# [100%]
    
    $ rpm -Uvh ~/rpmbuild/SRPMS/cello-1.0-1.el7.src.rpm
    Updating / installing...
       1:cello-1.0-1.el7                  ################################# [100%]
    ```
    
    В этом руководстве выполните приведенные выше команды `rpm -Uvh` чтобы продолжить взаимодействие с файлами спецификаций и исходными кодами.

</div></div><div class="sect4">  
</div></div></div></div></div></div>##### Создание бинарного файла из SPEC файла

Чтобы собрать `bello`, `pello`, и `cello` из их SPEC файлов, запустите:

<div id="bkmrk--114"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="paragraph">  
</div><div class="listingblock"><div class="content">  
</div></div></div></div></div></div></div></div>```
$ rpmbuild -bb ~/rpmbuild/SPECS/bello.spec

$ rpmbuild -bb ~/rpmbuild/SPECS/pello.spec

$ rpmbuild -bb ~/rpmbuild/SPECS/cello.spec
```

<div id="bkmrk--115"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="listingblock"><div class="content">  
</div></div><div class="paragraph">  
</div></div></div></div></div></div></div>Теперь Вы собрали RPM из SPEC файлов.

Большая часть информации, содержащейся в разделе [Восстановление из исходного RPM](https://rpm-packaging-guide-ru.github.io/#rebuild) применима здесь.

### Проверка RPMs на корректность

После создания упаковки хорошо бы проверить её качество. Качество пакета, а не программного обеспечения, поставляемого в нём. Основным инструментом для этого является [rpmlint](https://github.com/rpm-software-management/rpmlint). Это улучшает редактируемость RPM и обеспечивает проверку работоспособности и ошибок путем выполнения статического анализа RPM. Эта утилита может проверять бинарные RPM, исходные RPM (SRPMs) и spec файлы, поэтому она полезна на всех этапах упаковки, как показано в следующих примерах.

Обратите внимание, что `rpmlint` имеет очень строгие правила, и иногда допустимо и необходимо пропустить некоторые из его ошибок и предупреждений, как показано в следующих примерах.

<div id="bkmrk-%D0%9F%D1%80%D0%B8%D0%BC%D0%B5%D1%87%D0%B0%D0%BD%D0%B8%D0%B5-%D0%92-%D0%BF%D1%80%D0%B8%D0%BC%D0%B5%D1%80%D0%B0"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="paragraph">  
</div><div class="admonitionblock note"><table><tbody><tr><td class="icon"><div class="title">Примечание</div></td><td class="content">В примерах мы запускаем `rpmlint` без каких-либо опций, что приводит к невербальному выводу. Для получения подробных объяснений каждой ошибки или предупреждения вместо этого запустите `rpmlint -i` instead.</td></tr></tbody></table>

</div><div class="sect3">  
</div></div></div></div></div>#### Проверка SPEC файла bello

Это результат выполнения `rpmlint` в SPEC файле `bello`:

<div id="bkmrk--116"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="paragraph">  
</div><div class="listingblock"><div class="content">  
</div></div></div></div></div></div></div>```
$ rpmlint bello.spec
bello.spec: W: invalid-url Source0: https://www.example.com/bello/releases/bello-0.1.tar.gz HTTP Error 404: Not Found
0 packages and 1 specfiles checked; 0 errors, 1 warnings.
```

<div id="bkmrk--117"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="listingblock"><div class="content">  
</div></div><div class="paragraph">  
</div></div></div></div></div></div>Наблюдения:

<div id="bkmrk-%D0%94%D0%BB%D1%8F-bello.spec-%D0%B5%D1%81%D1%82%D1%8C-"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="paragraph">  
</div><div class="ulist">- Для `bello.spec` есть только одно предупреждение. В нем говорится, что URL-адрес, указанный в директиве `Source0` недоступен. Это ожидаемо, поскольку указанный `example.com` URL-адрес не существует. Предполагая, что мы ожидаем, что этот URL-адрес будет работать в будущем, мы можем проигнорировать это предупреждение

</div><div class="paragraph">  
</div></div></div></div></div></div>Это результат выполнения `rpmlint` на SRPM для `bello`:

<div id="bkmrk--118"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="paragraph">  
</div><div class="listingblock"><div class="content">  
</div></div></div></div></div></div></div>```
$ rpmlint ~/rpmbuild/SRPMS/bello-0.1-1.el7.src.rpm
bello.src: W: invalid-url URL: https://www.example.com/bello HTTP Error 404: Not Found
bello.src: W: invalid-url Source0: https://www.example.com/bello/releases/bello-0.1.tar.gz HTTP Error 404: Not Found
1 packages and 0 specfiles checked; 0 errors, 2 warnings.
```

<div id="bkmrk--119"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="listingblock"><div class="content">  
</div></div><div class="paragraph">  
</div></div></div></div></div></div>Наблюдения:

<div id="bkmrk-%D0%94%D0%BB%D1%8F-bello-srpm-%D0%BF%D0%BE%D1%8F%D0%B2%D0%B8"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="paragraph">  
</div><div class="ulist">- Для `bello` SRPM появилось новое предупреждение, в котором говорится, что URL-адрес, указанный в директиве URL, недоступен. Предполагая, что ссылка будет работать в будущем, мы можем проигнорировать это предупреждение.

</div></div><div class="sect3">  
</div></div></div></div></div>#### Проверка бинарного RPM bello

При проверке бинарных RPMs, `rpmlint` проверяет дополнительные параметры, в том числе:

<div id="bkmrk-%D0%B4%D0%BE%D0%BA%D1%83%D0%BC%D0%B5%D0%BD%D1%82%D0%B0%D1%86%D0%B8%D1%8E-%D1%81%D1%82%D1%80%D0%B0%D0%BD%D0%B8%D1%86"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="paragraph">  
</div><div class="olist arabic">1. документацию
2. [страницы руководства](https://en.wikipedia.org/wiki/Man_page)
3. корректность [Иерархии файловой системы](https://en.wikipedia.org/wiki/Filesystem_Hierarchy_Standard)

</div><div class="paragraph">  
</div></div></div></div></div></div>Это результат выполнения`rpmlint` на бинарном RPM для `bello`:

<div id="bkmrk--120"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="paragraph">  
</div><div class="listingblock"><div class="content">  
</div></div></div></div></div></div></div>```
$ rpmlint ~/rpmbuild/RPMS/noarch/bello-0.1-1.el7.noarch.rpm
bello.noarch: W: invalid-url URL: https://www.example.com/bello HTTP Error 404: Not Found
bello.noarch: W: no-documentation
bello.noarch: W: no-manual-page-for-binary bello
1 packages and 0 specfiles checked; 0 errors, 3 warnings.
```

<div id="bkmrk--121"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="listingblock"><div class="content">  
</div></div><div class="paragraph">  
</div></div></div></div></div></div>Наблюдения:

<div id="bkmrk-no-documentation-%D0%B8no"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="paragraph">  
</div><div class="ulist">- `no-documentation` и`no-manual-page-for-binary` оворят о том, что в RPM нет документации или страниц руководства, потому что мы их не предоставили.

</div><div class="paragraph">  
</div></div></div></div></div></div>Помимо вышеприведенных предупреждений, наш RPM проходит проверку `rpmlint`.

#### Проверка SPEC файла pello

Это результат выполнения `rpmlint` на SPEC файле `pello`:

<div id="bkmrk--122"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="paragraph">  
</div><div class="listingblock"><div class="content">  
</div></div></div></div></div></div></div>```
$ rpmlint pello.spec
pello.spec:30: E: hardcoded-library-path in %{buildroot}/usr/lib/%{name}
pello.spec:34: E: hardcoded-library-path in /usr/lib/%{name}/%{name}.pyc
pello.spec:39: E: hardcoded-library-path in %{buildroot}/usr/lib/%{name}/
pello.spec:43: E: hardcoded-library-path in /usr/lib/%{name}/
pello.spec:45: E: hardcoded-library-path in /usr/lib/%{name}/%{name}.py*
pello.spec: W: invalid-url Source0: https://www.example.com/pello/releases/pello-0.1.1.tar.gz HTTP Error 404: Not Found
0 packages and 1 specfiles checked; 5 errors, 1 warnings.
```

<div id="bkmrk--123"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="listingblock"><div class="content">  
</div></div><div class="paragraph">  
</div></div></div></div></div></div>Наблюдения:

<div id="bkmrk-%D0%9F%D1%80%D0%B5%D0%B4%D1%83%D0%BF%D1%80%D0%B5%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5-inval"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="paragraph">  
</div><div class="ulist">- Предупреждение `invalid-url Source0` wговорит о том, что URL-адрес, указанный в директиве Source0 - недоступен. Это ожидаемо, поскольку указанный example.com URL-адрес не существует. Предполагая, что мы ожидаем, что этот URL-адрес будет работать в будущем, мы можем проигнорировать это предупреждение.
- Ошибок много, потому что мы намеренно написали этот файл спецификации, чтобы он был простым и показывал, о каких ошибках может сообщать `rpmlint`.
- Ошибки `hardcoded-library-path` предполагают использование макроса `%{_libdir}` вместо жесткого кодирования пути к библиотеке. Ради этого примера мы игнорируем эти ошибки, но для пакетов, запущенных в производство, Вам нужна веская причина для игнорирования этой ошибки.

</div><div class="paragraph">  
</div></div></div></div></div></div>Это результат выполнения `rpmlint` на SRPM `pello`:

<div id="bkmrk--124"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="paragraph">  
</div><div class="listingblock"><div class="content">  
</div></div></div></div></div></div></div>```
$ rpmlint ~/rpmbuild/SRPMS/pello-0.1.1-1.el7.src.rpm
pello.src: W: invalid-url URL: https://www.example.com/pello HTTP Error 404: Not Found
pello.src:30: E: hardcoded-library-path in %{buildroot}/usr/lib/%{name}
pello.src:34: E: hardcoded-library-path in /usr/lib/%{name}/%{name}.pyc
pello.src:39: E: hardcoded-library-path in %{buildroot}/usr/lib/%{name}/
pello.src:43: E: hardcoded-library-path in /usr/lib/%{name}/
pello.src:45: E: hardcoded-library-path in /usr/lib/%{name}/%{name}.py*
pello.src: W: invalid-url Source0: https://www.example.com/pello/releases/pello-0.1.1.tar.gz HTTP Error 404: Not Found
1 packages and 0 specfiles checked; 5 errors, 2 warnings.
```

<div id="bkmrk--125"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="listingblock"><div class="content">  
</div></div><div class="paragraph">  
</div></div></div></div></div></div>Наблюдения:

<div id="bkmrk-%D0%9D%D0%BE%D0%B2%D0%B0%D1%8F-%D0%BE%D1%88%D0%B8%D0%B1%D0%BA%D0%B0invalid-"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="paragraph">  
</div><div class="ulist">- Новая ошибка`invalid-url URL` здесь связана с директивой `URL`, которая недоступна. Предполагая, что мы ожидаем, что URL-адрес станет действительным в будущем, мы можем игнорировать эту ошибку.

</div></div><div class="sect3">  
</div></div></div></div></div>#### Проверка бинарного RPM pello

При проверке бинарного RPMs, `rpmlint` проверяет дополнительные параметры, в том числе:

<div id="bkmrk-%D0%B4%D0%BE%D0%BA%D1%83%D0%BC%D0%B5%D0%BD%D1%82%D0%B0%D1%86%D0%B8%D1%8E-%D1%81%D1%82%D1%80%D0%B0%D0%BD%D0%B8%D1%86-1"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="paragraph">  
</div><div class="olist arabic">1. документацию
2. [страницы руководства](https://en.wikipedia.org/wiki/Man_page)
3. последовательное использование
4. корректность [Иерархии файловой системы](https://en.wikipedia.org/wiki/Filesystem_Hierarchy_Standard)

</div><div class="paragraph">  
</div></div></div></div></div></div>Это результат выполнения `rpmlint` на бинарном RPM для `pello`:

<div id="bkmrk--126"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="paragraph">  
</div><div class="listingblock"><div class="content">  
</div></div></div></div></div></div></div>```
$ rpmlint ~/rpmbuild/RPMS/noarch/pello-0.1.1-1.el7.noarch.rpm
pello.noarch: W: invalid-url URL: https://www.example.com/pello HTTP Error 404: Not Found
pello.noarch: W: only-non-binary-in-usr-lib
pello.noarch: W: no-documentation
pello.noarch: E: non-executable-script /usr/lib/pello/pello.py 0644L /usr/bin/env
pello.noarch: W: no-manual-page-for-binary pello
1 packages and 0 specfiles checked; 1 errors, 4 warnings.
```

<div id="bkmrk--127"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="listingblock"><div class="content">  
</div></div><div class="paragraph">  
</div></div></div></div></div></div>Наблюдения:

<div id="bkmrk-%D0%9F%D1%80%D0%B5%D0%B4%D1%83%D0%BF%D1%80%D0%B5%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D1%8F-no-do"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="paragraph">  
</div><div class="ulist">- Предупреждения `no-documentation` и `no-manual-page-for-binary` говорят о том, что в RPM нет документации или страниц руководства, потому что мы их не предоставили.
- Предупреждение `only-non-binary-in-usr-lib` гласит, что Вы предоставили только бинарные артефакты `/usr/lib/`. Этот каталог обычно зарезервирован для общих объектных файлов, которые являются бинарными файлами. Следовательно, `rpmlint` eожидает, что по крайней мере один или несколько файлов в `/usr/lib/` будут бинарными.
    
    Это пример проверки `rpmlint` на соответствие [Иерархии Файловой Системы](https://en.wikipedia.org/wiki/Filesystem_Hierarchy_Standard) .
    
    Обычно для обеспечения правильного размещения файлов используются макросы RPM. Ради этого примера мы можем проигнорировать это предупреждение.
- Ошибка `non-executable-script` предупреждает о том, что `/usr/lib/pello/pello.py` файл не имеет прав на выполнение. Поскольку этот файл содержит [shebang](https://en.wikipedia.org/wiki/Shebang_%28Unix%29) , `rpmlint` ожидает, что файл будет исполняемым. Для целей примера оставьте этот файл без разрешений на выполнение и проигнорируйте эту ошибку.

</div><div class="paragraph">  
</div></div></div></div></div></div>Помимо вышеприведенных предупреждений и ошибок, наш RPM проходит проверку `rpmlint`.

#### Проверка SPEC файла cello

Это результат выполнения `rpmlint` на SPEC файле `cello`:

<div id="bkmrk--128"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="paragraph">  
</div><div class="listingblock"><div class="content">  
</div></div></div></div></div></div></div>```
$ rpmlint ~/rpmbuild/SPECS/cello.spec
/home/admiller/rpmbuild/SPECS/cello.spec: W: invalid-url Source0: https://www.example.com/cello/releases/cello-1.0.tar.gz HTTP Error 404: Not Found
0 packages and 1 specfiles checked; 0 errors, 1 warnings.
```

<div id="bkmrk--129"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="listingblock"><div class="content">  
</div></div><div class="paragraph">  
</div></div></div></div></div></div>Наблюдения:

<div id="bkmrk-%D0%95%D0%B4%D0%B8%D0%BD%D1%81%D1%82%D0%B2%D0%B5%D0%BD%D0%BD%D0%BE%D0%B5-%D0%BF%D1%80%D0%B5%D0%B4%D1%83%D0%BF%D1%80"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="paragraph">  
</div><div class="ulist">- Единственное предупреждение для `cello.spec` гласит, что URL-адрес, указанный в директиве `Source0`, недоступен. Это ожидаемо, поскольку указанный `example.com` URL-адрес не существует. Предполагая, что мы ожидаем, что этот URL-адрес будет работать в будущем, мы можем проигнорировать это предупреждение.

</div><div class="paragraph">  
</div></div></div></div></div></div>Это результат выполнения `rpmlint` в файле SRPM для `cello`:

<div id="bkmrk--130"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="paragraph">  
</div><div class="listingblock"><div class="content">  
</div></div></div></div></div></div></div>```
$ rpmlint ~/rpmbuild/SRPMS/cello-1.0-1.el7.src.rpm
cello.src: W: invalid-url URL: https://www.example.com/cello HTTP Error 404: Not Found
cello.src: W: invalid-url Source0: https://www.example.com/cello/releases/cello-1.0.tar.gz HTTP Error 404: Not Found
1 packages and 0 specfiles checked; 0 errors, 2 warnings.
```

<div id="bkmrk--131"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="listingblock"><div class="content">  
</div></div><div class="paragraph">  
</div></div></div></div></div></div>Наблюдения:

<div id="bkmrk-%D0%94%D0%BB%D1%8F-cello-srpm-%D0%BF%D0%BE%D1%8F%D0%B2%D0%B8"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="paragraph">  
</div><div class="ulist">- Для `cello` SRPM появилось новое предупреждение, в котором говорится, что URL-адрес, указанный в директиве `URL`, недоступен. Предполагая, что ссылка будет работать в будущем, мы можем проигнорировать это предупреждение.

</div></div><div class="sect3">  
</div></div></div></div></div>#### Проверка бинарного RPM cello

При проверке бинарных RPMs, `rpmlint` проверяет дополнительные параметры, в том числе:

<div id="bkmrk-%D0%B4%D0%BE%D0%BA%D1%83%D0%BC%D0%B5%D0%BD%D1%82%D0%B0%D1%86%D0%B8%D1%8E-%D1%81%D1%82%D1%80%D0%B0%D0%BD%D0%B8%D1%86-2"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="paragraph">  
</div><div class="olist arabic">1. документацию
2. [страницы руководства](https://en.wikipedia.org/wiki/Man_page)
3. корректность [Иерархии файловой системы](https://en.wikipedia.org/wiki/Filesystem_Hierarchy_Standard) .

</div><div class="paragraph">  
</div></div></div></div></div></div>Это результат выполнения `rpmlint` на бинарном RPM для `cello`:

<div id="bkmrk--132"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="paragraph">  
</div><div class="listingblock"><div class="content">  
</div></div></div></div></div></div></div>```
$ rpmlint ~/rpmbuild/RPMS/x86_64/cello-1.0-1.el7.x86_64.rpm
cello.x86_64: W: invalid-url URL: https://www.example.com/cello HTTP Error 404: Not Found
cello.x86_64: W: no-documentation
cello.x86_64: W: no-manual-page-for-binary cello
1 packages and 0 specfiles checked; 0 errors, 3 warnings.
```

<div id="bkmrk--133"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="listingblock"><div class="content">  
</div></div><div class="paragraph">  
</div></div></div></div></div></div>Наблюдения:

<div id="bkmrk-%D0%9F%D1%80%D0%B5%D0%B4%D1%83%D0%BF%D1%80%D0%B5%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D1%8F-no-do-1"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="paragraph">  
</div><div class="ulist">- Предупреждения `no-documentation` и `no-manual-page-for-binary` говорят о том, что в RPM нет документации или страниц руководства, потому что мы их не предоставили.

</div><div class="paragraph">  
</div></div></div></div></div></div>Помимо вышеприведенных предупреждений и ошибок, наш RPM проходит проверку `rpmlint`.

Наши RPM теперь готовы и проверены с помощью `rpmlint`. На этом учебное пособие заканчивается. Для получения дополнительной информации о RPM упаковке перейдите к главе [Дополнительные материалы](https://rpm-packaging-guide-ru.github.io/#advanced-topics).

## Дополнительные материалы

В этой главе рассматриваются темы, которые выходят за рамки вводного руководства, но часто полезны в реальной упаковке RPM.

### Подпись пакетов

Подпись пакета - это способ защитить пакет для конечного пользователя. Безопасная транспортировка может быть достигнута с помощью реализации протокола HTTPS. Такой метод используют, когда пакет загружается непосредственно перед установкой. Однако пакеты часто загружаются заранее и хранятся в локальных репозиториях перед их использованием. Пакеты подписываются, чтобы гарантировать, что никакая третья сторона не сможет изменить содержимое пакета.

Существует три способа подписи пакета:

<div id="bkmrk-%D0%94%D0%BE%D0%B1%D0%B0%D0%B2%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5-%D0%BF%D0%BE%D0%B4%D0%BF%D0%B8%D1%81%D0%B8-%D0%BA"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="paragraph">  
</div><div class="ulist">- [Добавление подписи к уже существующему пакету](https://rpm-packaging-guide-ru.github.io/#Adding-a-Signature-to-a-Package).
- [Замена подписи на уже существующем пакете](https://rpm-packaging-guide-ru.github.io/#Replacing-a-Package-Signature).
- [ Подпись пакета во время сборки](https://rpm-packaging-guide-ru.github.io/#Build-time-Signing).

</div><div class="sect3">  
</div></div></div></div></div>#### Добавление подписи к пакету

В большинстве случаев пакеты создаются без подписи. Подпись добавляется непосредственно перед выпуском пакета.

Чтобы добавить другую подпись к пакету, используйте опцию `--addsign`. Наличие более чем одной подписи позволяет зафиксировать путь владения пакетом от разработчика пакета до конечного пользователя.

В качестве примера подразделение компании создает пакет и подписывает его ключом подразделения. Затем штаб-квартира компании проверяет подпись пакета и добавляет корпоративную подпись к пакету, заявляя, что подписанный пакет является подлинным.

С двумя подписями пакеи попадает к продавцу. Продавец проверяет подписи и, если они проверяются, также добавляет свою подпись.

Теперь пакет отправляется в компанию, которая желает его развернуть. Проверив каждую подпись на упаковке, они знают, что это подлинная копия, не изменившаяся с момента её первого создания. В зависимости от внутреннего контроля внедряющей компании, они могут добавить свою собственную подпись, чтобы заверить своих сотрудников в том, что пакет получил их корпоративное одобрение.

Вывод из команды `--addsign`:

<div id="bkmrk--134"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="paragraph">  
</div><div class="listingblock"><div class="content">  
</div></div></div></div></div></div></div>```
$ rpm --addsign blather-7.9-1.i386.rpm
            Enter pass phrase:

Pass phrase is good.
blather-7.9-1.i386.rpm:
```

<div id="bkmrk--135"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="listingblock"><div class="content">  
</div></div><div class="paragraph">  
</div></div></div></div></div></div>Для проверки подписей пакета с несколькими подписями:

<div id="bkmrk--136"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="paragraph">  
</div><div class="listingblock"><div class="content">  
</div></div></div></div></div></div></div>```
$ rpm --checksig blather-7.9-1.i386.rpm
blather-7.9-1.i386.rpm: size pgp pgp md5 OK
```

<div id="bkmrk--137"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="listingblock"><div class="content">  
</div></div><div class="paragraph">  
</div></div></div></div></div></div>Два обозначения `pgp` в выходных данных команды `rpm --checksig` показывают, что пакет был подписан дважды.

RPM позволяет добавлять одну и ту же подпись несколько раз. Параметр `--addsign` не проверяет наличие нескольких идентичных подписей.

<div id="bkmrk--138"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="paragraph">  
</div><div class="listingblock"><div class="content">  
</div></div></div></div></div></div></div>```
$ rpm --addsig blather-7.9-1.i386.rpm
              Enter pass phrase:

Pass phrase is good.
blather-7.9-1.i386.rpm:
$ rpm --addsig blather-7.9-1.i386.rpm
              Enter pass phrase:

Pass phrase is good.
blather-7.9-1.i386.rpm:
$ rpm --addsig blather-7.9-1.i386.rpm
              Enter pass phrase:

Pass phrase is good.
blather-7.9-1.i386.rpm:
$ rpm --checksig blather-7.9-1.i386.rpm
blather-7.9-1.i386.rpm: size pgp pgp pgp pgp md5 OK
```

<div id="bkmrk--139"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="listingblock"><div class="content">  
</div></div><div class="paragraph">  
</div></div></div></div></div></div>На выходе команды `rpm --checksig` отображается четыре подписи.

#### Замена подписи пакета

Чтобы изменить открытый ключ без необходимости пересобирать каждый пакет, используйте опцию `--resign`.

<div id="bkmrk--140"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="paragraph">  
</div><div class="listingblock"><div class="content">  
</div></div></div></div></div></div></div>```
$ rpm --resign blather-7.9-1.i386.rpm
            Enter pass phrase:

Pass phrase is good.
blather-7.9-1.i386.rpm:
```

<div id="bkmrk--141"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="listingblock"><div class="content">  
</div></div><div class="paragraph">  
</div></div></div></div></div></div>Использование опции `--resign` с несколькими пакетами:

<div id="bkmrk--142"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="paragraph">  
</div><div class="listingblock"><div class="content">  
</div></div></div></div></div></div></div>```
$ rpm --resign b*.rpm
            Enter pass phrase:

Pass phrase is good.
blather-7.9-1.i386.rpm:
bother-3.5-1.i386.rpm:
```

<div id="bkmrk--143"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="listingblock"><div class="content">  
</div></div></div><div class="sect3">  
</div></div></div></div></div>#### Подпись во время сборки

Чтобы подписать пакет во время сборки, используйте команду `rpmbuild` с параметром `--sign`. Для этого необходимо ввести кодовую фразу PGP.

Для примера:

<div id="bkmrk--144"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="paragraph">  
</div><div class="listingblock"><div class="content">  
</div></div></div></div></div></div></div>```
$ rpmbuild -ba --sign blather-7.9.spec
            Enter pass phrase:

Pass phrase is good.
* Package: blather
…
Binary Packaging: blather-7.9-1
Finding dependencies...
…
Generating signature: 1002
Wrote: /usr/src/redhat/RPMS/i386/blather-7.9-1.i386.rpm
…
Source Packaging: blather-7.9-1
…
Generating signature: 1002
Wrote: /usr/src/redhat/SRPMS/blather-7.9-1.src.rpm
```

<div id="bkmrk--145"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="listingblock"><div class="content">  
</div></div><div class="paragraph">  
</div></div></div></div></div></div>Сообщение "Generating signature" появляется как в бинарном, так и в исходном разделах упаковки. Число, следующее за сообщением, указывает на то, что добавленная подпись была создана с использованием PGP.

<div id="bkmrk-%D0%9F%D1%80%D0%B8%D0%BC%D0%B5%D1%87%D0%B0%D0%BD%D0%B8%D0%B5-%D0%9F%D1%80%D0%B8-%D0%B8%D1%81%D0%BF%D0%BE%D0%BB"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="paragraph">  
</div><div class="admonitionblock note"><table><tbody><tr><td class="icon"><div class="title">Примечание</div></td><td class="content">При использовании опции `--sign` в `rpmbuild`, используйте только аргументы `-bb` или `-ba` для сборки пакета. Аргумент `-ba` обозначает сборку бинарных **и** исходных пакетов.

</td></tr></tbody></table>

</div><div class="paragraph">  
</div></div></div></div></div></div>Чтобы проверить подпись пакета, используйте комманду `--checksig`. Для примера:

<div id="bkmrk--146"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="paragraph">  
</div><div class="listingblock"><div class="content">  
</div></div></div></div></div></div></div>```
$ rpm --checksig blather-7.9-1.i386.rpm
blather-7.9-1.i386.rpm: size pgp md5 OK
```

<div id="bkmrk--147"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="listingblock"><div class="content">  
</div></div><div class="sect4">  
</div></div></div></div></div></div>##### Сборка нескольких пакетов

При сборке нескольких пакетов используйте следующий синтаксис, чтобы избежать многократного ввода кодовой фразы PGP. Например, при сборке пакетов `blather` и `bother`, подпишите их, следуя примеру ниже:

<div id="bkmrk--148"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="paragraph">  
</div><div class="listingblock"><div class="content">  
</div></div></div></div></div></div></div></div>```
$ rpmbuild -ba --sign b*.spec
              Enter pass phrase:

Pass phrase is good.
* Package: blather
…
Binary Packaging: blather-7.9-1
…
Generating signature: 1002
Wrote: /usr/src/redhat/RPMS/i386/blather-7.9-1.i386.rpm
…
Source Packaging: blather-7.9-1
…
Generating signature: 1002
Wrote: /usr/src/redhat/SRPMS/blather-7.9-1.src.rpm
…
* Package: bother
…
Binary Packaging: bother-3.5-1
…
Generating signature: 1002
Wrote: /usr/src/redhat/RPMS/i386/bother-3.5-1.i386.rpm
…
Source Packaging: bother-3.5-1
…
Generating signature: 1002
Wrote: /usr/src/redhat/SRPMS/bother-3.5-1.src.rpm
```

<div id="bkmrk--149"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="listingblock"><div class="content">  
</div></div></div></div></div><div class="sect2">  
</div></div></div></div>### Mock

[Mock](https://github.com/rpm-software-management/mock/wiki) - это инструмент для сборки пакетов. Он может создавать пакеты для разных архитектур и разных версий Fedora или RHEL. Mock создает chroots и собирает в них пакеты. Его единственная задача - надежно заполнить chroot и попытаться собрать пакет в этом chroot.

Mock также предлагает многопакетный инструмент `mockchain`, который может собирать цепочки пакетов, зависящих друг от друга.

Mock способен создавать RPM из управления конфигурацией исходного кода, если присутствует пакет `mock-scm`, а затем встраивать SRPM в RPMs. Смотрите `–scm-enable` в документации. (Из upstream документации)

<div id="bkmrk-%D0%9F%D1%80%D0%B8%D0%BC%D0%B5%D1%87%D0%B0%D0%BD%D0%B8%D0%B5-%D0%A7%D1%82%D0%BE%D0%B1%D1%8B-%D0%B8%D1%81%D0%BF"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="paragraph">  
</div><div class="admonitionblock note"><table><tbody><tr><td class="icon"><div class="title">Примечание</div></td><td class="content">Чтобы использовать [Mock](https://github.com/rpm-software-management/mock/wiki) в системе RHEL или CentOS, Вам необходимо включить репозиторий “Extra Packages for Enterprise Linux” ([EPEL](https://fedoraproject.org/wiki/EPEL)) . Это репозиторий, предоставляемый сообществом [Fedora](https://getfedora.org/), содержащий множество полезных инструментов для пакетов RPM, системных администраторов и разработчиков.</td></tr></tbody></table>

</div><div class="paragraph">  
</div></div></div></div></div>Одним из наиболее распространенных вариантов для RPM-упаковщиков, использующих [Mock](https://github.com/rpm-software-management/mock/wiki) , является создание так называемой “нетронутой среды сборки”. При использовании mock в качестве “нетронутой среды сборки”, ничто в текущем состоянии вашей системы не влияет на сам пакет RPM. Mock использует различные конфигурации, чтобы указать, какова “цель” сборки, они находятся в Вашей системе в каталоге`/etc/mock/` (после установки пакета `mock`). Вы можете выполнить сборку для разных дистрибутивов или выпусков, просто указав это в командной строке. Следует иметь в виду, что файлы конфигурации, поставляемые с макетом, предназначены для упаковщиков Fedora RPM, и поэтому выпускаемые версии RHEL и CentOS помечены как “epel” , потому что это «целевой» репозиторий, для которого эти RPM пакеты будут созданы. Вы просто указываете конфигурацию, которую хотите использовать (без расширения файла `.cfg`). Например, вы можете создать наш пример `cello` как для RHEL 7, так и для Fedora 23, используя следующие команды, даже не используя разные машины.

<div id="bkmrk--150"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="paragraph">  
</div><div class="listingblock"><div class="content">  
</div></div></div></div></div></div>```
$ mock -r epel-7-x86_64 ~/rpmbuild/SRPMS/cello-1.0-1.el7.src.rpm

$ mock -r fedora-23-x86_64 ~/rpmbuild/SRPMS/cello-1.0-1.el7.src.rpm
```

<div id="bkmrk--151"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="listingblock"><div class="content">  
</div></div><div class="paragraph">  
</div></div></div></div></div>Один из примеров того, почему Вы можете захотеть использовать `mock` - это если вы упаковывали RPMs на своем ноутбуке, и у Вас был установлен пакет (в этом примере мы назовем его `foo`), который был указан в секции `BuildRequires` того пакета, который Вы собирали, но забыли фактически сделать запись `BuildRequires: foo`. Сборка завершится успешно, когда вы запустите`rpmbuild`, потому что`foo` был необходим для сборки, и он был найден в системе во время сборки. Однако, если Вы перенесёте SRPM в другую систему, в которой отсутствовал `foo`, он выйдет из строя, что вызовет неожиданный побочный эффект. [Mock](https://github.com/rpm-software-management/mock/wiki) решает эту проблему, сначала анализируя содержимое SRPM и устанавливая `BuildRequires` в его [chroot](https://en.wikipedia.org/wiki/Chroot), что означает, что если бы Вам не хватало записи `BuildRequires` , сборка завершилась бы с ошибкой, потому что `mock` не знал бы, как её установить, и поэтому она не присутствовала бы в buildroot.

Другой пример - противоположный сценарий, допустим, Вам нужен `gcc` для сборки пакета, но он не установлен в вашей системе (что маловероятно для RPM-упаковщика, но просто ради примера давайте притворимся, что это правда). С [Mock](https://github.com/rpm-software-management/mock/wiki), Вам не нужно устранавливать `gcc` в Вашей системе, потому что он будет установлен в chroot как часть процесса `mock`.

Ниже приведен пример попытки пересобрать пакет, у которого есть зависимость, которой мне не хватает в моей системе. Главное, что следует отметить, это то, что `gcc` обычно используется в большинстве систем RPM упаковщиками, некоторые пакеты RPM могут содержать более дюжины сборочных запросов, и это позволяет Вам не загромождать свою рабочую станцию ненужными пакетами.

<div id="bkmrk--152"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="paragraph">  
</div><div class="listingblock"><div class="content">  
</div></div></div></div></div></div>```
$ rpmbuild --rebuild ~/rpmbuild/SRPMS/cello-1.0-1.el7.src.rpm
Installing /home/admiller/rpmbuild/SRPMS/cello-1.0-1.el7.src.rpm
error: Failed build dependencies: gcc is needed by cello-1.0-1.el7.x86_64

$ mock -r epel-7-x86_64 ~/rpmbuild/SRPMS/cello-1.0-1.el7.src.rpm
INFO: mock.py version 1.2.17 starting (python version = 2.7.5)...
Start: init plugins
INFO: selinux enabled
Finish: init plugins
Start: run
INFO: Start(/home/admiller/rpmbuild/SRPMS/cello-1.0-1.el7.src.rpm)  Config(epel-7-x86_64)
Start: clean chroot
Finish: clean chroot
Start: chroot init
INFO: calling preinit hooks
INFO: enabled root cache
Start: unpacking root cache
Finish: unpacking root cache
INFO: enabled yum cache
Start: cleaning yum metadata
Finish: cleaning yum metadata
Mock Version: 1.2.17
INFO: Mock Version: 1.2.17
Start: yum update
base                                                                    | 3.6 kB  00:00:00
epel                                                                    | 4.3 kB  00:00:00
extras                                                                  | 3.4 kB  00:00:00
updates                                                                 | 3.4 kB  00:00:00
No packages marked for update
Finish: yum update
Finish: chroot init
Start: build phase for cello-1.0-1.el7.src.rpm
Start: build setup for cello-1.0-1.el7.src.rpm
warning: Could not canonicalize hostname: rhel7
Building target platforms: x86_64
Building for target x86_64
Wrote: /builddir/build/SRPMS/cello-1.0-1.el7.centos.src.rpm
Getting requirements for cello-1.0-1.el7.centos.src
 --> Already installed : gcc-4.8.5-4.el7.x86_64
 --> Already installed : 1:make-3.82-21.el7.x86_64
No uninstalled build requires
Finish: build setup for cello-1.0-1.el7.src.rpm
Start: rpmbuild cello-1.0-1.el7.src.rpm
Building target platforms: x86_64
Building for target x86_64
Executing(%prep): /bin/sh -e /var/tmp/rpm-tmp.v9rPOF
+ umask 022
+ cd /builddir/build/BUILD
+ cd /builddir/build/BUILD
+ rm -rf cello-1.0
+ /usr/bin/gzip -dc /builddir/build/SOURCES/cello-1.0.tar.gz
+ /usr/bin/tar -xf -
+ STATUS=0
+ '[' 0 -ne 0 ']'
+ cd cello-1.0
+ /usr/bin/chmod -Rf a+rX,u+w,g-w,o-w .
Patch #0 (cello-output-first-patch.patch):
+ echo 'Patch #0 (cello-output-first-patch.patch):'
+ /usr/bin/cat /builddir/build/SOURCES/cello-output-first-patch.patch
patching file cello.c
+ /usr/bin/patch -p0 --fuzz=0
+ exit 0
Executing(%build): /bin/sh -e /var/tmp/rpm-tmp.UxRVtI
+ umask 022
+ cd /builddir/build/BUILD
+ cd cello-1.0
+ make -j2
gcc -g -o cello cello.c
+ exit 0
Executing(%install): /bin/sh -e /var/tmp/rpm-tmp.K3i2dL
+ umask 022
+ cd /builddir/build/BUILD
+ '[' /builddir/build/BUILDROOT/cello-1.0-1.el7.centos.x86_64 '!=' / ']'
+ rm -rf /builddir/build/BUILDROOT/cello-1.0-1.el7.centos.x86_64
++ dirname /builddir/build/BUILDROOT/cello-1.0-1.el7.centos.x86_64
+ mkdir -p /builddir/build/BUILDROOT
+ mkdir /builddir/build/BUILDROOT/cello-1.0-1.el7.centos.x86_64
+ cd cello-1.0
+ /usr/bin/make install DESTDIR=/builddir/build/BUILDROOT/cello-1.0-1.el7.centos.x86_64
mkdir -p /builddir/build/BUILDROOT/cello-1.0-1.el7.centos.x86_64/usr/bin
install -m 0755 cello /builddir/build/BUILDROOT/cello-1.0-1.el7.centos.x86_64/usr/bin/cello
+ /usr/lib/rpm/find-debuginfo.sh --strict-build-id -m --run-dwz --dwz-low-mem-die-limit 10000000 --dwz-max-die-limit 110000000 /builddir/build/BUILD/cello-1.0
extracting debug info from /builddir/build/BUILDROOT/cello-1.0-1.el7.centos.x86_64/usr/bin/cello
dwz: Too few files for multifile optimization
/usr/lib/rpm/sepdebugcrcfix: Updated 0 CRC32s, 1 CRC32s did match.
+ /usr/lib/rpm/check-buildroot
+ /usr/lib/rpm/redhat/brp-compress
+ /usr/lib/rpm/redhat/brp-strip-static-archive /usr/bin/strip
+ /usr/lib/rpm/brp-python-bytecompile /usr/bin/python 1
+ /usr/lib/rpm/redhat/brp-python-hardlink
+ /usr/lib/rpm/redhat/brp-java-repack-jars
Processing files: cello-1.0-1.el7.centos.x86_64
Executing(%license): /bin/sh -e /var/tmp/rpm-tmp.vxtAuO
+ umask 022
+ cd /builddir/build/BUILD
+ cd cello-1.0
+ LICENSEDIR=/builddir/build/BUILDROOT/cello-1.0-1.el7.centos.x86_64/usr/share/licenses/cello-1.0
+ export LICENSEDIR
+ /usr/bin/mkdir -p /builddir/build/BUILDROOT/cello-1.0-1.el7.centos.x86_64/usr/share/licenses/cello-1.0
+ cp -pr LICENSE /builddir/build/BUILDROOT/cello-1.0-1.el7.centos.x86_64/usr/share/licenses/cello-1.0
+ exit 0
Provides: cello = 1.0-1.el7.centos cello(x86-64) = 1.0-1.el7.centos
Requires(rpmlib): rpmlib(CompressedFileNames) <= 3.0.4-1 rpmlib(FileDigests) <= 4.6.0-1 rpmlib(PayloadFilesHavePrefix) <= 4.0-1
Requires: libc.so.6()(64bit) libc.so.6(GLIBC_2.2.5)(64bit) rtld(GNU_HASH)
Processing files: cello-debuginfo-1.0-1.el7.centos.x86_64
Provides: cello-debuginfo = 1.0-1.el7.centos cello-debuginfo(x86-64) = 1.0-1.el7.centos
Requires(rpmlib): rpmlib(FileDigests) <= 4.6.0-1 rpmlib(PayloadFilesHavePrefix) <= 4.0-1 rpmlib(CompressedFileNames) <= 3.0.4-1
Checking for unpackaged file(s): /usr/lib/rpm/check-files /builddir/build/BUILDROOT/cello-1.0-1.el7.centos.x86_64
Wrote: /builddir/build/RPMS/cello-1.0-1.el7.centos.x86_64.rpm
warning: Could not canonicalize hostname: rhel7
Wrote: /builddir/build/RPMS/cello-debuginfo-1.0-1.el7.centos.x86_64.rpm
Executing(%clean): /bin/sh -e /var/tmp/rpm-tmp.JuPOtY
+ umask 022
+ cd /builddir/build/BUILD
+ cd cello-1.0
+ /usr/bin/rm -rf /builddir/build/BUILDROOT/cello-1.0-1.el7.centos.x86_64
+ exit 0
Finish: rpmbuild cello-1.0-1.el7.src.rpm
Finish: build phase for cello-1.0-1.el7.src.rpm
INFO: Done(/home/admiller/rpmbuild/SRPMS/cello-1.0-1.el7.src.rpm) Config(epel-7-x86_64) 0 minutes 16 seconds
INFO: Results and/or logs in: /var/lib/mock/epel-7-x86_64/result
Finish: run
```

<div id="bkmrk--153"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="listingblock"><div class="content">  
</div></div><div class="paragraph">  
</div></div></div></div></div>Как Вы можете видеть, `mock` - довольно подробный инструмент. Вы также заметите много выходных данных [yum](http://yum.baseurl.org/) или [dnf](https://github.com/rpm-software-management/dnf) (в зависимости от фиктивной цели RHEL7, CentOS7 или Fedora), которых нет в этом выводе, который был опущен для краткости и часто опускается после того, как Вы выполнили `--init` для mock target. Например `mock -r epel-7-x86_64 --init`, который предварительно загрузит все необходимые пакеты, закэширует их и запустит предварительный этап сборки chroot.

Для получения дополнительной информации, пожалуйста, обратитесь к [Mock](https://github.com/rpm-software-management/mock/wiki) upstream документации.

### Система контроля версий

При работе с RPMs желательно использовать [Системы контроля версий](https://en.wikipedia.org/wiki/Version_control) (VCS), такую как [git](https://git-scm.com/), для управления компонентами программного обеспечения, которое мы упаковываем. Следует отметить, что хранение бинарных файлов в системе контроля версий нецелесообразно, поскольку это резко увеличивает размер исходного репозитория, поскольку эти инструменты разработаны для обработки различий в файлах (часто оптимизированных для текстовых файлов), чему не поддаются бинарные файлы, поэтому обычно сохраняется весь бинарные файл целиком. Существуют некоторые утилиты, популярные среди upstream проектов с открытым исходным кодом, которые решают эту проблему, либо сохраняя SPEC файл, где исходный код находится в VCS (т. е. - он не находится в сжатом архиве для распространения ), либо помещая в VCS только SPEC-файл и патчи и загружается сжатый архив updtream исходного кода в так называемый «кэш просмотра».

В этом разделе мы рассмотрим два различных варианта использования системы контроля версий [git](https://git-scm.com/) для управления содержимым, которое в конечном итоге будет преобразовано в пакет RPM. Первый называется [tito](https://github.com/dgoodwin/tito), второй - [dist-git](https://github.com/release-engineering/dist-git).

<div id="bkmrk-%D0%9F%D1%80%D0%B8%D0%BC%D0%B5%D1%87%D0%B0%D0%BD%D0%B8%D0%B5-%D0%92%D0%B0%D0%BC-%D0%BD%D1%83%D0%B6%D0%BD%D0%BE"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="paragraph">  
</div><div class="admonitionblock note"><table><tbody><tr><td class="icon"><div class="title">Примечание</div></td><td class="content">Вам нужно будет установить пакет `git` в Вашу систему, он понадобится нам для изучения данного раздела.</td></tr></tbody></table>

</div><div class="sect3">  
</div></div></div></div></div>#### tito

<div id="bkmrk-%D1%8D%D1%82%D0%BE-%D1%83%D1%82%D0%B8%D0%BB%D0%B8%D1%82%D0%B0%2C-%D0%BA%D0%BE%D1%82%D0%BE%D1%80%D0%B0%D1%8F"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="ulist">- это утилита, которая предполагает, что весь исходный код программного обеспечения, которое будет упаковано, уже находится в репозитории [git](https://git-scm.com/). Это хорошо для тех, кто практикует рабочий процесс DevOps, поскольку позволяет команде, пишущей программное обеспечение, поддерживать свой нормальный [рабочий процесс ветвления](https://git-scm.com/book/en/v2/Git-Branching-Branching-Workflows). Затем Tito позволит поэтапно упаковывать программное обеспечение, собирать его в автоматическом режиме и по-прежнему обеспечивать собственный процесс установки для системы на основе RPM [RPM](http://rpm.org/).

</div><div class="admonitionblock note"><table><tbody><tr><td class="icon"><div class="title">Примечание</div></td><td class="content">Пакет [tito](https://github.com/dgoodwin/tito) доступен в [Fedora](https://getfedora.org/), а также в репозитории [EPEL](https://fedoraproject.org/wiki/EPEL) для использования на RHEL 7 и CentOS 7.</td></tr></tbody></table>

</div><div class="paragraph">  
</div></div></div></div></div></div>Tito работает на основе тегов [git tags](https://git-scm.com/book/en/v2/Git-Basics-Tagging) и будет управлять тегами для Вас, если Вы решите разрешить это, но при желании может работать по любой схеме тегов, которую Вы предпочитаете, поскольку эта функциональность настраивается.

Давайте немного познакомимся с tito, взглянув на исходный проект, который уже использует его. На самом деле мы будем использовать репозиторий git проекта, который является предметом нашего следующего раздела, [dist-git](https://github.com/release-engineering/dist-git). Поскольку этот проект публично размещен на [GitHub](https://github.com/), давайте клонируем репозиторий git.

<div id="bkmrk--154"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="paragraph">  
</div><div class="listingblock"><div class="content">  
</div></div></div></div></div></div></div>```
$ git clone https://github.com/release-engineering/dist-git.git
Cloning into 'dist-git'...
remote: Counting objects: 425, done.
remote: Total 425 (delta 0), reused 0 (delta 0), pack-reused 425
Receiving objects: 100% (425/425), 268.76 KiB | 0 bytes/s, done.
Resolving deltas: 100% (184/184), done.
Checking connectivity... done.

$ cd dist-git/

$ ls *.spec
dist-git.spec

$ tree rel-eng/
rel-eng/
├── packages
│   └── dist-git
└── tito.props

1 directory, 2 files
```

<div id="bkmrk--155"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="listingblock"><div class="content">  
</div></div><div class="paragraph">  
</div></div></div></div></div></div>Как мы видим, SPEC файл находится в корне репозитория git, и в репозитории есть каталог `rel-eng`, который используется tito для общего учета, настройки и различных дополнительных тем, таких как пользовательские модули tito. В макете каталога мы видим, что есть подкаталог с названием `packages`, в котором будет храниться файл для каждого пакета, которым tito управляет в репозитории, поскольку у Вас может быть много RPM в одном репозитории git, и tito справится с этим просто отлично. Однако, в этом сценарии мы видим только один список пакетов, и следует отметить, что он соответствует имени нашего SPEC файла. Все это настраивается командой `tito init`, когда разработчики [dist-git](https://github.com/release-engineering/dist-git) впервые инициализировали свой репозиторий git для управления tito.

Если бы мы следовали обычному рабочему процессу DevOps Practitioner, мы, вероятно, хотели бы использовать его как часть процесса [Непрерывной интеграции](https://en.wikipedia.org/wiki/Continuous_integration) (CI) или [Непрерывной доставки](https://en.wikipedia.org/wiki/Continuous_delivery) (CD). Что мы можем сделать в этом сценарии, так это выполнить “test build” для tito, мы даже можем использовать mock. Затем мы могли бы использовать выходные данные в качестве точки установки для какого-либо другого компонента в конвейере. Ниже приведен простой пример команд, которые могут это сделать, и их можно адаптировать к другим средам.

<div id="bkmrk--156"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="paragraph">  
</div><div class="listingblock"><div class="content">  
</div></div></div></div></div></div></div>```
$ tito build --test --srpm
Building package [dist-git-0.13-1]
Wrote: /tmp/tito/dist-git-git-0.efa5ab8.tar.gz

Wrote: /tmp/tito/dist-git-0.13-1.git.0.efa5ab8.fc23.src.rpm

$ tito build --builder=mock --arg mock=epel-7-x86_64 --test --rpm
Building package [dist-git-0.13-1]
Creating rpms for dist-git-git-0.efa5ab8 in mock: epel-7-x86_64
Wrote: /tmp/tito/dist-git-git-0.efa5ab8.tar.gz

Wrote: /tmp/tito/dist-git-0.13-1.git.0.efa5ab8.fc23.src.rpm

Using srpm: /tmp/tito/dist-git-0.13-1.git.0.efa5ab8.fc23.src.rpm
Initializing mock...
Installing deps in mock...
Building RPMs in mock...
Wrote:
  /tmp/tito/dist-git-selinux-0.13-1.git.0.efa5ab8.el7.centos.noarch.rpm
  /tmp/tito/dist-git-0.13-1.git.0.efa5ab8.el7.centos.noarch.rpm

$ sudo yum localinstall /tmp/tito/dist-git-*.noarch.rpm
Loaded plugins: product-id, search-disabled-repos, subscription-manager
Examining /tmp/tito/dist-git-0.13-1.git.0.efa5ab8.el7.centos.noarch.rpm: dist-git-0.13-1.git.0.efa5ab8.el7.centos.noarch
Marking /tmp/tito/dist-git-0.13-1.git.0.efa5ab8.el7.centos.noarch.rpm to be installed
Examining /tmp/tito/dist-git-selinux-0.13-1.git.0.efa5ab8.el7.centos.noarch.rpm: dist-git-selinux-0.13-1.git.0.efa5ab8.el7.centos.noarch
Marking /tmp/tito/dist-git-selinux-0.13-1.git.0.efa5ab8.el7.centos.noarch.rpm to be installed
Resolving Dependencies
--> Running transaction check
---> Package dist-git.noarch 0:0.13-1.git.0.efa5ab8.el7.centos will be installed
```

<div id="bkmrk--157"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="listingblock"><div class="content">  
</div></div><div class="paragraph">  
</div></div></div></div></div></div>Обратите внимание, что последняя команда должна быть запущена либо с правами sudo, либо с правами root, и что большая часть выходных данных была опущена для краткости, поскольку список зависимостей довольно длинный.

На этом наш простой пример использования tito заканчивается, но в нём есть много удивительных функций для системных администраторов, разработчиков RPM-пакетов и практиков DevOps. Я бы настоятельно рекомендовал ознакомиться с upstream документацией, найденной на сайте *tito* GitHub, для получения дополнительной информации о том, как быстро начать использовать его для вашего проекта, а также о различных дополнительных функциях, которые он предлагает.

#### dist-git

Утилита [dist-git](https://github.com/release-engineering/dist-git) использует несколько иной подход, чем у [tito](https://github.com/dgoodwin/tito), так что вместо того, чтобы хранить исходный код в [git](https://git-scm.com/), она вместо этого будет хранить файлы спецификаций и патчи в репозитории git и загружать сжатый архив исходного кода в так называемый “look-aside cache”. “Look-aside-cache” - это термин, который был придуман при использовании систем сборки RPM, хранящих большие файлы, подобные этим, “на стороне”. Подобная система, как правило, привязана к правильной системе сборки RPM, такой как [Koji](https://pagure.io/koji). Затем система сборки настраивается на извлечение элементов, которые перечислены в качестве записей `SourceX` в файлах спецификаций из этого внешнего кэша, в то время как SPEC файл и исправления остаются в системе контроля версий. Существует также вспомогательный инструмент командной строки, который поможет в этом.

Чтобы не дублировать документацию для получения дополнительной информации о том, как настроить такую систему, пожалуйста, обратитесь к документации [dist-git](https://github.com/release-engineering/dist-git).

### Подробнее о макросах

Существует множество встроенных макросов RPM, и мы рассмотрим некоторые из них в следующем разделе, однако исчерпывающий список можно найти на странице [RPM Official Documentation](https://rpm-software-management.github.io/rpm/manual/macros.html).

Существуют также макросы, предоставляемые Вашим дистрибутивом [Linux](https://en.wikipedia.org/wiki/Linux), в этом разделе мы рассмотрим некоторые из них, предоставляемые [Fedora](https://getfedora.org/), [CentOS](https://www.centos.org/) и [RHEL](https://www.redhat.com/en/technologies/linux-platforms), а также предоставим информацию о том, как проверить Вашу систему, чтобы узнать о других, которые мы не рассматриваем, или для их обнаружения в других дистрибутивах Linux на основе RPM

#### Определение Ваших Собственных Макросов

Вы можете определить свои собственные макросы. Ниже приводится выдержка из [RPM Official Documentation](https://rpm-software-management.github.io/rpm/manual/macros.html), в которой содержится исчерпывающая информация о возможностях макросов.

Чтобы определить макрос, используйте:

<div id="bkmrk--158"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="paragraph">  
</div><div class="listingblock"><div class="content">  
</div></div></div></div></div></div></div>```
%global <name>[(opts)] <body>
```

<div id="bkmrk--159"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="listingblock"><div class="content">  
</div></div><div class="paragraph">  
</div></div></div></div></div></div>Все пробелы, окружающие `\ `, удаляются. Имя может состоять из буквенно-цифровых символов и символа `_`, и должно иметь длину не менее 3 символов. Макрос без поля `(opts)` является “простым” в том смысле, что выполняется только рекурсивное расширение макроса. Параметризованный макрос содержит поле `(opts)` field. The `opts` - (строка в круглых скобках) передается точно так же, как и в getopt(3) для обработки argc/argv в начале вызова макроса.

<div id="bkmrk-%D0%9F%D1%80%D0%B8%D0%BC%D0%B5%D1%87%D0%B0%D0%BD%D0%B8%D0%B5-%D0%91%D0%BE%D0%BB%D0%B5%D0%B5-%D1%81%D1%82%D0%B0"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="paragraph">  
</div><div class="admonitionblock note"><table><tbody><tr><td class="icon"><div class="title">Примечание</div></td><td class="content">Более старый SPEC файлы RPM могут использовать шаблон макроса `%define <name> <body>`. Различия между макросами `%define` и `%global` заключаются в следующем:

<div class="ulist">- `%define` имеет локальную область действия, что означает, что он применяется только к указанной части SPEC файла. Кроме того, тело макроса `%define` расширяется при использовании.
- `%global` имеет глобальную область действия, что означает, что он применяется ко всему SPEC файлу. Кроме того, тело макроса `%global` асширяется во время определения.

</div></td></tr></tbody></table>

</div><div class="paragraph">  
</div></div></div></div></div></div>Пример:

<div id="bkmrk--160"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="paragraph">  
</div><div class="listingblock"><div class="content">  
</div></div></div></div></div></div></div>```
%global githash 0ec4e58
%global python_sitelib %(%{__python} -c "from distutils.sysconfig import get_python_lib; print(get_python_lib())")
```

<div id="bkmrk-%D0%9F%D1%80%D0%B8%D0%BC%D0%B5%D1%87%D0%B0%D0%BD%D0%B8%D0%B5-%D0%9C%D0%B0%D0%BA%D1%80%D0%BE%D1%81%D1%8B-%D0%B2"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="listingblock"><div class="content">  
</div></div><div class="admonitionblock note"><table><tbody><tr><td class="icon"><div class="title">Примечание</div></td><td class="content">Макросы всегда оцениваются, даже в комментариях. Иногда это безобидно. Но во втором примере мы выполняем команду python, чтобы получить содержимое макроса. Эта команда будет выполняться даже тогда, когда Вы закомментируете макрос, или когда Вы вводите имя макроса в %changelog. Чтобы закомментировать макрос, используйте `%%`. Например: `%%global`.</td></tr></tbody></table>

</div></div><div class="sect3">  
</div></div></div></div></div>#### %setup

Макрос `%setup` можно использовать для сборки пакета с помощью tarball. Стандартное поведение макроса `%setup` можно увидеть в выходных данных `rpmbuild`. В начале каждой фазы макрос выводит `Executing(%something)`. Например:

<div id="bkmrk--161"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="paragraph">  
</div><div class="listingblock"><div class="content">  
</div></div></div></div></div></div></div>```
Executing(%prep): /bin/sh -e /var/tmp/rpm-tmp.DhddsG
```

<div id="bkmrk--162"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="listingblock"><div class="content">  
</div></div><div class="paragraph">  
</div></div></div></div></div></div>Выходные данные оболочки устанавливаются с включенным `set -x`. Чтобы просмотреть содержимое `/var/tmp/rpm-tmp.DhddsG`, используйте опцию `--debug`, поскольку `rpmbuild` удаляет временные файлы после успешной сборки. Здесь отображается настройка переменных среды, например:

<div id="bkmrk--163"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="paragraph">  
</div><div class="listingblock"><div class="content">  
</div></div></div></div></div></div></div>```
cd '/builddir/build/BUILD'
rm -rf 'cello-1.0'
/usr/bin/gzip -dc '/builddir/build/SOURCES/cello-1.0.tar.gz' | /usr/bin/tar -xof -
STATUS=$?
if [ $STATUS -ne 0 ]; then
  exit $STATUS
fi
cd 'cello-1.0'
/usr/bin/chmod -Rf a+rX,u+w,g-w,o-w .
```

<div id="bkmrk--164"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="listingblock"><div class="content">  
</div></div><div class="paragraph">  
</div></div></div></div></div></div>Макрос `%setup` гарантирует, что мы работаем в правильном каталоге, удаляет остатки предыдущих сборок, распаковывает исходный архив и устанавливает некоторые привилегии по умолчанию. Существует несколько вариантов настройки поведения макроса `%setup`.

##### %setup -q

Параметр `-q` ограничивает детализацию макроса `%setup`. Вместо `tar -xof` выполняется только `tar -xvvof`. Этот параметр должен быть использован в качестве первого.

##### %setup -n

В некоторых случаях каталог из расширенного архива имеет другое имя, чем ожидалось `%{name}-%{version}`. Это может привести к ошибке макроса `%setup`. Имя каталога должно быть указано параметром `-n directory_name`.

Например, если имя пакета `cello`, но исходный код заархивирован в `hello-1.0.tgz` и содержит каталог `hello/`, содержимое SPEC файла должно быть следующим:

<div id="bkmrk--165"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="paragraph">  
</div><div class="listingblock"><div class="content">  
</div></div></div></div></div></div></div></div>```
Name: cello
Source0: https://example.com/%{name}/release/hello-%{version}.tar.gz
…
%prep
%setup -n hello
```

<div id="bkmrk--166"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="listingblock"><div class="content">  
</div></div></div><div class="sect4">  
</div></div></div></div></div></div>##### %setup -c

Параметр `-c` можно использовать, если архив исходного кода не содержит никаких подкаталогов и после распаковки файлы из архива заполняют текущий каталог. Опция `-c` создает каталог и переходит к расширению архива. Наглядный пример:

<div id="bkmrk--167"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="paragraph">  
</div><div class="listingblock"><div class="content">  
</div></div></div></div></div></div></div></div>```
/usr/bin/mkdir -p cello-1.0
cd 'cello-1.0'
```

<div id="bkmrk--168"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="listingblock"><div class="content">  
</div></div><div class="paragraph">  
</div></div></div></div></div></div></div>Каталог не изменяется после расширения архива.

##### %setup -D и -T

Параметр `-D` отключает удаление каталога исходного кода. Этот параметр полезен, если макрос `%setup` используется несколько раз. По сути, параметр `-D` означает, что сделующие строки не используются:

<div id="bkmrk--169"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="paragraph">  
</div><div class="listingblock"><div class="content">  
</div></div></div></div></div></div></div></div>```
rm -rf 'cello-1.0'
```

<div id="bkmrk--170"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="listingblock"><div class="content">  
</div></div><div class="paragraph">  
</div></div></div></div></div></div></div>Параметр`-T` отключает расширение хранилища исходного кода, удаляя следующую строку из скрипта:

<div id="bkmrk--171"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="paragraph">  
</div><div class="listingblock"><div class="content">  
</div></div></div></div></div></div></div></div>```
/usr/bin/gzip -dc '/builddir/build/SOURCES/cello-1.0.tar.gz' | /usr/bin/tar -xvvof -
```

<div id="bkmrk--172"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="listingblock"><div class="content">  
</div></div></div><div class="sect4">  
</div></div></div></div></div></div>##### %setup -a и -b

Параметры `-a` и `-b` расширяют определённые источники.

<div id="bkmrk-%D0%9F%D0%B0%D1%80%D0%B0%D0%BC%D0%B5%D1%82%D1%80--b-%28%D1%80%D0%B0%D1%81%D1%88%D0%B8%D1%84%D1%80"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="paragraph">  
</div><div class="ulist">- Параметр `-b` (расшифровывается как `before`) расширяет определенные источники перед входом в рабочий каталог.
- Параметр `-a` (расшифровывается как `after`) расширяет эти источники после входа. Их аргументами являются исходные номера из преамбулы файла спецификации.

</div><div class="paragraph">  
</div></div></div></div></div></div></div>Например, допустим, что архив `cello-1.0.tar.gz` содержит пустой каталог `examples`, и примеры поставляются в отдельных `examples.tar.gz` tarball архивах, и они разархивируются в каталог с тем же именем. В этом случае используйте `-a 1`, так как мы хотим разархивировать `Source1` после входа в рабочий каталог:

<div id="bkmrk--173"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="paragraph">  
</div><div class="listingblock"><div class="content">  
</div></div></div></div></div></div></div></div>```
Source0: https://example.com/%{name}/release/%{name}-%{version}.tar.gz
Source1: examples.tar.gz
…
%prep
%setup -a 1
```

<div id="bkmrk--174"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="listingblock"><div class="content">  
</div></div><div class="paragraph">  
</div></div></div></div></div></div></div>Но если бы примеры были в отдельном `cello-1.0-examples.tar.gz` tarball архиве, который расширяется до `cello-1.0/examples`, используйте параметры `-b 1`, поскольку `Source1` должен быть разархивирован перед входом в рабочий каталог:

<div id="bkmrk--175"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="paragraph">  
</div><div class="listingblock"><div class="content">  
</div></div></div></div></div></div></div></div>```
Source0: https://example.com/%{name}/release/%{name}-%{version}.tar.gz
Source1: %{name}-%{version}-examples.tar.gz
…
%prep
%setup -b 1
```

<div id="bkmrk--176"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="listingblock"><div class="content">  
</div></div><div class="paragraph">  
</div></div></div></div></div></div></div>Вы также можете использовать комбинацию всех этих опций.

#### %files

Общие “расширенные” макросы RPM, необходимые в разделе `%files`:

<div id="bkmrk-%D0%9C%D0%B0%D0%BA%D1%80%D0%BE%D1%81-%D0%9E%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D0%B5-%25lic"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="paragraph">  
</div><table class="tableblock frame-all grid-all stretch"><colgroup> <col style="width: 20%;"></col> <col style="width: 80%;"></col> </colgroup><tbody><tr><td class="tableblock halign-left valign-top">Макрос

</td><td class="tableblock halign-left valign-top">Описание

</td></tr><tr><td class="tableblock halign-left valign-top">%license

</td><td class="tableblock halign-left valign-top">Макрос идентифицирует файл, указанный в списке, как файл ЛИЦЕНЗИИ, и он будет установлен и помечен как таковой RPM. Пример: `%license LICENSE`

</td></tr><tr><td class="tableblock halign-left valign-top">%doc

</td><td class="tableblock halign-left valign-top">Этот макрос идентифицирует файл, указанный как документация, и он будет установлен и помечен RPM как таковой. Это часто используется не только для документации об упаковываемом программном обеспечении, но и для примеров кода и различных элементов, которые должны сопровождать документацию. Пример: `%doc README`

</td></tr><tr><td class="tableblock halign-left valign-top">%dir

</td><td class="tableblock halign-left valign-top">Макрос указывает, что путь является каталогом, которым должен владеть этот RPM. Это важно, чтобы манифест RPM-файла точно знал, какие каталоги очищать при удалении. Пример: `%dir %{_libdir}/%{name}`

</td></tr><tr><td class="tableblock halign-left valign-top">%config(noreplace)

</td><td class="tableblock halign-left valign-top">Указывает, что следующий файл является файлом конфигурации и поэтому не должен перезаписываться (или заменяться) при установке или обновлении пакета, если файл был изменен по сравнению с исходной контрольной установкой. В случае внесения изменений файл будет создан с добавлением `.rpmnew` в конец имени файла при обновлении или установке, чтобы ранее существующий или измененный файл в целевой системе не был изменен. Пример: `%config(noreplace) %{_sysconfdir}/%{name}/%{name}.conf`

</td></tr></tbody></table>

</div><div class="sect3">  
</div></div></div></div></div>#### Встроенные макросы

В Вашей системе есть много встроенных макросов RPM, и самый быстрый способ просмотреть их все - это просто выполнить команду `rpm --showrc`. Обратите внимание, что это будет содержать много выходных данных, поэтому его часто используют в сочетании с каналом для `grep`.

Вы также можете найти информацию о макросах RPM, которые поставляются непосредственно с версией RPM Вашей системы, просмотрев выходные данные `rpm -ql rpm` , обратив внимание на файлы с названием `macros` в структуре каталогов.

#### RPM Макросы, предоставляемые дистрибутивом

Различные дистрибутивы будут предоставлять разные наборы рекомендуемых макросов RPM в зависимости от языковой реализации упаковываемого программного обеспечения или конкретных рекомендаций рассматриваемого дистрибутива.

Они часто предоставляются в виде самих пакетов RPM и могут быть установлены с помощью пакетного менеджера, такого как [yum](http://yum.baseurl.org/) или [dnf](https://github.com/rpm-software-management/dnf). Сами файлы макросов после установки можно найти в `/usr/lib/rpm/macros.d/`, и они будут включены в вывод `rpm --showrc` по умолчанию после установки.

Одним из основных примеров этого является раздел [Fedora Packaging Guidelines](https://docs.fedoraproject.org/en-US/packaging-guidelines/), относящийся конкретно к [Application Specific Guidelines](https://docs.fedoraproject.org/en-US/packaging-guidelines/_domain_specific_guidelines), который на момент написания этой статьи содержит более 60 различных наборов руководств вместе с соответствующими наборами макросов RPM для упаковки.

Одним из примеров такого рода RPM может быть [Python](https://www.python.org/) версии 2.x, и если у нас установлен пакет `python2-rpm-macros` (доступный в EPEL для RHEL 7 и CentOS 7), у нас есть ряд доступных, специфичных для python2 макросов.

<div id="bkmrk--177"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="paragraph">  
</div><div class="listingblock"><div class="content">  
</div></div></div></div></div></div></div>```
$ rpm -ql python2-rpm-macros
/usr/lib/rpm/macros.d/macros.python2

$ rpm --showrc | grep python2
-14: __python2  /usr/bin/python2
CFLAGS="%{optflags}" %{__python2} %{py_setup} %{?py_setup_args} build --executable="%{__python2} %{py2_shbang_opts}" %{?1}
CFLAGS="%{optflags}" %{__python2} %{py_setup} %{?py_setup_args} install -O1 --skip-build --root %{buildroot} %{?1}
-14: python2_sitearch   %(%{__python2} -c "from distutils.sysconfig import get_python_lib; print(get_python_lib(1))")
-14: python2_sitelib    %(%{__python2} -c "from distutils.sysconfig import get_python_lib; print(get_python_lib())")
-14: python2_version    %(%{__python2} -c "import sys; sys.stdout.write('{0.major}.{0.minor}'.format(sys.version_info))")
-14: python2_version_nodots     %(%{__python2} -c "import sys; sys.stdout.write('{0.major}{0.minor}'.format(sys.version_info))")
```

<div id="bkmrk--178"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="listingblock"><div class="content">  
</div></div><div class="paragraph">  
</div></div></div></div></div></div>В приведенном выше выводе отображаются необработанные определения макросов RPM, но нас, вероятно, больше интересует, что мы можем сделать с помощью `rpm --eval` , чтобы определить, что они делают, а также как они могут быть полезны для нас при упаковке RPMs.

<div id="bkmrk--179"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="paragraph">  
</div><div class="listingblock"><div class="content">  
</div></div></div></div></div></div></div>```
$ rpm --eval %{__python2}
/usr/bin/python2

$ rpm --eval %{python2_sitearch}
/usr/lib64/python2.7/site-packages

$ rpm --eval %{python2_sitelib}
/usr/lib/python2.7/site-packages

$ rpm --eval %{python2_version}
2.7

$ rpm --eval %{python2_version_nodots}
27
```

<div id="bkmrk--180"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="listingblock"><div class="content">  
</div></div></div></div><div class="sect2">  
</div></div></div></div>### Пользовательские макросы

Вы можете переопределить макросы в файле `~/.rpmmacros`. Любые внесенные вами изменения повлияют на каждую сборку на Вашем компьютере.

<div id="bkmrk-%D0%A1%D1%83%D1%89%D0%B5%D1%81%D1%82%D0%B2%D1%83%D0%B5%D1%82-%D0%BD%D0%B5%D1%81%D0%BA%D0%BE%D0%BB%D1%8C%D0%BA%D0%BE"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="paragraph">  
</div><div class="dlist"><dl><dt class="hdlist1">Существует несколько макросов, которые Вы можете использовать для переопределения</dt><dt class="hdlist1">`%_topdir /opt/some/working/directory/rpmbuild`</dt><dd>Вы можете создать этот каталог, включая все подкаталоги, с помощью утилиты `rpmdev-setuptree`. Значение этого макроса по умолчанию равно`~/rpmbuild`.

</dd><dt class="hdlist1">`%_smp_mflags -l3`</dt><dd>Этот макрос часто используется для передачи в Makefile, например: `make %{?_smp_mflags}`, и для задания количества одновременных процессов на этапе сборки. По умолчанию для него задано значение `-jX`, где X - количество ядер. Если Вы измените количество ядер, Вы можете ускорить или замедлить сборку пакетов.

</dd></dl></div><div class="paragraph">  
</div></div></div></div></div>Хотя Вы можете определить любые новые макросы в файле `~/.rpmmacros` это не рекомендуется, поскольку эти макросы не будут присутствовать на других компьютерах, где пользователи могут захотеть попытаться пересобрать Ваш пакет.

### Epoch, Скриптлеты и Триггеры

В мире SPEC файлов RPM существуют различные разделы, которые считаются продвинутыми, поскольку они влияют не только на файл спецификации, способ сборки пакета, но и на конечный компьютер, на который устанавливается результирующий RPM. В этом разделе мы рассмотрим наиболее распространенные из них, такие как Epoch, Скриптлеты и триггеры.

#### Epoch

Первым в списке стоит `Epoch`, epoch - это способ определения взвешенных зависимостей на основе номеров версий. Его значение по умолчанию равно 0, если директива `Epoch` не указана в SPEC файле. Это не рассматривалось в разделе "SPEC файл" этого руководства, потому что почти всегда вводить значени Epoch - плохая идея, поскольку это искажает то, что вы обычно ожидаете от RPM при сравнении версий пакетов.

Например, если был установлен пакет `foobar` с `Epoch: 1` и `Version: 1.0`, а кто-то другой упаковал `foobar` с`Version: 2.0` , но просто опустил директиву `Epoch` либо потому, что они не знали о её необходимости, либо просто забыли, эта новая версия никогда не будет считаться обновлением, потому что версия Epoch превалирует над традиционным маркером Name-Version-Release, который означает управление версиями для RPM-пакетов.

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

#### Скриптлеты и Триггеры

В пакетах RPM существует ряд директив, которые можно использовать для внесения необходимых или желаемых изменений в систему во время установки RPM. Они называются **скриптлеты**.

Один из основных примеров того, когда и почему Вы хотели бы это сделать, это когда установлена системная служба RPM и она предоставляет [systemd](https://freedesktop.org/wiki/Software/systemd/) [файл](https://www.freedesktop.org/software/systemd/man/systemd.unit.html). Во время установки нам нужно будет уведомить [systemd](https://freedesktop.org/wiki/Software/systemd/) о появлении нового модуля, чтобы системный администратор мог выполнить команду, аналогичную `systemctl startfoo.service` после установки вымышленного `foo` (который в этом примере предоставляет демон). Аналогично, нам нужно было бы отменить это действие при деинсталляции, чтобы администратор не получал ошибок из-за того, что бинарный файл демона больше не установлен, но файл модуля все еще существует в запущенной конфигурации systemd.

Существует небольшая горстка распространенных скриптлетов, они похожи на “заголовки разделов”, такие как `%build` or `%install`, в том смысле, что они определяются многострочными сегментами кода, часто написанными, как стандартный сценарий оболочки [POSIX](https://en.wikipedia.org/wiki/POSIX) , но могут быть на нескольких разных языках программирования. Исчерпывающий список этих доступных языков можно найти в *Официальной документации RPM*.

Следующие скриптлеты:

<div id="bkmrk-%D0%94%D0%B8%D1%80%D0%B5%D0%BA%D1%82%D0%B8%D0%B2%D0%B0-%D0%9E%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D0%B5-%25"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="paragraph">  
</div><table class="tableblock frame-all grid-all stretch"><colgroup> <col style="width: 20%;"></col> <col style="width: 80%;"></col> </colgroup><tbody><tr><td class="tableblock halign-left valign-top">Директива

</td><td class="tableblock halign-left valign-top">Описание

</td></tr><tr><td class="tableblock halign-left valign-top">`%pre`

</td><td class="tableblock halign-left valign-top">Скриптлет, который выполняется непосредственно перед установкой пакета в целевую систему.

</td></tr><tr><td class="tableblock halign-left valign-top">`%post`

</td><td class="tableblock halign-left valign-top">Скриптлет, который выполняется сразу после установки пакета в целевой системе.

</td></tr><tr><td class="tableblock halign-left valign-top">`%preun`

</td><td class="tableblock halign-left valign-top">Скриптлет, который выполняется непосредственно перед удалением пакета из целевой системы.

</td></tr><tr><td class="tableblock halign-left valign-top">`%postun`

</td><td class="tableblock halign-left valign-top">Скриптлет, который выполняется сразу после удаления пакета из целевой системы.

</td></tr></tbody></table>

<div class="paragraph">  
</div></div></div></div></div></div>Также часто для этой функции существуют макросы RPM. В нашем предыдущем примере мы обсуждали необходимость получения [systemd](https://freedesktop.org/wiki/Software/systemd/) уведомления о новом [unit file](https://www.freedesktop.org/software/systemd/man/systemd.unit.html), , это легко обрабатывается макросами скриптлетов systemd, как Мы можем видеть из приведенного ниже примера вывода. Более подробную информацию об этом можно найти в [Fedora systemd Packaging Guidelines](https://fedoraproject.org/wiki/Packaging:Systemd).

<div id="bkmrk--181"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="paragraph">  
</div><div class="listingblock"><div class="content">  
</div></div></div></div></div></div></div>```
$ rpm --showrc | grep systemd
-14: __transaction_systemd_inhibit      %{__plugindir}/systemd_inhibit.so
-14: _journalcatalogdir /usr/lib/systemd/catalog
-14: _presetdir /usr/lib/systemd/system-preset
-14: _unitdir   /usr/lib/systemd/system
-14: _userunitdir       /usr/lib/systemd/user
/usr/lib/systemd/systemd-binfmt %{?*} >/dev/null 2>&1 || :
/usr/lib/systemd/systemd-sysctl %{?*} >/dev/null 2>&1 || :
-14: systemd_post
-14: systemd_postun
-14: systemd_postun_with_restart
-14: systemd_preun
-14: systemd_requires
Requires(post): systemd
Requires(preun): systemd
Requires(postun): systemd
-14: systemd_user_post  %systemd_post --user --global %{?*}
-14: systemd_user_postun        %{nil}
-14: systemd_user_postun_with_restart   %{nil}
-14: systemd_user_preun
systemd-sysusers %{?*} >/dev/null 2>&1 || :
echo %{?*} | systemd-sysusers - >/dev/null 2>&1 || :
systemd-tmpfiles --create %{?*} >/dev/null 2>&1 || :

$ rpm --eval %{systemd_post}

if [ $1 -eq 1 ] ; then
        # Initial installation
        systemctl preset  >/dev/null 2>&1 || :
fi

$ rpm --eval %{systemd_postun}

systemctl daemon-reload >/dev/null 2>&1 || :

$ rpm --eval %{systemd_preun}

if [ $1 -eq 0 ] ; then
        # Package removal, not upgrade
        systemctl --no-reload disable  > /dev/null 2>&1 || :
        systemctl stop  > /dev/null 2>&1 || :
fi
```

<div id="bkmrk--182"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="listingblock"><div class="content">  
</div></div><div class="paragraph">  
</div></div></div></div></div></div>Еще один элемент, который обеспечивает еще более детальный контроль над транзакцией RPM в целом, - это то, что известно как **триггеры**. По сути, это то же самое, что и скриптлет, но выполняется в очень определенном порядке операций во время транзакции установки или обновления RPM, что позволяет более точно контролировать весь процесс.

Порядок, в котором выполняется каждый из них, и подробная информация о котором - приведена ниже.

<div id="bkmrk--183"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="paragraph">  
</div><div class="listingblock"><div class="content">  
</div></div></div></div></div></div></div>```
all-%pretrans
...
any-%triggerprein (%triggerprein from other packages set off by new install)
new-%triggerprein
new-%pre      for new version of package being installed
...           (all new files are installed)
new-%post     for new version of package being installed

any-%triggerin (%triggerin from other packages set off by new install)
new-%triggerin
old-%triggerun
any-%triggerun (%triggerun from other packages set off by old uninstall)

old-%preun    for old version of package being removed
...           (all old files are removed)
old-%postun   for old version of package being removed

old-%triggerpostun
any-%triggerpostun (%triggerpostun from other packages set off by old un
            install)
...
all-%posttrans
```

<div id="bkmrk--184"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="listingblock"><div class="content">  
</div></div><div class="paragraph">  
</div></div></div></div></div></div>Вышеуказанные элементы взяты из прилагаемой документации RPM, найденной в `/usr/share/doc/rpm/triggers` на системах Fedora и `/usr/share/doc/rpm-4.*/triggers` в системах RHEL 7 и CentOS 7.

##### Использование скриптов без оболочки в SPEC файле

Параметр скриптлета `-p`, в SPEC файле позволяет вызывать определенный интерпретатор вместо стандартного `-p /bin/sh`. Наглядным примером является скрипт, который выводит сообщение после установки `pello.py`.

<div id="bkmrk-%D0%9E%D1%82%D0%BA%D1%80%D0%BE%D0%B9%D1%82%D0%B5-%D1%84%D0%B0%D0%B9%D0%BB-pello."><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="paragraph">  
</div><div class="olist arabic">1. Откройте файл `pello.spec`.
2. Найдите следующую строку:
    
    ```
    install -m 0644 %{name}.py* %{buildroot}/usr/lib/%{name}/
    ```
    
    Под этой строкой вставьте следующий код:
    
    ```
    %post -p /usr/bin/python3
    print("This is {} code".format("python"))
    ```
3. Создайте свой пакет в соответствии с главой [Сборка RPMS](https://rpm-packaging-guide-ru.github.io/#building-rpms).
4. Установите Ваш пакет:
    
    ```
    # dnf install /home/<username>/rpmbuild/RPMS/noarch/pello-0.1.1-1.fc27.noarch.rpm
    ```
    
    Результатом выполнения этой команды является следующее сообщение после установки:
    
    ```
    Installing       : pello-0.1.1-1.fc27.noarch                              1/1
    Running scriptlet: pello-0.1.1-1.fc27.noarch                              1/1
    This is python code
    ```

</div><div class="admonitionblock note"><table><tbody><tr><td class="icon"><div class="title">Примечание</div></td><td class="content"><div class="ulist">- Чтобы использовать скрипт Python 3: Напишите строку `%post -p /usr/bin/python3` под строкой `install -m` in a SPEC file.
- Чтобы использовать скрипт Lua: Напишите строку `%post -p <lua>` под строкой `install -m` в SPEC файле.
- Таким образом, в SPEC файле может быть указан любой интерпретатор.

</div></td></tr></tbody></table>

</div></div></div></div><div class="sect2">  
</div></div></div></div>### Условные обозначения RPM

Условные обозначения RPM позволяют условно включать различные разделы SPEC файла.

Чаще всего условные обозначения имеют дело с:

<div id="bkmrk-%D1%80%D0%B0%D0%B7%D0%B4%D0%B5%D0%BB%D0%B0%D0%BC%D0%B8%2C-%D0%BE%D1%82%D0%BD%D0%BE%D1%81%D1%8F%D1%89%D0%B8%D0%BC"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="paragraph">  
</div><div class="ulist">- разделами, относящимися к конкретной архитектуре
- разделами, относящимися к конкретной операционной системе
- проблемами совместимости между различными версиями операционных систем
- существованием и определением макросов

</div><div class="sect3">  
</div></div></div></div></div>#### Синтаксис условий RPM

Если *expression* истинно, то выполните какое-нибудь действие:

<div id="bkmrk--185"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="paragraph">  
</div><div class="literalblock"><div class="content">  
</div></div></div></div></div></div></div>```
%if expression
...
%endif
```

<div id="bkmrk--186"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="literalblock"><div class="content">  
</div></div><div class="paragraph">  
</div></div></div></div></div></div>Если *expression* истинно, то выполните какое-нибудь действие, в другом случае выполните другое действие:

<div id="bkmrk--187"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="paragraph">  
</div><div class="literalblock"><div class="content">  
</div></div></div></div></div></div></div>```
%if expression
...
%else
...
%endif
```

<div id="bkmrk--188"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="literalblock"><div class="content">  
</div></div></div><div class="sect3">  
</div></div></div></div></div>#### Примеры условий RPM

##### Обозначение `%if`

```
%if 0%{?rhel} == 6
sed -i '/AS_FUNCTION_DESCRIBE/ s/^/#/' configure.in
sed -i '/AS_FUNCTION_DESCRIBE/ s/^/#/' acinclude.m4
%endif
```

<div id="bkmrk--189"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="literalblock"><div class="content">  
</div></div><div class="paragraph">  
</div></div></div></div></div></div></div>Это условие обрабатывает совместимость между RHEL6 и другими операционными системами с точки зрения поддержки макроса AS\_FUNCTION\_DESCRIBE. Когда пакет создается для RHEL, определяется макрос `%rhel` , и он расширяется до версии RHEL. Если его значение равно 6, что означает, что пакет создан для RHEL 6, , то ссылки на AS\_FUNCTION\_DESCRIBE, который не поддерживается RHEL6, удаляются из сценариев автоконфигурации.

<div id="bkmrk--190"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="paragraph">  
</div><div class="literalblock"><div class="content">  
</div></div></div></div></div></div></div></div>```
%if 0%{?el6}
%global ruby_sitearch %(ruby -rrbconfig -e 'puts Config::CONFIG["sitearchdir"]')
%endif
```

<div id="bkmrk--191"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="literalblock"><div class="content">  
</div></div><div class="paragraph">  
</div></div></div></div></div></div></div>Это условие регулирует совместимость между Fedora версии 17 и новее и RHEL 6 с точки зрения поддержки макроса `%ruby_sitearch` macro. Fedora версии 17 и никогда не определяет `%ruby_sitearch` по умолчанию, но RHEL6 не поддерживает этот макрос. Условие проверяет, является ли операционная система RHEL 6. Если это так, `%ruby_sitearch` определяется явно. Обратите внимание, что `0%{?el6}` имеет то же значение, что и `0%{?rhel} == 6` из предыдущего примера, и он проверяет, построен ли пакет на RHEL 6.

<div id="bkmrk--192"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="paragraph">  
</div><div class="literalblock"><div class="content">  
</div></div></div></div></div></div></div></div>```
%if 0%{?fedora} >= 19
%global with_rubypick 1
%endif
```

<div id="bkmrk--193"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="literalblock"><div class="content">  
</div></div><div class="paragraph">  
</div></div></div></div></div></div></div>Это условие обрабатывает поддержку инструмента выбора ruby. Если операционная система Fedora версии 19 или новее, поддерживается rubypick.

<div id="bkmrk--194"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="paragraph">  
</div><div class="literalblock"><div class="content">  
</div></div></div></div></div></div></div></div>```
%define ruby_archive %{name}-%{ruby_version}
%if 0%{?milestone:1}%{?revision:1} != 0
%define ruby_archive %{ruby_archive}-%{?milestone}%{?!milestone:%{?revision:r%{revision}}}
%endif
```

<div id="bkmrk--195"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="literalblock"><div class="content">  
</div></div><div class="paragraph">  
</div></div></div></div></div></div></div>Это условие обрабатывает определение макросов. Если заданы макросы `%milestone` или `%revision`, переопределяется макрос `%ruby_archive`, который определяет имя вышестоящего файла архива.

##### Специальные варианты обозначения `%if`

Условные обозначения `%ifarch`, `%ifnarch` и `%ifos` являются специализированными вариантами условных обозначений `%if`. Эти варианты обычно используются, поэтому у них есть свои собственные макросы.

###### Обозначение`%ifarch`

Условие `%ifarch` спользуется для начала блока SPEC файла, который зависит от архитектуры. За ним следует один или несколько спецификаторов архитектуры, каждый из которых разделен запятыми или пробелами.

<div id="bkmrk--196"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="sect5"><div class="paragraph">  
</div><div class="literalblock"><div class="content">  
</div></div></div></div></div></div></div></div></div>```
%ifarch i386 sparc
...
%endif
```

<div id="bkmrk--197"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="sect5"><div class="literalblock"><div class="content">  
</div></div><div class="paragraph">  
</div></div></div></div></div></div></div></div>Все содержимое SPEC файла между `%ifarch` и `%endif` обрабатывается только на 32-разрядных архитектурах AMD и Intel или системах на базе Sun SPARC.

###### Условное обозначение `%ifnarch`

Условие `%ifnarch` имеет обратную логику, чем условие `%ifarch`.

<div id="bkmrk--198"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="sect5"><div class="paragraph">  
</div><div class="literalblock"><div class="content">  
</div></div></div></div></div></div></div></div></div>```
%ifnarch alpha
...
%endif
```

<div id="bkmrk--199"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="sect5"><div class="literalblock"><div class="content">  
</div></div><div class="paragraph">  
</div></div></div></div></div></div></div></div>Все содержимое SPEC файла между `%ifnarch` и `%endif` обрабатывается только в том случае, если оно не выполняется в системе на основе Digital Alpha/AXP.

###### Условие `%ifos`

Условие `%ifos` используется для управления обработкой на основе операционной системы сборки. За ним может следовать одно или несколько имен операционной системы.

<div id="bkmrk--200"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="sect5"><div class="paragraph">  
</div><div class="literalblock"><div class="content">  
</div></div></div></div></div></div></div></div></div>```
%ifos linux
...
%endif
```

<div id="bkmrk--201"><div class="sect1"><div class="sectionbody"><div class="sect2"><div class="sect3"><div class="sect4"><div class="sect5"><div class="literalblock"><div class="content">  
</div></div><div class="paragraph">  
</div></div></div></div></div></div></div></div>Все содержимое SPEC файла между `%ifos` и `%endif` обрабатывается только в том случае, если сборка была выполнена в системе Linux.

## Appendix A: Новые возможности RPM в RHEL 7

Этот список документирует наиболее заметные изменения в упаковке RPM между Red Hat Enterprise Linux 6 и 7.

<div id="bkmrk-%D0%94%D0%BE%D0%B1%D0%B0%D0%B2%D0%BB%D0%B5%D0%BD%D0%B0-%D0%BD%D0%BE%D0%B2%D0%B0%D1%8F-%D0%BA%D0%BE%D0%BC%D0%B0"><div class="sect1"><div class="sectionbody"><div class="paragraph">  
</div><div class="ulist">- Добавлена новая команда `rpmkeys`, используемая для импорта связки ключей и проверки подписи.
- Добавлена новая команда `rpmspec`, используемая для запросов спецификаций и анализируемых выходных данных.
- Добавлена новая команда `rpmsign`, используемая для подписи пакета.
- Расширения `posix.exec()` и `os.exit()` встроенные в `%{lua:…​}` скрипты, завершают работу скрипта с ошибкой, если они не вызываются из дочернего процесса, созданного с помощью скриптлета `posix.fork()`.
- Сбой скриптлета `%pretrans` приводит к пропуску установки пакета
- Скриптлеты могут быть развернуты макросом и развернуты в формате запроса во время выполнения
- Зависимости скриптлетов до транзакции и после транзакции теперь могут быть правильно выражены с помощью `Requires(pretrans)` , `Requires(posttrans)` и скриптлетов.
- Добавлен тег `OrderWithRequires` для предоставления дополнительных подсказок. Тег следует синтаксису тега `Requires`, но не создает фактических зависимостей. Подсказки по порядку обрабатываются так, как если бы они были `Requires` при расчете порядка транзакции, только если задействованные пакеты присутствуют в одной и той же транзакции.
- The `%license`. Этот флаг можно использовать в разделе `%files`. Можно использовать аналогично флагу `%doc` для пометки файлов как лицензий, которые необходимо установить, несмотря на параметр `--nodocs``.
- Добавлен макрос `%autosetup` для автоматизации применения исправлений с дополнительной интеграцией с распределенной системой контроля версий.
- Автоматический генератор зависимостей был переписан в расширяемую и настраиваемую систему на основе правил со встроенной фильтрацией.
- Открытые ключи OpenPGP V3 больше не поддерживаются.

</div></div></div><div class="sect1">  
</div></div>## Appendix B: Ссылки на источники

Ниже приведены ссылки на различные темы, представляющие интерес для RPM, упаковки RPM и сборки RPM. Некоторые из них являются продвинутыми и выходят далеко за рамки вводного материала, включённого в данное руководство.

[Software Collections](https://www.softwarecollections.org/en/) - SoftwareCollections.org это проект с открытым исходным кодом для создания и распространения поддерживаемых сообществом коллекций программного обеспечения (SCL) для Red Hat Enterprise Linux, Fedora, CentOS, и Scientific Linux.

[Creating RPM package](https://docs.fedoraproject.org/en-US/quick-docs/creating-rpm-packages/index.html) - Пошаговое руководство по изучению основ упаковки RPM.

[Packaging software with RPM, Part 1](http://www.ibm.com/developerworks/library/l-rpm1/), [Part 2](http://www.ibm.com/developerworks/library/l-rpm2/), [Part 3](http://www.ibm.com/developerworks/library/l-rpm3/) - Руководство по упаковке IBM RPM.

[RPM Documentation](http://rpm.org/documentation) - Официальная документация RPM.

[Fedora Packaging Guidelines](https://docs.fedoraproject.org/en-US/packaging-guidelines/) - Официальные рекомендации по упаковке для Fedora, полезные для всех дистрибутивов на основе RPM.

[rpmfluff](https://pagure.io/rpmfluff) - библиотека python для создания пакетов RPM и их саботажа, чтобы они были взломаны контролируемыми способами.

## Appendix C: Выражение благодарности

Некоторые фрагменты этого текста впервые появились в [RPM Packaging Guide](https://rpm-packaging-guide.github.io/). Copyright © 2020 Adam Miller and others. Лицензировано в соответствии с [Creative Commons Attribution-ShareAlike 3.0 Unported License](http://creativecommons.org/licenses/by-sa/3.0/).

## Список используемых источников

- [Руководство по сборке RPM пакетов](https://rpm-packaging-guide-ru.github.io/#what-is-a-spec-file)
- [Conditionals](http://ftp.rpm.org/max-rpm/s1-rpm-inside-conditionals.html)

# Ошибки сборки пакетов RPM

## <span class="mw-headline" id="bkmrk-error%3A-installed-%28bu-1">error: Installed (but unpackaged) file(s) found</span>

Ошибка означает, что в новой версии пакета появились новые файлы, отсутствующие ранее, и rpmbuild не знает, что с ними делать (и нужны ли ли вообще).

Если вы полагаете, что эти файлы нужны (а это так в 99.9% случаев), необходимо добавить их в секцию %files spec-файла. Если у вас есть несколько подпакетов, то надо сначала определить - к какому из них эти файлы относятся. Универсального рецепта здесь нет, однако есть несколько правил:

- файлы в директориях <span style="font-family: courier; color: #a0522d;">/usr/include</span>, <span style="font-family: courier; color: #a0522d;">/ust/lib(64)/pkgconfig</span>, добавляются в devel-пакеты
- файлы библиотек в директориях <span style="font-family: courier; color: #a0522d;">/lib(64)</span> и <span style="font-family: courier; color: #a0522d;">/usr/lib(64)</span>, оканчивающиеся на суффикс .so (например, <span style="font-family: courier; color: #a0522d;">libfoo.so</span>), также добавляются в devel-пакеты
- файлы библиотек в директориях <span style="font-family: courier; color: #a0522d;">/lib(64)</span> и <span style="font-family: courier; color: #a0522d;">/usr/lib(64)</span>, оканчивающиеся на цифру (например, <span style="font-family: courier; color: #a0522d;">libfoo.so.1</span>), добавляются в пакеты с соответсвующими библиотеками. Согласно правилам РОСЫ, каждая библиотека должна упаковываться в отдельный подпакет, соотевтсвующий ее имени и значению SONAME (как правило, это цифра после суффикса .so - так что файлы <span style="font-family: courier; color: #a0522d;">libfoo.so.1</span> и <span style="font-family: courier; color: #a0522d;">libfoo.so.1.2</span> должны находиться в пакете <span style="color: #343072; font-weight: bold;">lib(64)foo1</span>). Часто ошибка "Installed but unpackaged files found) в пакетах с библиотеками вызвана изменением значения SONAME - например, вместо <span style="font-family: courier; color: #a0522d;">libfoo.so.1</span> в новой версии пакета появился файл <span style="font-family: courier; color: #a0522d;">libfoo.so.2</span>. В этом случае вы также дополнительно увидите ошибку "Cannot find file or directory", сообщающую, что отсутсвует файл <span style="font-family: courier; color: #a0522d;">libfoo.so.1</span>. Для исправления сборки в такой ситуации достаточно изменить значение макроса major в начале spec-файла:

```
%define major 2
```

Помните, что при добавлении файлов в секции <span style="font-style: italic; font-weight: bold; color: #3e3e3e;">%files</span> принято заменять некоторые стандартные пути макросами (например вместо <span style="font-family: courier; color: #a0522d;">/usr/lib</span> и <span style="font-family: courier; color: #a0522d;">/usr/lib64</span> необходимо использовать макрос <span style="font-style: italic; font-weight: bold; color: #3e3e3e;">%{\_libdir</span>}, вместо <span style="font-family: courier; color: #a0522d;">/usr/share/man</span> - <span style="font-style: italic; font-weight: bold; color: #3e3e3e;">%{\_mandir</span>} и так далее). Более полный список можно посмотреть с [скрипте из Updates Builder](https://abf.rosalinux.ru/dsilakov/updates_builder_launcher/blob/master/dir_to_macro.pm).

## <span class="mw-headline" id="bkmrk-file-not-found-1">File not found</span>

Ошибка означает, что в новой версии пакета отсутсвуют некоторые файлы, присутствувавшие ранее. Причин для этого может быть несколько:

- в новой версии действительно нет таких файлов - в этом случае надо просто убрать их описание из секции <span style="font-style: italic; font-weight: bold; color: #3e3e3e;">%files</span>
- в новой версии файлы переименованы либо перемещены в другое место. В этом случае вы также увидите ошибку "Installed (but unpackaged) file(s) found", как в случае с библиотеками (см. выше). В случае с библиотеками для исправления сразу обеих ошибок необходимо изменить значение переменной major в начале spec-файла. В общем же случае необходимо исправить секцию <span style="font-style: italic; font-weight: bold; color: #3e3e3e;">%files</span>, чтобы она соответсвовала новым реалиям.
- отсутсвующие файлы собираются или не собираются в зависимости от параметров окружения и сборки - опций компиляции или установленных в сборочной среде пакетов (описанных в BuildRequires). Возможно, в новой версии пакета надо использовать какие-то новые опции сборки либо добавить дополнительные сборочные зависимости для получения этих файлов. Выявить такие ситуации можно на основе анализа журналов сборки и вывода команд наподобие configure или cmake, которые сообщают, какие опцилнальные возможности будут включены при сборке, а какие - нет. Например, <span style="color: #343072; font-weight: bold;">squid</span> может собираться с поддержкой аутентификации SASL и без нее. В первом случае в пакете будет присутсвовать файл basic\_sasl\_auth, во втором его не будет. Для ключения/отключения SASL необходимо добавить/удалить значение SASL из параметра --enable-auth-basic команды <span style="margin: 0 2px; color: #7a1869; padding: 0 5px; white-space: pre-line; word-wrap: break-word; border: 1px solid #EAEAEA; background-color: #f8f8f8; border-radius: 3px;">configure</span>, а также добавить сборочную зависимость (BuildRequires) от <span style="color: #343072; font-weight: bold;">sasl-devel</span>.

Помните, что при добавлении файлов в секции <span style="font-style: italic; font-weight: bold; color: #3e3e3e;">%files</span> принято заменять некоторые стандартные пути макросами, а также что при описании файлов в секции <span style="font-style: italic; font-weight: bold; color: #3e3e3e;">%files</span> могут использоваться символы '?' и '\*' (означающие один произвольный символ и любое количество произвольных символов соответсвенно).

## <span class="mw-headline" id="bkmrk-cp%3A-cannot-stat-%27%3Cso-1">cp: cannot stat '&lt;some\_file&gt;': No such file or directory</span>

Ошибка означает, что rpmbuild не смог найти файл &lt;some\_file&gt;, помеченный как <span style="font-style: italic; font-weight: bold; color: #3e3e3e;">%doc</span> в секции %files вашего пакета. Возможно, этот файл был переименован (в этом случе вы также увидите ошибку "Installed (but unpackaged) file(s) found") - в этом случае надо изменить имя файла на новое (например, <span style="font-family: courier; color: #a0522d;">README</span> могут переименовать в <span style="font-family: courier; color: #a0522d;">README.md</span>). Если неупакованного файла с близким именем не появилось, то значит старый файл в новой версии отсутсвует и его определение в <span style="font-style: italic; font-weight: bold; color: #3e3e3e;">%doc</span> необходимо просто удалить.

## <span class="mw-headline" id="bkmrk-reversed-%28or-previou-1">Reversed (or previously applied) patch detected</span>

Ошибка означает, что применявшийся к старой версии пакета патч по крайней мере частично уже применен в новой версии. Обратите внимание, что команда <span style="margin: 0 2px; color: #7a1869; padding: 0 5px; white-space: pre-line; word-wrap: break-word; border: 1px solid #EAEAEA; background-color: #f8f8f8; border-radius: 3px;">patch</span> делает такой вывод на основе попытки применить только первую часть патча! Если патч сложный и затрагивает несколько файлов или несколько мест одного файла, то необходимо вручную проверить, какие из этих изменений уже есть в новой версии, а каких нет. Для этого можно попробовать применить патч к новой версии вручную, отвечая "n" на вопрос "Assume '-R'" и "y" на предложени продолжить применять остальные части патча.

Если все изменения из патча действительно уже присутсвуют в новой версии, то патч можно спокойно удалить (физически из Git-репозитория, а также из spec-файла). Если же нет, то необходимо определить - нужны ли в новой версии оставшиеся изменения и если да, то переделать патч соответствующим образом. Такой анализ уже требует некоторых познаний в том, что именно делает патч.

## <span class="mw-headline" id="bkmrk-%2Fvar%2Ftmp%2Frpm-tmp.xxx-1">/var/tmp/rpm-tmp.xxxxx: line yy: cd: &lt;some\_folder\_name&gt;: No such file or directory</span>

Ошибка означает, что rpmbuild не может определить, в какую директорию ему перейти после распаковки архива с исходным кодом. Имя директории указывается в опции <span style="font-style: italic; font-weight: bold; color: #3e3e3e;">-n</span> макроса <span style="font-style: italic; font-weight: bold; color: #3e3e3e;">%setup</span> в секции <span style="font-style: italic; font-weight: bold; color: #3e3e3e;">%prep</span>. Если эта опция отсутсвует, то подразумевается использование "-n %{name}-%{version}". Для исправления ошибки необходимо посмотреть, как расположены файлы в новой версии архива с исходным кодом - обычно такой архив содержит директорию вида "&lt;имя\_программы&gt;-&lt;версия&gt;", но время от времени разработчики могут что-то изменять (например, переименовывать директорию просто в &lt;имя\_программы&gt;. Для исправления ошибки необходимо соответсвующим образом исправить значение опции "-n" макроса <span style="font-style: italic; font-weight: bold; color: #3e3e3e;">%setup</span>.

## <span class="mw-headline" id="bkmrk-empty-debug-info-%D0%B8-d-1">empty-debug-info и debuginfo-without-sources</span>

Ошибки означают, что rpmbuild не смог должным образом сформировать подпакет с отладочной информацией. Две основные причины такого поведения:

- в пакете нет исполнимых файлов или библиотек. Если в пакете при этом вообще нет архитектурно-зависимых файлов (т.е. пакеты для разных архитектур имею абсолютно одинаковое содержимое), то необходимо объявить пакет как независимый от архитектуры, добавив в заголовок spec-файла декларацию "BuildArch: noarch". Если же архитектурно-зависимые файлы все-таки есть (например, какие-то данные могут быть специфичны для каждой арзитектуры; другой типичный пример - проприетарное приложение, которое уже поставляется в виде бинарных файлов бех отладочной информации), то необходимо отключить генерацию debug-пакетов, сбросив определение макроса <span style="font-style: italic; font-weight: bold; color: #3e3e3e;">debug\_package</span> в начале spec-файла:

```
%define debug_package %{nil}
```

- скрипты сборки приложения в пакете устроены таким образом, что сразу удаляют отладочную информацию из собранных файлов (например, явно вызывают команду strip либо просто не передают опцию "-g" компилятору). В таких случаях необходимо посмотреть, как заставить скрипты сборки оставить отладочную информацию. Иногда этого можно добиться с помощью переменных среды, а иногда может потребоваться патч, изменяющий сборочные скрипты. Если никакие способы не помогают, то можно отключить генерацию debug-пакетов, как описано выше, но делать так не рекомендуется - эти пакеты очень полезны для анализа ошибок в случае падений приложения.

Остановимся подробнее на втором пункте. Большинство приложений позволяют настраивать передаваемые компилятору опции при старте сборки - например, при вызове <span style="font-family: courier; color: blue;">**configure**</span> или <span style="font-family: courier; color: blue;">**cmake**</span>. Для генерации отладочной информации для подобных приложений вам не нужно прилагать особых усилий - достаточно вместо прямого вызова <span style="font-family: courier; color: blue;">**configure**</span> или <span style="font-family: courier; color: blue;">**cmake**</span> использовать соответствующие макросы <span style="font-family: courier; color: blue;">**rpm**</span> (в нашем примере - <span style="font-style: italic; font-weight: bold; color: #3e3e3e;">%configure2\_5x</span> или <span style="font-style: italic; font-weight: bold; color: #3e3e3e;">%cmake</span> соответственно). Если использование макросов не помогает (либо в проекте не используются стандартные средства типа GNU Autotools или CMake), необходимо изучить схему сборки и посмотреть - нельзя ли на нее повлиять как-то еще - например, специфическими опциями либо переменными окружения. Типичными переменными, которые могут повлиять на сборку, являются <span style="font-style: italic; font-weight: bold; color: #ff3501;">CFLAGS</span>, <span style="font-style: italic; font-weight: bold; color: #ff3501;">CXXFLAGS</span> и <span style="font-style: italic; font-weight: bold; color: #ff3501;">CPPFLAGS</span>. Мы рекомендуем выставить эти переменные в макрос <span style="font-style: italic; font-weight: bold; color: #3e3e3e;">%optflags</span>; при этом может оказаться необходимым перед использованием <span style="font-style: italic; font-weight: bold; color: #3e3e3e;">%%optflags</span> вызвать макрос <span style="font-style: italic; font-weight: bold; color: #3e3e3e;">%%setup\_compile\_flag</span> (например так сделано в пакете \[[slock](https://abf.io/import/slock/commit/78a9ac338470558c7a38d1709ce6d9036e5bb408#diff-F2R24)\].

Обратите внимание на использование кавычек при выставлении переменных среды - они необходимы, поскольку макрос разворачивается в большой набор опций, разделенных пробелами.

Бывают ситуации, когда повлиять на сборку извне нельзя никак - в скриптах сборки или файлах типа Makefile инструкции сборки "забиты" намертво. В таких случаях необходимо подготовить патч, добавляющий во все вызовы компилятора опцию <span style="font-style: italic; font-weight: bold; color: #ff3501;">-g</span>. Помимо опции <span style="font-style: italic; font-weight: bold; color: #ff3501;">-g</span> у компилятора, повлиять на отладочную информацию может компоновщик - например, его опции <span style="font-style: italic; font-weight: bold; color: #ff3501;">-s</span> и <span style="font-style: italic; font-weight: bold; color: #ff3501;">-S</span>, удаляющие подобные данные из результирующих бинарных файлов. Если такие опции явно передаются компоновщику в скриптах сборки либо файлах Makefile, то необходимо подготовить патч, убирающий их - например, так сделано в пакете \[[kicad](https://abf.io/import/kicad/commit/c93e3d9911e3b3be485ee5dd01cbea723a087729#diff-13)\].

## <span class="mw-headline" id="bkmrk-error%3A-can%27t-locate--1">Error: Can't locate Some/Perl/Module.pm in @INC</span>

Ошибка означает, что для сборки необходим Perl-модуль <span style="font-family: courier; color: #a0522d;">Some/Perl/Module.pm</span>, который не был обнаружен в сборочном окружении.

Для исправления ошибки необходимо добавить сборочную зависимость от такого модуля в spec-файл:

```
BuildRequires: perl(Some::Perl::Module)
```

Предварительно необходимо убедиться, что такой модуль вообще существует - попробовать его установить с помощью dnf (в rosa2019.1 и новее) либо urpmi (в rosa2016.1):

```
# dnf install 'perl(Some::Perl::Module)'
```

```
# urpmi 'perl(Some::Perl::Module)'
```

Если urpmi скажет, что не нашел нужного пакета, то необходимо сначала добавить пакет с модулем в репозитории РОСЫ.

Обратите внимание, что необходимый модуль может находиться в репозитории Contrib, в то время как вы собираете пакет в репозитории Main, Non-free или Restricted. В таком случае Contrib при сборке не используется и требуемый модуль необходимо перенести в репозиторий Main.

## <span class="mw-headline" id="bkmrk-package%28s%29-suggested-1">Package(s) suggested but not available</span>

Данная ошибка может встречаться при сборке расширений языка R. Она означает, что у собираемого пакета есть необязательная зависимость от каких-то других модулей, которые в сборочной среде отсутствуют. В идеале для каждой такой зависимости необходимо прописать BuildRequires и Requires - например, если ошибка относится к модулю "foo", то нужны зависимости от R-foo:

```
BuildRequires: R-foo
Requires: R-foo
```

Предварительно необходимо убедиться, что такой пакет вообще есть в репозиториях - вывод команды "urpmq R-foo" должен быть непуст. Если пакета нет, то желательно его сначала собрать и добавить в репозитории. Однако при сборке может оказаться, что он зависит от того пакета, который вы собирали до этого и для которого вы собственно и хотите добавить зависимость R-foo. В этом случае первый пакет можно собрать и без R-foo, закомментировав при этом команду "%{\_bindir}/R CMD check %{packname}" в секции %check. После этого можно будет уже собрать R-foo и вернуться к исходному пакету, добавив в него зависимость от R-foo и включив тесты в секции %check

## <span class="mw-headline" id="bkmrk-error%3A-dependency%28ie-1">ERROR: dependency(ies) not available</span>

Данная ошибка также может встречаться при сборке расширений языка R, но в отличие от предыдущей, здесь речь идет об обязательных зависимостях сборки. Без них пакет не может быть собран, поэтому вам необходимо добавить в spec-файл соответсвующие BuildRequiers и Requires, а при необходимости предварительно собрать нужные пакеты в репозитории РОСЫ.

## <span class="mw-headline" id="bkmrk-hunk-failed----savin-1">hunk FAILED -- saving rejects</span>

Данная ошибка означает, что один из патчей (какой именно - указано в выводе rpmbuild) не может быть применен без изменений к новой версии программы. Обычно это означает, что патч необходимо переделать (предварительно определив - нужен ли он еще), что требует определенной квалификации. Однако иногда такая ошибка возникает из-за "строгости" rpmbuild при применении патчей и проблемный патч может быть исправлен в автоматическом режиме с помощью утилиты rediff\_patch.

Для этого достаточно склонировать себе Git-репозиторий, перейти в склонированную папку, поместить туда архив с исходным кодом новой версии и запустит rediff\_patch, передав ей нужный патч к качестве аргумента:

```
$ abf get myrepo/myproject
$ cd myproject
$ wget http://project-upstream.org/new-version.tgz
$ rediff_patch <some_patch_to_rediff>
```

Если все сложится удачно, то радом со старым патчем "some\_patch\_to\_rediff" появится новый - "some\_patch\_to\_rediff.new" Если же что-то пойдет не так, то в текущей директории останутся папка "rediff\_patch" с подпапками вида "myproject.orig" и "myproject" - содержащие соответсвенно оригинальный исходный код и исходный код, получившийся после попытки применить патч. Во второй папке вы найдете файлы с расширениями \*rej - это куски патчей, которые применить не удалось.

## <span class="mw-headline" id="bkmrk-package-%27foo%27-not-fo-1">package 'foo' not found</span>

Аналогична следующей ошибке

## <span class="mw-headline" id="bkmrk-%22no-package-%27.%2A%27-fou-1">"No package '.\*' found"</span>

Такая ошибка возникает, если пакет проверяет необходимые сборочные зависимости с помощью <span style="margin: 0 2px; color: #7a1869; padding: 0 5px; white-space: pre-line; word-wrap: break-word; border: 1px solid #EAEAEA; background-color: #f8f8f8; border-radius: 3px;">pkgconfig</span> и не обнаруживает одну из них.

Для исправления ошибки достаточно добавить нужную зависимость сборки в spec-файл:

```
BuildRequires: pkgconfig(foo)
```

Предварительно необходимо убедиться, что такая зависимость может быть разрешена - попробовать ее установить с помощью <span style="margin: 0 2px; color: #7a1869; padding: 0 5px; white-space: pre-line; word-wrap: break-word; border: 1px solid #EAEAEA; background-color: #f8f8f8; border-radius: 3px;">urpmi</span>:

```
# urpmi 'pkgconfig(foo)'
```

Если urpmi скажет, что не нашел нужного пакета, то необходимо сначала добавить пакет с соответсвующей библиотекой (как правило, она и называется **libfoo**) в репозитории РОСЫ.

Обратите внимание, что необходимый пакет может находиться в репозитории Contrib, в то время как вы собираете пакет в репозитории Main, Non-free или Restricted. В таком случае Contrib при сборке не используется и требуемый пакет необходимо перенести в репозиторий Main.

## <span class="mw-headline" id="bkmrk-%2Fusr%2Fbin%2Fld%3A-cannot--1">/usr/bin/ld: cannot find -lfoo</span>

Данная ошибка возникает при линковке уже собранных объектных файлов и означает, что линкер не смог найти библиотеку "foo". Для исправления ошибки достаточно добавить зависимость от библиотеки в spec-файл. Чтобы определить, как именно эту зависимость прописать, необходимо сначала поискать devel-пакет для библиотеки <span style="color: #343072; font-weight: bold;">libfoo</span> (или <span style="color: #343072; font-weight: bold;">lib64foo</span> в 64битной системе) с помощью urpmq:

```
# urpmq -a libfoo
  lib64foo1
  lib64foo-devel
```

Интересующий нас пакет - тот, что оканчивается на -devel. Посмотрим, что он предоставляет:

```
# urpmq --provides libfoo-devel
  lib64foo-devel: devel(libfoo(64bit))
  lib64foo-devel: lib64sasl-devel[== 2.1.25]
  lib64foo-devel: libsasl-devel[== 2.1.25]
  lib64foo-devel: libfoo-devel[== 2.1.25]
  lib64foo-devel: sasl-devel[== 2.1.25-7]
  lib64foo-devel: lib64foo-devel[== 2.1.25-7:2014.1]
  lib64foo-devel: pkgconfig(foo)[== 2.1.25-7]
```

Из данного набора необходимо выбрать что-то одно. В РОСЕ отдается предпочтение зависимостям вида **pkgconfig(...)**, так что в нашем примере в spec-файл необходимо добавить следующую строку:

```
BuildRequires: pkgconfig(foo)
```

Если pkgconfig(foo) не окажется в выводе urpmq, то надо добавить зависимость от foo-devel, а если и ее нет - то от libfoo-devel.

## <span class="mw-headline" id="bkmrk-build.pl%3A-no-such-fi-1">Build.PL: No such file or directory</span>

Такая ошибка означает, что предыдущая версия пакета собиралась с помощью скрипт Build.PL, отсутсвующего в новой версии. Часто встречающаяся ситуация - замена Build.PL на Makefile.PL. Если в архиве с исходным кодом новой версии есть скрипт Makefile.PL, то в spec-файле необходимо произвести следующие замены:

- "/Build.PL installdirs=vendor" -&gt; "Makefile.PL INSTALLDIRS=vendor"
- ./Build install destdir=%{buildroot} -&gt; %makeinstall\_std
- perl Build.PL install destdir=%{buildroot} -&gt; %makeinstall\_std
- ./Build test -&gt; %make test
- ./Build -&gt; %make

Для примера можно посмотреть на пакет ...

Если же скрипта Makefile.PL в новой версии также нет, то необходимо смотреть - а что, собственно, есть - файлы CMake, configure или готовый Makefile и модифицировать spec-файл в соответствии с используемой системой сборки.

## <span class="mw-headline" id="bkmrk-makefile.pl%3A-no-such-1">Makefile.PL: No such file or directory</span>

Возможна и обратная ситуация - вместо Makefile.PL разработчики перешли на Build.PL или что-то еще. Если в архиве с новым исходным кодом есть файл Build.PL, то необходимо провести в spec-файле замены, обратные приведенным в предыдущем пункте:

- "Makefile.PL INSTALLDIRS=vendor" -&gt; "./Build.PL installdirs=vendor"
- %makeinstall\_std -&gt; ./Build install destdir=%{buildroot}
- (если предыдущая замена не сработала) %makeinstall\_std -&gt; perl Build.PL install destdir=%{buildroot}
- %make test -&gt; ./Build test
- %make -&gt; ./Build

Если же скрипта Build.PL в новой версии также нет, то необходимо смотреть - а что, собственно, есть - файлы CMake, configure или готовый Makefile и модифицировать spec-файл в соответствии с используемой системой сборки.

## <span class="mw-headline" id="bkmrk-fg%3A-no-job-control-1">fg: no job control</span>

Ошибка означает, что внутри spec-файла используется макрос RPM, не поддерживаемый в РОСЕ. Поддерживается или нет каждый конкретный макрос, можно узнать с помощью команды <span style="margin: 0 2px; color: #7a1869; padding: 0 5px; white-space: pre-line; word-wrap: break-word; border: 1px solid #EAEAEA; background-color: #f8f8f8; border-radius: 3px;">rpm --eval</span>:

```
$ rpm --eval %macro_name
```

Если <span style="font-family: courier; color: blue;">**rpm**</span> ничего не знает о таком макросе, то результатом выполнения этой команды будет "%macro\_name". В противном случае <span style="font-family: courier; color: blue;">**rpm**</span> развернет определение макроса.

## <span class="mw-headline" id="bkmrk-package-check-%22%2Fusr%2F-1">Package check "/usr/bin/rpmlint ..." failed</span>

Для проверки соблюдения политик сборки и оформления spec-файлов, в Росе используется утилита [Rpmlint](http://wiki.rosalab.ru/ru/index.php/Rpmlint "Rpmlint"). Многие ошибки этой утилиты некритичны, однако многие имеют "вес" (badness) и если суммарный вес ошибок для пакета больше или равен 50, то сборка завершается с ошибкой. Если это ваш случай - то первым делом необходимо поискать в логе ошибки, для которых светится "Badness: 50" и исправлять их. Перечень всех ошибок можно найти здесь: [Rpmlint Errors](http://wiki.rosalab.ru/ru/index.php/Rpmlint_Errors "Rpmlint Errors")

## Список используемых источников

- [Ошибки сборки пакетов RPM](http://wiki.rosalab.ru/ru/index.php/%D0%9E%D1%88%D0%B8%D0%B1%D0%BA%D0%B8_%D1%81%D0%B1%D0%BE%D1%80%D0%BA%D0%B8_%D0%BF%D0%B0%D0%BA%D0%B5%D1%82%D0%BE%D0%B2_RPM#error:_Installed_.28but_unpackaged.29_file.28s.29_found)

# Системы основанные на RPM

CentOS, OracleLinux, SuSe и другие.

# Ошибка открытия RPMD базы данных

### Проявление

```bash
rpmdb: PANIC: fatal region error detected; run recovery
error: db3 error(-30974) from dbenv->open: DB_RUNRECOVERY: Fatal error, run database recovery
error: cannot open Packages index using db3 - (-30974)
error: cannot open Packages database in /var/lib/rpm
CRITICAL:yum.main:

Error: rpmdb open failed
```

### Решение

**Резервируем текущую базу данных.**

```bash
mkdir /root/backups.rpm.mm_dd_yyyy/
cp -avr /var/lib/rpm/ /root/backups.rpm.mm_dd_yyyy/
```

**Удаляем и восстанавливаем.**

```bash
rm -f /var/lib/rpm/__db*
db_verify /var/lib/rpm/Packages
rpm --rebuilddb
yum clean all
```