# Общее для всех языков : макросы

## Базовые управляющие конструкции

В отличии от большинства других макросов `ya.make` относительный порядок базовых управляющих конструкций имеет значение. `IF` видит то значение переменной, которое было выставлено 
раньше в ya.make файле и не видит то, которое будет выставлено ниже по тексту. Однако, если установка переменной меняет свойства модуля или его команд, она может повилять и
на конструкции, написанные выше по тексту:

__Пример:__

```
SRCS(x.cpp)

SET(LLVM_VER 11)
PEERDIR(contrib/libs/llvm${LLVM_VER})

# SET(LLVM_VER 8) — Это бы повлияло на IF ниже, но не на PEERDIR выше.

IF (LLVM_VER == 11)
   SET(CFLAGS -Wno-error)    # Это влияет на SRCS выше
ENDIF()
```

### INCLUDE/INCLUDE_ONCE

```
INCLUDE(<path>)
```

```
INCLUDE_ONCE([yes|no])
```

Макросы текстового включения `INCLUDE` и управления текстовым включением `INCLUDE_ONCE` описаны в разделе [о языке ya.make](../syntax)

- [Описание `INCLUDE`](../syntax#include)
- [Описание `INCLUDE_ONCE`](../syntax#include_once)


### SET 

```
SET(VarName VarValue)
```

Задаёт или изменяет значение переменной, перетирая то значение, которое возможно было выставлено раньше.

### SET_APPEND

```
SET_APPEND(VarName Values...)
```

Добавляет значение к переменной. Существующее значение могло быть определено в глобальной конфигурации сборке, например `SET_APPEND(CFLAGS -fno-omit-frame-pointer)` добавит к глобально определённым для C++ и C модулей флагам тот, что требуется текущему модулю.

### DEFAULT

```
DEFAULT(VarName DefaultVal)
```

Задаёт значение по умолчанию для переменной. В отличии от `SET(VarName VarValue)` установит запрошенное значение переменной только если для данной переменной значение ещё нигде не было определено.

### ENABLE/DISABLE { #enable }

```
ENABLE(VarName)
DISABLE(VarName)
```

Взводит либо сбрасывает переменную, используемую в качестве флага. Эквивалентно `SET(VarName yes)`/`SET(VarName no)`.

### IF/ELSEIF/ELSE/ENDIF { #if }

```
IF (Expr)
    ...
ELSEIF (Expr)
    ...
ELSE()
    ...
ENDIF()
```

Позволяет исполнять или не исполнять макросы в зависимости от условий. Работает аналогично похожим конструкциям в императивных языках программирования.

В качестве выражений можно использовать:
 * `VAR_NAME`, `$VAR_NAME`, `${VAR_NAME}` - будет проверено значение переменной. Пустое значение или имя не установленной нигде переменной интерпретируется как `false`. В остальных случаях значение интерпретируется с помощью функций из util:
   * [IsTrue](https://a.yandex-team.ru/arc/trunk/arcadia/util/string/type.h?rev=r5705189#L38): `true`, `t`, `yes`, `y`, `on`, `1`, `da`.
   * [IsFalse](https://a.yandex-team.ru/arc/trunk/arcadia/util/string/type.h?rev=r5705189#L50): `false`, `f`, `no`, `n`, `off`, `0`, `net`.
 * `NOT Expr`, `Expr AND Expr`, `Expr OR Expr` - логические выражения (приоритеты операций соблюдаются правильно: `NOT`, `AND`, `OR`).
 * `DEFINED VAR_NAME` - проверка определена ли переменная.
 * `ISNUM SomeStrValue`, `ISNUM $VAR_NAME`, `ISNUM ${VAR_NAME}` - проверка является ли содержимое переменной целочисленным значением.
 * `==`, `!=` - сравнение значений переменных и/или строковых значений
 * `MATCHES` - бинарный оператор проверяющий, что значение правого операнда является подстрокой в значении левого.
 * `VERSION_GT`, `VERSION_GE`, `VERSION_LT`, `VERSION_LE` - бинарные операторы сравнения версии. Учитывают не более трёх численных сегментов, разделённых точкой или дефисом. Например, `3.5.17`, `5.2`, `4-12`, `1-5-17`.
 * `>`, `<`, `>=`, `<=` - бинарные операторы сравнения целых чисел.


### MESSAGE
```
MESSAGE([KIND] Text)
```

Выводит сообщение во время обработки `ya.make`.

- Если `KIND` указан как `STATUS` или не указан, то сообщение выводится предупреждение.
- Если `KIND` указан как `FATAL_ERROR`, то сообщение выводится как configure-ошибка и сборка останавливается.

__**Пример**__:

```
LIBRARY()
    IF (NOT DEFINED USE_SYSTEM_PYTHON
        AND NOT DEFINED PYTHON_CONFIG)
        MESSAGE(FATAL_ERROR "USE_SYSTEM_PYTHON/PYTHON_CONFIG is not defined, use ya make -D USE_SYSTEM_PYTHON=2.7")
    ENDIF()
END()
```

### BUILD_ONLY_IF/NO_BUILD_IF { #make_only_if }
```
BUILD_ONLY_IF([FATAL_ERROR] COND_VARS...)
```
```
NO_BUILD_IF([FATAL_ERROR] COND_VARS...)
```

Выдать предупреждение или ошибку если модуль попал в сборку в нарушение условий, выраженных `COND_VARS`.

- Макрос `BUILD_ONLY_IF` выдаёт ошибку если все переменные `COND_VARS` не определены или имеют значение *ложь* (`false`, `f`, `no`, `off`, `0`) и, соответственно, не выдаёт, если хотя бы для одной переменной это не выполнено.
- Макрос `NO_BUILD_IF` выдаёт ошибку если хотя бы одна переменная определена и не имеет значение *ложь* (т.е. значение отличное от `false`, `f`, `no`, `off`, `0`).

Параметр `FATAL_ERROR` управляет тем должна ли выдаваться ошибка или только предупреждение.

**Примеры:**

```yamake
LIBRARY()

# The code is not suitable for Windows, fail if accidentally used
NO_BUILD_IF(FATAL_ERROR OS_WINDOWS) 

SRCS(
    memtrack.cpp
)

END()
```

```
FAT_OBJECT()

OWNER(g:group)

BUILD_ONLY_IF(OS_IOS OS_DARWIN) # Either iOS or MacOS is OK, warn otherwise

PEERDIR(
    keyboard/ios/impl
)

END()
```

{% note warning %}

Эти макросы на самом деле не предотвращают сборку модулей. В случае без `FATAL_ERROR` будет лишь выдано предупреждение на консоль, и сборка продолжится. Модуль либо соберётся в запрещённой конфигурации,
либо случится ошибка сборки. В случае с `FATAL_ERROR` сборка прервётся с configure-ошибкой если не указан параметр [`-k` (`--keep-going`)](../../usage/ya_make/#obshie-nastrojki). В автосборке этот
параметр указан, а значит модуль попадёт в сборку даже несмотря на ошибку конфигурации и может дополнительно сломаться с ошибкой сборки.

{% endnote %}


## Информация о владении кодом

### OWNER

```
OWNER(name|g:group ...)
SUBSCRIBER(name|g:group ...)
```

Позволяет указать владельца кода в данной директории. Владелец автоматически добавляется во все ревью, затрагивающие код, сборка которого описывается в данном ya.make файле.
Таким кодом считается код, из которого строится модуль, описанный в данном `ya.make`, либо сам `ya.make` файл если в нём нет модулей, а только перечисление подпроектов через `RECURSE`.

При указании имени пользователя используется логин на staff. Группы описываются в самой Аркадии в папке [groups](https://a.yandex-team.ru/arc/trunk/arcadia/groups).
Данный макрос должен использоваться обязательно. При этом настоятельно рекомендуется использовать группы, а не отдельных людей, чтобы не возникали ситуации, когда, некоторый код поддерживается бывшим сотрудником (т.е. никем).

### SUBSCRIBER

```
SUBSCRIBER(name|g:group ...)
```

Параметры и их семантика полностью аналогичны макросу `OWNER`.

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

## Структура подпроектов

### RECURSE/RECURSE_ROOT_RELATIVE/RECURSE_FOR_TESTS { #recurse }

```
RECURSE(Dirs...)
RECURSE_FOR_TESTS(Dirs...)
RECURSE_ROOT_RELATIVE(Dirs...)
```

Добавляет к списку стартовых целей сборки проекты по указанным путям если текущая директория находится в списке стартовых целей. Данные макросы не имеют никакого эффекта если текущий проект попал в сборку через `PEERDIR`, `DEPENDS` или как инструмент сборки (через `RUN_PROGRAM` либо из реализации специфичного макроса, использующего проект из аркадии в качестве шага сборки).

При вызове `ya make some/arcadia/subdir another/arcadia/dir` изначальный список стартовых проектов задаётся через аргументы командной строки, в процессе чтения `ya.make` файлов к стартовым целям будут добавляться директории, добавленные через одну из форм макроса `RECURSE`. Данный процесс продолжается рекурсивно.

Простейшая и наиболее часто используемая форма макроса `RECURSE` принимает в качестве аргументов список относительных путей и интерпретирует их как пути относительно директории, в которой лежит интерпретируемый в данный момент ya.make файл.

Для подключения проектов с тестами рекомендуется использовать форму `RECURSE_FOR_TESTS`, которая не добавляет перечисленные в ней проекты в сборку, если не запрошена сборка тестов. Так же, как и в случае простейшей формы макроса, принимаемые пути интерпретируются как пути относительно директории интерпретируемого в данный момент ya.make файла.

Если требуется задать путь, лежащий за пределами текущей директории, то используется форма `RECURSE_ROOT_RELATIVE`. Крайне не рекомендуется использовать эту расширенную форму для обычных проектов. Она в первую очередь нужна для внутренних особенностей описания настроек автосборки.

{% note info %}

В наивной идеальной картине мира каждый проект должен быть доступен через цепочку `RECURSE` от ya.make из корня аркадии, а пре и посткомитные проверки выполняют инкрементальную сборку всей Аркадии способом эквивалентным `ya make -tt autocheck`. Иногда при разработке библиотеки забывают добавить её в список `RECURSE`'ов родительской директории, но при этом разумеется добавляют `PEERDIR` зависимость на неё, там, где требуется. При этом сборка этой библиотеки в автосборке происходит, но её тесты в автосборке не собираются и не запускаются. Всегда добавляйте новые модули в список `RECURSE`'ов родительской директории.

{% endnote %}

{% note info %}

В реальности не все проекты собираются под все платформы, поэтому какие-то из путей могут быть достижимы из ya.make только в сборке под Linux, а какие-то только в сборке с использованием JDK15. Описание того в какие платформы попадает сборка того или иного проекта поддерживается централизовано в директории `autocheck` в корне Аркадии.

{% endnote %}

## Базовые модульные макросы

### PEERDIR { #peerdir }

```
PEERDIR(Dirs..)
```

Основной макрос указания [зависимостей между модулями](../#mod_deps). Зависимости указываются ссылкой на директорию, в которой размещён файл `ya.make` c описанием сборки модуля, от которого данный и зависит.

{% note warning %}

Чтобы `PEERDIR` был корректным должна существовать директория, указанная в нём, в ней должен быть файл `ya.make` и этот файл должен [описывать сборку модуля](../#mod).
Если что-то из этого не выполнено, то будет выдана [ошибка конфигурации `BadDir`](../../usage/ya_make/messages#baddir).

{% endnote %}

Модуль, в котором написан `PEERDIR` зависит от модуля, на который этот `PEERDIR` ссылается.


`PEERDIR` интерпретируется с учётом типа [модуля](../../general/base_concepts#moduli), в котором он указан. Он может обозначать один из двух вариантов зависимостей:
1. Для программ и подобных им модулей `PEERDIR` означает *включение* или *необходимость для сборки*, т.е. модули в `PEERDIR` являются составными частями или требуются в построенном виде для построения данного. Например, библиотеки *включаются* в программы, а пакеты в go используют зависимые пакеты для собственной сборки. 
2. Для библиотек и подобных им модулей `PEERDIR` означает *необходимость для использования*, т.е. модули в `PEERDIR` не нужны для сборки данного, но нужны для использования. Например, когда библиотека в C++ зависит от другой, их можно собирать раздельно, но обе они должны быть влинкованы в программу, чтобы первую библиотеку можно было корректно использовать.

Интерпретация `PEERDIR` зависимости определяет состав и порядок сборки:
- Первый вариант требует построения всех зависимых модулей при сборке данного и до его сборки (результаты их сборки подаются на вход сборки данного).
- Второй вариант не требует построения зависимых модулей при сборке данного, и они не будут строиться. Например, при сборке [`LIBRARY`](../cpp/modules#library), библиотеки от которых она зависит, строиться не будут. При сборке программы [`PROGRAM`](../cpp/modules#program) все библиотеки будут строиться, но все они могут собираться параллельно без учёта зависимостей между ними.

В смысле свойств сборки `PEERDIR` является зависимостью при любой интерпретации. Это означает, что от зависимого модуля к зависящему могут передаваться *артефакты* и *свойства*. 

* Если [`LIBRARY`](../cpp/modules#library) зависит от [`PROTO_LIBRARY`](../proto/modules#proto_library), то строить их как библиотеки можно параллельно, но в библиотеке будут доступны заголовочные файлы `.pb.h`, генерируемые в `PROTO_LIBRARY`.
* Если в библиотеке [`LIBRARY`](../cpp/modules#library) указан макрос добавления путей в поиск [`ADDINCL(GLOBAL ...)`](#addincl), то все зависящие от ней библиотеки тоже будут использовать этот путь для поиска include в своих файлах, хоть и строиться они будут независимо.

{% note info %}

`PEERDIR` — это всегда зависимость времени сборки, а не времени исполнения. Например, если из [`PY3TEST`](../python/modules#pytestpy3test) разрешён `PEERDIR` на [`PY3_PROGRAM`](../python/modules#py3_program) это означает, что программа будет включена в *сборку* теста ([см. ниже](#peer_multi)), а не будет доступна во время его исполнения.
Для выражения зависимости времени исполнения теста используется макрос [`DEPENDS`](../tests/common#depends). 

Однако в определённых случаях зависимые модули могут расширять набор выходных артефактов (транзитивно) зависящего от них модуля. Так `PACKAGE` содержит, как часть своих результатов, копии программ, которые включены в него через `PEERDIR`.

{% endnote %}

#### Ограничения { #restr }

Не любые `PEERDIR` допустимы. Зависимости между модулями могут быть ограничены как по естественным причинам (зависимость не имеет интерпретации), так и искусственно (явными правилами). Для PEERDIR есть следующий набор ограничений:

- [*Промежуточные модули*](../../general/base_concepts#moduli) (вроде [`LIBRARY`](../cpp/modules#library)) не могут зависеть от *финальных* (вроде [`PROGRAM`](../cpp/modules#program)).
- *Сборочные модули* (любые `xPROGRAM`, `xLIBRARY`, `xTEST`) не могут зависеть от агрегирующих модулей `PACKAGE` и `UNION`.
- Никакие модули не могут зависеть от тестов*.
- Модули размечены *тэгами* для контроля совместимости. Недопустимы `PEERDIR` между несовместимыми модулями, например [`PY2_LIBRARY`](../python/modules/py2_library) не может зависеть от [`PY3_LIBRARY`](../python/modules/py2_library) и наоборот.
  Поскольку программа на Python содержит интерпретатор фиксированной версии, то она целиком должна быть на одной версии Python: [`PY2_PROGRAM`](../python/modules/py2_program) только на 2-й, [`PY3_PROGRAM`](../python/modules/py3_program) только на 3-й.
  Через *тэги* также ограничены зависимости, не имеющие интерпретации в рамках системы сборки. Так система сборки просто не знала бы что делать с зависимостью [`LIBRARY`](../cpp/modules#library) от [`JAVA_LIBRARY`](../java/modules#java_library).
- Зависимости могут быть ограничены на основе их расположения автоматически для директорий `internal` и политиками. Такие ограничения описаны в [специальном разделе документации](./peerdir_rules).

{% note info %}

\* Тестовые модули в Java могут зависеть друг от друга за счёт того, что в реальности такой модуль — это *мультимодуль* из собственно теста и java-библиотеки с кодом.
Таким образом, в реальности эта зависимость превращается ([см. ниже](#peer_multi)) в зависимость одного теста от библиотеки с кодом другого. В частности, это означает, что при запуске зависящего теста зависимый запущен не будет.

{% endnote %}

#### PEERDIR и мультимодули { #peer_multi }

Система сборки ya make поддерживает [**мультимодули**](../#multi). По сути, это способ описать различное поведение сборки одного и того же исходного кода для разных вариантов использования.
Собственно вариант использования в случае `PEERDIR` на *мультимодуль* определяется типом модуля, в котором этот `PEERDIR` написан. 

**Примеры:**
- Для мультимодуля типа [`PROTO_LIBRARY`](../proto/modules#proto_library), если `PEERDIR` написан в [`JAVA_LIBRARY`](../java/modules#java_library), то получится зависимость на Java-вариант, а если из [`LIBRARY`](../cpp/modules#library), то на C++-вариант.
- `PY23_LIBRARY` — это мультимодуль, позволяющий собирать Python-код как в варианте для Python 3 (как [PY3_LIBRARY](../python/modules#py3_library)), так и для Python 2 (как [PY2_LIBRARY](../python/modules#py2_library)). Соответственно `PEERDIR` из [`PY3_PROGRAM`](../python/modules#py3_program) выберет вариант для Python 3. Что важно, далее этот вариант для своих зависимостей будет делать такой же выбор (что в точности соответствует поведению [PY3_LIBRARY](../python/modules#py3_library)).
- `PY3_PROGRAM` является мультимодулем из библиотеки с кодом, аналогичной [`PY3_LIBRARY`](../python/modules#py3_library) и собственно исполняемой программы, аналогичной [`PROGRAM`](../cpp/modules#program) со встроенным интерпретатором Python 3. Это позволяет иметь на неё `PEERDIR` из [`PY3TEST`](../python/modules#pytestpy3test), который сделает код программы (библиотечную часть) доступным в коде теста.

{% note info %}

Выбором вариантов в мультимодулях управляют ровно те же *тэги*, которые используются для [контроля совместимости](#restr). Т.е., по сути, общее правило такое:

*`PEERDIR` разрешён только на совместимый модуль, если вариантов несколько совместимый будет выбран из них, если варианта нет совсем или нет совместимого, то будет выдана [ошибка конфигурации `BadDir`](../../usage/ya_make/messages#baddir)*.

{% endnote %}

### SRC

```
SRC(Source Flags...)
```

Добавляет в сборку один файл исходного кода. 

Файл будет обработан [на основе его расширения](../extensions#srcs). К стандартной обработке будут добавлены флаги `Flags...` специфичные для команды обработки этого типа файлов (напр. флаги компиляции для С++).

### SRCS { #srcs }

```
SRCS( ([GLOBAL] Files)... )
GLOBAL_SRCS(Files...)
```

Добавляет в сборку набор файлов исходного кода. Файл будет обработан [на основе его расширения](../extensions#srcs). 

{% note warning %}

Кроме того, что макрос `SRCS` работает по-разному для разных файлов, перечисленных в них, он также работает по-разному в зависимости от модуля, в котором написан и даже в зависимости от варианта мультимодуля, который выбран.
- Макрос `SRCS` в модулях [GO_LIBRARY](../go/modules#go_library), [GO_PROGRAM](../go/modules#go_program) и т.п. поддерживает исходный код на языке go, а модули на других языках их не поддерживают.
- в `PROTO_LIBRARY` файлы `.proto` обрабатываются по-разному для разных языковых вариантов.

{% endnote %}

Параметр `GLOBAL` позволяет гарантировать включение результат сборки файла в программу, как если бы объектный файл, полученный от сборки указанного исходного файла, подавался бы в строку линковки непосредственно. Он действует только на один файл, указанный сразу после него.
Макрос `GLOBAL_SRCS` эквивалентен указанию `GLOBAL` в макросе `SRCS` перед всеми перечисленными файлами.

{% note info "Обратите внимание" %}

Для сборки кода на Python и Java исходные файлы указываются в макросах [PY_SRCS](../python/macros#py_srcs) и [JAVA_SRCS](../java/macros#java_srcs) соответственно.
Это связано с тем, что исходные файлы при сборке для этих языков обрабатываются специальным образом и макросы позволяют указывать специальные параметры для этой обработки.

При этом:
- Модули Python поддерживают макрос `SRCS` для исходного кода на С++ и других поддержанных в макросе языках. Использование этого макроса вызовет обработку файлов до объектного кода и складывание этого кода в библиотеку. Код будет влинкован в программу и может быть использован из кода на Python через биндинги, например через Cython.
- Модуль [`EXTERNAL_JAVA_LIBRARY`](../java/modules#external_java_library) поддерживает очень ограниченный набор типов файлов в макросе `SRCS` через специальную обработку файлов, совместимую с Java (порождение .java или .jsrc на выходе).

{% endnote %}

### SRCDIR

```
SRCDIR(
     Dirs...
)
```

Макрос `SRCDIR` позволяет добавить пути для поиска исходных файлов (входов для команд). Относительного этого пути будут искаться не только исходные файлы в макросе [`SRCS`](#srcs) и подобных, он будет действовать на поиск
входных файлов во всех макросах, например в параметре `<value>` макроса [`RESOURCE`](#resource) или в параметре `IN` макроса `RUN_PROGRAM`. 

При описании сборки в `ya.make` входные файлы могут быть указаны полностью, даже с указанием `${ARCADIA_ROOT}` (дерево исходного кода) или `${ARCADIA_BUILD_ROOT}` (дерево генерированных файлов). Однако в большинстве случаев
указывается относительный путь, который система сборки должна превратить в путь до реального файла или в дереве исходного кода, или в дереве генерированных файлов. Этот процесс называется [*резолвинг*](../../general/how_it_works.md#resolving).
Он пытается подобрать директорию относительно которой указано имя файла. По умолчанию список директорий для такого поиска — это `${ARCADIA_ROOT}` (дерево исходного кода), `${ARCADIA_BUILD_ROOT}` (дерево генерированных файлов), 
`${CURDIR}` (директория проекта в дереве исходного кода). Именно в таком порядке.

Макрос `SRCDIR` позволяет добавить к списку другие директории.
- Директории в дереве исходного кода должны указываться от корня Аркадии, указание `${ARCADIA_ROOT}` не обязательно.
- Директории в дереве генерированных файлов (если хочется указывать генерированные файлы не от корня) должны быть обязательно указаны от корня с указанием самого корня, т.е. `${ARCADIA_BUILD_ROOT}/1st_level_dir/subdir`

{% note warning %}

1. Мы не рекомендуем использовать макрос `SRCDIR` без необходимости: в большинстве случаев он осложняет понимание описания сборки поскольку скрывает реально размещение исходных файлов из макросов.
2. Не используйте макрос `SRCDIR` чтобы использовать один и те же исходные файлы в разных модулях. Каждый файл анализируется системой сборки один раз и *воровство исходных файлов* может приводить к потерянным зависимостям, неоднозначностям описания сборки и другим проблемам.

{% endnote %}

### ADDINCL { #addincl }

```
ADDINCL(
     ([GLOBAL|ONE_LEVEL] [FOR lang] Dirs)...
)
```

Добавление путей поиска файлов для include/import конструкций в файлах, участвующих в сборке модуля. 

Система сборки [анализирует исходные файлы, чтобы найти зависимости между файлами}(../#file_to_file). Она получает список имён, по которому ей надо найти реальные файлы. Этот процесс называется [*резолвинг*](../../general/how_it_works.md#resolving).
Он пытается подобрать директорию относительно которой указано имя файла. При этом для инклудов и импортов этот процесс отличается от такого же для исходных файлов:

- Поиск может быть разным для разных типов файлов.
- Часть файлов может вообще отсутствовать в Аркадии (например, загружаться вместе со сборочными инструментами). С другой стороны, может иметься в виду несколько разных файлов. 
  Что делать в таком случае написано в [разделе об инклудах в С++](../cpp/includes).
- По умолчанию список директорий для такого поиска — это `.` (путь до файла, в котором написан include, применяется не всегда), `${ARCADIA_BUILD_ROOT}` (дерево генерированных файлов), `${ARCADIA_ROOT}` (дерево исходного кода). 
  Сравните этот список с аналогичным в описании [`SRCDIR`](#srcdir).

Кроме [*резолвинга*](../../general/how_it_works.md#resolving) `ADDINCL` передаёт пути поиска в соответствующие инструменты, чтобы при исполнении сборки уже сами инструменты могли найти упомянутые файлы.

Макрос `ADDINCL` позволяет задавать директории поиска для каждого языка независимо. Так как самый большой потребитель данного макроса это C++, то при использовании без указания языка директории считается директорией поиска для C++. Помимо этого, можно управлять 
путями поиска включаемых файлов для следующих языков:

 * `ADDINCL(FOR asm ...)`
 * `ADDINCL(FOR asp ...)`
 * `ADDINCL(FOR c ...)` дефолт если не указан язык явно
 * `ADDINCL(FOR cython ...)`
 * `ADDINCL(FOR flatc ...)`
 * `ADDINCL(FOR fortran ...)`
 * `ADDINCL(FOR gperf ...)`
 * `ADDINCL(FOR idl ...)`
 * `ADDINCL(FOR lex ...)`
 * `ADDINCL(FOR nlg ...)`
 * `ADDINCL(FOR proto ...)`
 * `ADDINCL(FOR ragel ...)`
 * `ADDINCL(FOR sc ...)`
 * `ADDINCL(FOR swig ...)`
 * `ADDINCL(FOR xs ...)`
 * `ADDINCL(FOR xsyn ...)`
 * `ADDINCL(FOR yacc ...)`
 * `ADDINCL(FOR ydl ...)`

Существует возможность указать директории поиска включаемых файлов не только для текущего модуля, но и тех модулей, которые от
него зависят, используя модификатор `GLOBAL` или `ONE_LEVEL`. Это важный сценарий, когда одна сторонняя библиотека в своих
заголовочных файлах включает заголовочные файлы другой сторонней библиотеки. Тогда дополнительные флаги с директориями поиска
нужно передать не только при компиляции самой библиотеки, но и тех библиотек, которые её используют. 

- `GLOBAL` действует на все модули, зависящие от данного как напрямую, так и транзитивно через другие модули. Это позволяет использовать
  имя файла относительно такого пути поиска как в исходных, так и в заголовочных файлах, распространяя их использование по зависимостям.
- `ONE_LEVEL` действует только на модули, зависящие от данного напрямую. Для такого поведения есть два сценария:
  1. Путь поиска для приватного интерфейса, который должен использоваться только другими частями реализации библиотеки,
     но не должен быть доступен внешним пользователям.
  2. Путь поиска обеспечивает резолвинг локальных файлов с распространёнными именами (например, `stdio.h` или `config.h`). В норме такие имена должны
     ссылаться или на стандартную библиотеку или на файл той библиотеки, к которой относится код. `ADDINCL(GLOBAL)` привёл бы к нежелательному резолвингу
     в зависящих модулях, а писать `ADDINCL()` во всех прямых пользователях может быть неудобно и тяжело в поддержке.

В макросе `ADDINCL`:
- Директории в дереве исходного кода должны указываться от корня Аркадии, указание `${ARCADIA_ROOT}` не обязательно.
- Директории в дереве генерированных файлов (если хочется указывать генерированные файлы не от корня) должны быть обязательно указаны от корня с указанием самого корня, т.е. `${ARCADIA_BUILD_ROOT}/1st_level_dir/subdir`

{% note warning %}

В Аркадии рекомендуется все импорты и инклуды делать только от корня аркадии, механизм `ADDINCL` предназначен для работы с `contrib` кодом либо кодом, который выкладывается в `opensource`.

{% endnote %}

### RUN_PROGRAM/PYTHON/RUN_PYTHON3/RUN_LUA { #run_program }

```
RUN_PROGRAM(
    tool_path args...
    [CWD dir]
    [ENV key=value...]
    [TOOL tools...]
    [IN[_NOPARSE] inputs...]
    [OUT[_NOAUTO] outputs...]
    [STDOUT[_NOAUTO] output]
    [OUTPUT_INCLUDES output_includes...]
)
```

```
PYTHON(
    script_path args...
    [CWD dir]
    [ENV key=value...]
    [TOOL tools...]
    [IN[_NOPARSE] inputs...]
    [OUT[_NOAUTO] outputs...]
    [STDOUT[_NOAUTO] output]
    [OUTPUT_INCLUDES output_includes...]
)
```

```
RUN_PYTHON3(
    script_path args...
    [CWD dir]
    [ENV key=value...]
    [TOOL tools...]
    [IN[_NOPARSE] inputs...]
    [OUT[_NOAUTO] outputs...]
    [STDOUT[_NOAUTO] output]
    [OUTPUT_INCLUDES output_includes...]
)
```

```
RUN_LUA(
    script_path args...
    [CWD dir]
    [ENV key=value...]
    [TOOL tools...]
    [IN[_NOPARSE] inputs...]
    [OUT[_NOAUTO] outputs...]
    [STDOUT[_NOAUTO] output]
    [OUTPUT_INCLUDES output_includes...]
)
```

Макросы для запуска производной кодогенерации с помощью, программы, собранной из Аркадии (`RUN_PROGRAM`) или скрипта на Python (2-й или 3-й версии) или Lua. Основные принципы работы и значение параметров совпадают для всех макросов и будут описаны ниже, отличается только первый параметр.
- В `RUN_PROGRAM` первым параметром указывается путь до директории с `ya.make`, описывающем сборку программы ([`PROGRAM`](../cpp/modules#program), [`GO_PROGRAM`](../go/modules#go_program), [`PY3_PROGRAM`](../python/modules#py3_program) и т.п.). В процессе сборки эта программа будет собрана под [сборочную платформу](../../general/base_concepts#configs) и исполнена для генерации исходного кода. При этом все зависимости программы будут автоматически учтены и при их изменении программа будет перестроена и перезапущена.
- В остальных макросах первым параметром указывается путь до файла скрипта от корня Аркадии. Как в случае Lua, так и в случае Python скрипт не будет компилировать и будет исполняться интерпретатором. Если скрипт зависит от других фалов их надо будет явно указать в параметре `IN` иначе они не будут учтены.

Следующие параметры общие для всех макросов.

* `args` - Аргументы программы или макроса. Будут переданы в командной строке.

    {% note warning %}

    Если аргументы соответствуют именам, указанным в `IN`, `IN_NOPARSE`, `OUT` или `OUT_NOAUTO`, то аргументы будут превращены в абсолютные пути, соответствующие файлам в этих параметрах. Считается, что они в точности являются входными или выходными файлами, указанными в параметрах.
    На данный момент не существует способа отключить это поведение. Если оно не желательно, рекомендуется каким-то образом изменить имя в параметре (например, добавить символ в конце) и затем обработать эту ситуацию в программе/скрипте либо добавить параметр, соответствующий корню Аркадии в исходном или сборочном дереве и на основе него рассчитать относительный путь.

    {% endnote %}

* `CWD` - Директория, к которой надо запускать программу/скрипт. Путь надо указывать вместе с корнем вроде `${ARCADIA_ROOT}`, `${BINDIR}` и т.п.

    {% note warning %}

    На данный момент у этого параметра нет фиксированного умолчания (локально и в distbuild оно разное), поэтому если рабочая директория важна, указывайте её явно.

    {% endnote %}

* `ENV` - Переменные среды для запуска программы/скрипта. Если программе/скрипту нужны переменные среды - указывайте их явно. Наличие переменных `$HOME`, `$PATH` и подобных в среде исполнения программы/скрипта не гарантируется.

* `TOOL` - Пути до `ya.make` собирающих программы, динамические библиотеки и т.п. нужные вовремя **исполнения** программы или скрипта. Все они будут собраны под [сборочную платформу](../../general/base_concepts#configs) и доступны по своим путям в Аркадии от `${ARCADIA_BUILD_ROOT}` во время исполнения программы/скрипта.

* `IN[_NOPARSE]` - входные файлы для запуска программы/макроса. Могут быть как явными, так и неявными. Явные должны быть указаны в `Args`, значения из `IN` и `IN_NOPARSE` сами по себе в командную строку не попадают, но засчитываются как зависимости для запуска программы/скрпипта.
  - Значения параметров `IN` и `IN_NOPARSE` [отображаются на реальные файлы (*резолвятся*)](../../general/how_it_works.md#resolving) по правилам для входных файлов.
  - Обязательно указывайте все файлы, используемые генератором (явно или неявно) в этом параметре. Иначе, при изменении неуказанного файла не случится перегенерация.
  - Файлы из параметра `IN` [анализируются на наличие зависимостей на основе их расширений](../extensions#parsing) если расширение поддержано в анализе (*парсинге*). Чтобы отключить это поведение для файлов, у которых оно по расширению включено, используйте параметр `IN_NOPARSE`, перечисленные в нём файлы интерпретируются как простой текст и не анализируются.
  
   {% note tip %}
    
   Проверить, что все нужные файлы указаны, можно запустив сборку с параметром `--sandboxing`. Это ограничит доступные команде генерации файлы, только описанными.
   
   {% endnote %}

* `OUT[_NOAUTO]` - выходные файлы, порождаемые генератором. Если путь не указан явно с `${ARCADIA_BUILD_ROOT}`, то он считается относительным от `${BINDIR}`.
  - Все генерируемые файлы должны быть выписаны явно. Без этого знания система сборки не сможет встроить результаты в дальнейшую обработку. Если результатов много, можно запаковать их генератором в .tar-архив и обрабатывать в следующих командах уже весь архив целиком, описав его там как вход.
  - Файлы из параметра `OUT` далее [автоматически обрабатываются в соответствии с их расширением](../extensions#srcs). Чтобы отключить эту обработку или если правил обработки нет, используйте вариант `OUT_NOAUTO`.

* `STDOUT[_NOAUTO]` - параметр, аналогичный `OUT`, но для случая, когда генератор пишет в стандартный поток вывода (`stdout`). Система сборки сама запишет вывод в файл с заданным именем. 
  - По понятным причинам может быть указан только один файл как `STDOUT` или `STDOUT_NOAUTO`.

* `OUTPUT_INCLUDES` - параметр, позволяющий задать зависимости генерируемых файлов. Для исходных файлов система сборки [выводит зависимости анализом файла по расширению](../extensions#parsing).
  Поскольку [граф зависимостей строится до исполнения сборки](../../general/base_concepts#conf), то генерируемые файлы так обработать не выйдет. В этом случае зависимости надо указывать явно.
  `OUTPUT_INCLUDES` позволяет перечислить файлы, от которых зависят результаты генерации. Эти файлы будут предоставлены командам обработки генерируемых файлов, а не команде генерации.
  При их изменении будет перезапущена обработка результатов генерации (например, компиляция), а не сама генерация. { #output_includes }

  {% note warning %}
  
  - Все файлы, перечисленные в этом параметре, являются зависимостями всех файлов, описанных в `OUT[_NOAUTO]` и [`STDOUT[_NOAUTO]`. Не предусмотрено более тонкой фильтрации ни по именам выходов ни по расширениям.
  - Для определённых расширений система сборки считает файлы зависимостями не только самих результатов генерации, но зависимостями результатов обработки файлов генерации (например, при генерации `.pyx` это будут и другие `,pyx` и `.h` в `.pyx`). Здесь также не предусмотрено фильтрации.
  - Не указывайте в этом параметре значения из параметров `OUT[_NOAUTO]` и [`STDOUT[_NOAUTO]` — это создаст цикл по зависимостям файла от самого себя и может привести к проблемам в анализе зависимостей таких файлов.
 
  {% endnote %}


  {% note tip %}

  Если программа-генератор, используемая в `RUN_PROGRAM` всегда генерирует одинаковые зависимости (или часть зависимостей), то она может описать их в своём модуле макросом [`INDUCED_DEPS`](#induced_deps),
  в этом случае их не надо будет перечислять в `OUTPUT_INCLUDES` всех `RUN_PROGRAM` этого генератора.

  {% endnote %}

* Все списочные параметры такие как `ENV`, `IN`, `OUT` и т.п. могут встречаться в вызове макроса несколько раз. Значением параметра является список строк, разделённых по пробелам от имени параметра до имени другого параметра или закрывающей скобки.

    **Например для a/b/ya.make:**
    ```
    RUN_PYTHON3(run.py x.inp xx.inp y.cpp ENV X=xx IN x.inp xx.inp OUT y.cpp IN .run.settings ENV HOME=${CURDIR})

    # Командная строка:
    #   python3 ${ARCADIA_ROOT}/a/b/run.py ${ARCADIA_ROOT}/a/b/x.inp ${ARCADIA_ROOT}/a/b/xx.inp ${ARCADIA_BUILD_ROOT}/a/b/y.cpp
    # Параметры:
    #   ENV=['X=xx', 'HOME=${ARCADIA_ROOT}/a/b']
    #   IN=['${ARCADIA_ROOT}/a/b/x.inp', '${ARCADIA_ROOT}/a/b/xx.inp', '${ARCADIA_ROOT}/a/b/.run.settings']
    #   OUT=['${ARCADIA_BUILD_ROOT}/a/b/y.cpp']
    ```
Параметры `OUT_NOAUTO` или `STDOUT_NOAUTO` одного макроса могут присутствовать в `IN`, `IN_NOPARSE` и `OUTPUT_INCLUDES` другого. При чём они могут быть написаны как в одном ya.make, так и в разных. Важно чтобы `ya.make` с 
макросом-потребителем имел [`PEERDIR`](#peerdir) зависимость (хотя бы транзитивную) на ya.make c макросом-источником.

**__Пример:__**
```
RUN_PYTHON3(${ARCADIA_ROOT}/devtools/dummy_arcadia/includes/gen.py
    inc.h.gen inc.h
    # preinc.h генерируется выше и является зависимостью команды генерации
    # Эта команда запустится второй и получит результат первой на вход
    IN inc.h.gen preinc.h
    OUT inc.h
)

RUN_PYTHON3(${ARCADIA_ROOT}/devtools/dummy_arcadia/includes/gen.py
    preinc.h.gen preinc.h
    IN preinc.h.gen
    OUT preinc.h       # Этот результат употребится верхним макросом
    # inc.h ниже генерируется верхним макросом, но это ОК, поскольку OUTPUT_INCLUDES — это
    # зависимости не команды генерации, а её результата. Эта команда выполнится первой
    OUTPUT_INCLUDES ${ARCADIA_BUILD_ROOT}/devtools/dummy_arcadia/includes/ambig_source_generated_from_gen/inc.h
)

```

### COPY_FILE/COPY_FILE_WITH_CONTEXT { #copy_file }

```
COPY_FILE(
    Src Dst
    [AUTO]
    [OUTPUT_INCLUDES output_includes...]
)
```

```
COPY_FILE_WITH_CONTEXT(
    Src Dst
    [AUTO]
    [OUTPUT_INCLUDES output_includes...]
)
```

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

- Исходный файл `Src` может быть как в репозитории, так и сгенерирован при сборке. Конкретный исходный файл определяется по имени с помощью
  [разрешения имён (*резолвинга*)](../../general/how_it_works.md#resolving).

- Результирующий файл `Dst` считается относительным путём от директории модуля в сборочном дереве [`${BINDIR}`](./vars)

- `COPY_FILE` делает файл *текстовым* в том смысле, что на него не будет распространяться анализ зависимостей исходного файла. `COPY_FILE_WITH_CONTEXT` позволяет
  скопировать файл со всеми зависимостями исходного. Список зависимостей полученного файла можно описать параметром [`OUTPUT_INCLUDES`](#output_includes), так же как в макросе
  [`RUN_PROGRAM`](#run_program) и подобных. В случае `COPY_FILE_WITH_CONTEXT` это будут дополнительные зависимости, в случае `COPY_FILE` - все.

- По умолчанию скопированный файл не будет [автоматически обрабатываться по расширению](../extensions#srcs), чтобы включить такую обработку надо или явно указать
  файл в макросе [SRCS](#srcs) или использовать параметр `AUTO`.


### INDUCED_DEPS

```
INDUCED_DEPS(Kind Paths...)
```

Задать зависимости кода, генерируемого программой.

Для исходного кода система сборки может выводить зависимости сама [парсингом исходных файлов](../extensions#parsing).
Однако, поскольку это делается до исполнения сборки, то для генерируемого кода сделать то же самое она не может.
Поэтому, зависимости для такого кода надо указывать явно. В макросах [`RUN_PROGRAM` и подобных](#run_program) для этого есть параметр `OUTPUT_INCLUDES`.
Однако, если программа-генератор всегда включает в код фиксированный набор зависимостей, то можно описать их вместе с программой в модуле `PROGRAM`/`PY3_PROGRAM`/`GO_PROGRAM` и подобных.
Тогда, при использовании такой программы в качестве генератора (первого параметра макроса [`RUN_PROGRAM`](#run_program)) или дополнительного инструмента (параметр `TOOL`)
любого из [макросов генерации кода](#run_program), описанные зависимости станут зависимостями генерируемых файлов.

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

Kind | Вид зависимостей
| :--- | :--- 
go | импорты в go
h | Инклуды в хедерах С++
cpp | Инклуды в исходных файла C++
h+cpp | Инклуды в С++ (и в исходных файлах, и в хедерах).
proto | импорты в proto-файлах
idl | импорты в idl-файлах mapkit

{% note warning %}

Если генерируется .cpp-файл и .h-файл и первый включает второй, то зависимости для хедера надо описывать как `h+cpp`.
Саму зависимость .cpp от .h порождаемых одним генератором описывать не нужно: это приведёт к циклу, и сборка может работать неправильно.

{% endnote %}

Поскольку использоваться файлы будут не в том модуле, где перечислены описание не должно рассчитывать
[*резолвинг*](../../general/how_it_works.md#resolving) по месту использования, поэтому все файлы должны 
быть указаны с полными путями включая `${ARCADIA_ROOT}`/`${ARCADIA_BUILD_ROOT}`. Если зависимые файлы не всегда лежат
в Аркадии или могут ссылаться на разные файлы в разных конфигурациях рекомендуется сделать рядом с программой файл,
где сослаться на все нужные зависимости и уже его указать в макросе.

**Пример:**
```

PROGRAM()

OWNER(g:group)

SRCS(
    main.cpp
)

# Это включается и в код, и в хедер
INDUCED_DEPS(h+cpp
    ${ARCADIA_BUILD_ROOT}/kernel/web_factors_info/metadata/factors_metadata.pb.h
    ${ARCADIA_ROOT}/util/network/address.h
)

# Это включается только в код
INDUCED_DEPS(cpp
    ${ARCADIA_ROOT}/util/network/endpoint.h
)

# Это включается только в хедер
INDUCED_DEPS(h
    ${ARCADIA_ROOT}/util/network/interface.h
)

PEERDIR(
    kernel/web_factors_info/metadata
)

END()
```


### FROM_SANDBOX/LARGE_FILES

Макросы для подвоза данных в сборку и тесты описаны [здесь](./data.md)

### BUNDLE

```
BUNDLE(TargetPath [NAME ResultName])
```

Привезти результат сборки модуля в другой модуль как файл. Этот результат можно использовать как файл, например в макросе [`RESOURCE`](#resource) или как побочный результат в модуле `UNION`.
В отличие от [`PEERDIR`](#peerdir) модуль из `BUNDLE` будет не зависимостью самого модуля (команды сборки модуля), а станет входом для макросов в модуле.

- Как и в других подобных модулях `TargetPath` — это имя директории с `ya.make`, который собирает модуль, артефакт которого мы хотим получить.
- Параметр `NAME` позволяет зафиксировать имя артефакта в данном модуле.

**Пример:**


test_bundle/ya.make
```
LIBRARY()

BUNDLE(test_bundle/prg NAME prg2)

RESOURCE(prg2 /prg)

END()
```

test_bundle/prg/ya.make
```
PROGRAM()

SRCS(main.cpp)

END()
```

{% note tip %}

- Если у модуля несколько результатов, макрос `BUNDLE` всегда выбирает главный. Обычно это именно то, что ожидается (программа, динамическая библиотека и т.п.).
- Если `TargetPath` указывает на `ya.make` c [мультимодулем](../#multi), то выберется вариант, который собирает финальный артефакт (программу, динамическую библиотеку и т.п., а не статическую библиотеку). Если такой вариант не один, то будет выдана ошибка.

{% endnote %}


### RESOURCE  { #resource }

```
RESOURCE([FORCE_TEXT ][Src Key]* [- Key=Value]*)
```

Макрос для добавления произвольных данных, взятых из файлов или прямо из параметра макроса в код программы.
Для доступа к данным есть библиотеки для C++, Python и Go.

Поддерживается два варианта макроса:

1. Включение содержимого файлов:

    ```
    RESOURCE(
        path/to/file1 key/in/program/1
        path/to/file2 key2
    )
    ```

    Содержимое файлов `path/to/file1` и `path/to/file2` будет включено в код программы и доступно по ключам `key/in/program/1` и `key2` соответственно.
    Файлы, указанные в первом параметре, могут быть как файлами в Аркадии, так и генерируемыми. Во втором случае мы рекомендуем использовать префикс `${ARCADIA_BUILD_ROOT}` или `${BINDIR}` для упрощения понимания происходящего.

2. Включение строки как ресурса

    ```
    RESOURCE(- my/key=my/value)
    ```

    Строка my/value будет включена в код программы и доступна по ключу `my/key`. Этот механизм может быть, в частности, полезен,
    чтобы передать информацию из сборки в программу на Python, например.

    ```
    IF (WITH_FEATURE_X)
        RESOURCE(- featuters/x=enabled)
    ENDIF()
    ```
    
    В С++ того же можно было бы добиться через `CFLAGS(-DFEATURE_X)`, но в Python ничего подобного нет.

{% list tabs %}

- C++

    Поддержка доступа к ресурсам реализована в `library/cpp/resource`.

    В `ya.make`:

    ```yamake
    PEERDIR(
        library/cpp/resource
    )
    ```

    В `source.cpp`:

    ```cpp
    #include <library/cpp/resource/resource.h>

    #include <util/stream/ios.h>

    int main() {
        Cout << NResource::Find("key/in/program/1") << Endl;
        Cout << NResource::Find("key2") << Endl;
    }
    ```

    * `key/in/program/1` и `key2` не должны содержать названия файлов `path/to/file1` и `path/to/file2`.

- Python

    В питоне нужно использовать `library/python/resource`

    ```yamake
    PEERDIR(
        library/python/resource
    )
    ```

    В `source.py`:

    ```python
    from library.python import resource
    r1 = resource.find("key/in/program/1")
    r1 = resource.find("key2")
    ```

- Go

    В go для доступа к ресурсам используйте модуль `a.yandex-team.ru/library/go/core/resource`.

    Подгружать ресурс, когда вам нужно как-то парсить его данные можно, например, так:

    ```go
    var resourceParsedData ResourceType
    var initResourceOnce = sync.Once{}

    func GetResource() ResourceType {
        initResourceOnce.Do(parseResource)

        return resourceParsedData
    }

    func parseResource() {
        resourceParsedData = ResourceType{}

        jsonData := resource.Get("/key2")
        if jsonData == nil {
            return
        }

        _ = json.Unmarshal(jsonData, &resourceParsedData)
    }
    ```

    {% note warning %}

    1. Из-за особенностей инициализации ресурсов, нельзя просто так взять и создать свой собственный пакет, в котором подгружать содержимое ресурса в init'е пакета. Так вам стабильно будет возвращаться пустые данные (будто ресурса не существует)

    2. Механизмы включения ресурсов в код для С++/Python и Go не совместимы между собой. Ресурсы, включённые в код в C++ библиотеке и влинкованные через CGO, не будут доступны через go-библиотеку для доступа к ресурсам.

    3. Механизм включения ресурсов в код для Go макросом `RESOURCE` не совместим с `go embed`. Механизм `go embed` поддерживается системой сборки ya make, см. [описание в документации по go](../go/macros#embed)

    {% endnote %}

{% endlist %}

* По умолчанию система сборки [анализирует](../extensions#parsing) все входные файлы на наличие зависимостей от других файлов. 
  Обычно для файлов, включаемых как ресурсы это не требуется. К сожалению, тотальное отключение парсинга оказалось пока слишком дорогим.
  Чтобы отключить парсинг используйте параметр `FORCE_TEXT` если хочется включить файл, для которого такой анализ производится.

  {% note alert %}
  
  Параметр `FORCE_TEXT` не доступен в макросе RESOURCE для go.

  {% endnote %}

Важно отличать ресурсы, включаемые макросом `RESOURCE` от ресурсов в Sandbox. Макрос `RESOURCE` позволяет включать файлы, доступные в сборке, а также строки.
Если вы хотите включить содержимое ресурса Sandbox, нужно использовать `FROM_SANDBOX` для получения данных в сборке. 
Тогда `ya.make` будет выглядеть так: 

```
FROM_SANDBOX(1234567 OUT_NOAUTO path/to/file1)  # 1234567 - номер ресурса, path/to/file1 - файл, который нужно извлечь из ресурса.
RESOURCE(${BINDIR}/path/to/file1 sb/data/file1) # Данные скачанного файла будет доступны в программе по ключу sb/data/file1
```

### RESOURCE_FILES {#resource_files}

```
RESOURCE_FILES(Files...)

```

Макрос `RESOURCE_FILES` — надстройка над макросом `RESOURCE` со следующими свойствами:

- в `RESOURCE_FILES` можно просто перечислять файлы (в `RESOURCE` для каждого файла нужно указать, по какому ключу его сохранить);
- `RESOURCE_FILES` запоминает пути к файлам.

Пути к файлам полезны для диагностики (программа может сказать, откуда взялся ресурс), для опционального чтения файлов с файловой системы (это используется с [Y_PYTHON_SOURCE_ROOT](../python/vars#y_python_source_root)), для распаковки ресурсов на файловую систему.

#### Реализация

`RESOURCE_FILES` является пространством имён, вложенным в пространство имён `RESOURCE`. Если в `aa/ya.make` написано `RESOURCE_FILES(PREFIX bb/ c/d)` и существует файл `aa/c/d` с текстом `e`, то имеется:

- resource file with key=`bb/c/d`, value=`e`, src=`aa/c/d` 
- resource with key=`resfs/file/bb/c/d`, value=`e`
- resource with key=`resfs/src/resfs/file/bb/c/d`, value=`aa/c/d`

#### Python API

`__res` определяет и `library.python.resource` реэкспортирует следующие функции:

- `resfs_files()` = `['bb/c/d', …]`
- `resfs_read('bb/c/d')` = `'e'`
- `resfs_src('bb/c/d', resfs_file=True)` = `'aa/c/d'`
- `resfs_src('resfs/file/bb/c/d')` = `'aa/c/d'`
- `resfs_resolve('aa/c/d')` = `'/home/user/arc/aa/c/d'`, если этот файл существует при env `Y_PYTHON_SOURCE_ROOT=/home/user/arc`, иначе None

{% note warning %}

приложениям на Python не надо использовать это API, если подходят стандартные [pkgutil.get_data](https://docs.python.org/2/library/pkgutil.html#pkgutil.get_data) и [pkg_resources](https://setuptools.readthedocs.io/en/latest/pkg_resources.html#resourcemanager-api).

{% endnote %}

#### Реализация PY_SRCS в Python 3

Механизм `RESOURCE_FILES` используется в сборке питона в [исполняемые программы](../python/#python_program)

Для модуля `c.f` из файла `aa/bb/c/f.py`, создаётся:

- resource file with key=`py/c/f.py`, value=содержимое `f.py`, src=`aa/bb/c/f.py`
- resource file with key=`py/c/f.py.yapyc3`, value=байткод `f.py`, src=`aa/bb/c/f.py.yapyc3`

(`aa/bb/c/f.py.yapyc3` это реальный файл в build root на выходе от [прекомпилятора Python pycc](https://a.yandex-team.ru/arc/trunk/arcadia/contrib/tools/python3/pycc).)

#### Взаимодействие с pkgutil.get_data

Пусть имеется `aa/ya.make` такого вида:

```yamake
OWNER(the_owner)

PY3_LIBRARY()

PY_SRCS(TOP_LEVEL 
        bb/__init__.py
        bb/f.py)

RESOURCE_FILES(PREFIX aa/ bb/c/d)

END()
```

В Аркадии лежат файлы `aa/bb/__init__.py`, `aa/bb/f.py`, и `aa/bb/c/d` с содержимым `e`. Тогда `f.py` может сделать `pkgutil.get_data(__package__, 'c/d')` и получить `e`, потому что:

```python
__package__ == 'bb'
bb.__file__ == 'aa/bb/__init__.py'
resource_name = join(dirname(bb.__file__), 'c/d')
resource_name == 'aa/bb/c/d'
get_data = resfs_read # для относительных путей
get_data('aa/bb/c/d') == 'e'
```

С `Y_PYTHON_SOURCE_ROOT=/home/user/arc`:

```python
bb.__file__ == '/home/user/arc/aa/bb/__init__.py'
resource_name == '/home/user/arc/aa/bb/c/d'
get_data(path) = open(path).read # для абсолютных путей
get_data('/home/user/arc/aa/bb/c/d') == 'e'
```

## Межмодульные ограничения

### LICENSE

```
LICENSE(license...)
```

Указывает лицензию текущего модуля. Позволяет указать более одной лицензии для библиотек, распространяющихся под несколькими лицензиями. По умолчанию модули, не прописавшие свою лицензию через макрос `LICENSE` имеют лицензию, заданную переменной `DEFAULT_MODULE_LICENSE` в [`ymake.core.conf`](../../extension/core_conf.md)

В качестве имени лицензии можно использовать только строку без пробелов (пробел является разделителем между именами лицензий в случае модулей, распространяющихся под несколькими лицензиями). Для используемого имени лицензии в файле [`ymake.core.conf`](../../extension/core_conf.md) должны быть описаны её свойства с помощью служебных переменных `LICENSE_PROPERTIES`, `LICENSES_<PROP_NAME>_STATIC`, `LICENSES_<PROP_NAME>_DYNAMIC` или `LICENSES_<PROP_NAME>`. Лицензия, для которой не описаны её свойства считается неизвестной и её использование в макросе `LICENSE` диагностируется как ошибка конфигурации.

Если в качестве аргумента макроса `LICENSE` указано несколько лицензий, то проект, зависящий от такой библиотеки обязан соблюдать все требования хотя бы одной из перечисленных лицензий (любой).

### RESTRICT_LICENSES

```
RESTRICT_LICENSES(ALLOW_ONLY|DENY LicenseProperty... EXCEPT LibPath...)
```

Макрос задаёт ограничение на лицензии библиотек, используемых текущим модулем напрямую или транзитивно. Если макрос используется несколько раз для одного и того же модуля, то все его вызовы должны иметь один и тот же тип ограничения (все `ALLOW_ONLY` либо все `DENY`).

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

В качестве свойства лицензии используется строка, не содержащая пробелов, перечисленная в переменной `LICENSE_PROPERTIES` в файле [`ymake.core.conf`](../../extension/core_conf.md).

Лицензии могут налагать разные ограничения на проекты, линкующиеся с лицензируемой библиотекой статически и динамически. При описании свойств лицензии указывается какие из них налагают требования на проект линкующийся с библиотекой статически, а какие ограничивают только тех, кто линкуется динамически. Ограничения, прописанные в макросе `RESTRICT_LICENSES` автоматически учитывают тип линковки с используемой библиотекой правильно. Для разных языков программирования статическая и динамическая линковка (с учётом нюансов аркадийной сборки) понимается следующим образом:

 * Компилирующиеся в нативный код языки (C++, Go): обычным образом
 * Python: так как мы складываем питон код в ресурсы герметичного бинарного файла, то все PEERDIR зависимости между модулями на python считаются статической линковкой.
 * JVM языки (Java, Kotlin): PEERDIR между библиотеками считается динамической линковкой, аналогично тому, как зависимости через classpath трактует [FSF в отношении LGPL](https://www.gnu.org/licenses/lgpl-java.en.html). Зависимость от JNI кода считается динамической линковкой.

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

### CHECK_DEPENDENT_DIRS

```
CHECK_DEPENDENT_DIRS(ALLOW_ONLY [PEERDIRS|ALL] restrictions...)
CHECK_DEPENDENT_DIRS(DENY [PEERDIRS|ALL] restrictions... EXCEPT exception)
```

Данный макрос позволяет потребовать, чтобы среди прямых и транзитивных зависимостей модуля не было нежелательных библиотек либо не использовались нежелательные сборочные инструменты. В одном модуле можно задать либо чёрный список через `CHECK_DEPENDENT_DIRS(DENY ...)` либо белый список через `CHECK_DEPENDENT_DIRS(ALLOW_ONLY ...)`. Если в одном модуле макрос используется несколько раз, то тип ограничения во всех местах вызова должен быть одинаковым (либо везде `DENY` либо везде `ALLOW_ONLY`), нарушения данного правила диагностируются как ошибки конфигурации. Ограничения от всех вызовов суммируются.

Сами ограничения можно задать двумя способами. Во-первых, это может быть путь в аркадии, тогда все модули, живущие в поддиректориях этого пути, подпадают под ограничение. Во-вторых, можно использовать ant-подобный шаблон пути, используя ключевое слово `GLOB`, и в этом случае ограничения действуют только на пути, полностью ему соответствующие, но не на их поддиректории. Например: `CHECK_DEPENDENT_DIRS(DENY some/nested/path/bad)` запретит зависимость как от `some/nested/path/bad` так и от `some/nested/path/bad/lib`, в то время как `CHECK_DEPENDENT_DIRS(DENY GLOB **/nes?ed/*/bad)` запретит от `some/nested/path/bad`, но не от `some/nested/path/bad/lib`. Ключевое слово `GLOB` действует только на следующий за ним аргумент.

По умолчанию ограничения действуют как на `PEERDIR`, так и на `TOOL` зависимости. Поведение ограничений можно переключать, используя ключевые слова `PEERDIRS` и `ALL`. Эти ключевые слова действуют на все ограничения, следующие за ними. Например
```
CHECK_DEPENDENT_DIRS(ALLOW_ONLY
    PEERDIRS
        contrib/libs
        build/platform
    ALL
        contrib/tools
)
```
позволяет проекту использовать только сторонние C++ библиотеки, сторонние сборочные инструменты и, возможно, их runtime библиотеки, лежащие рядом с самими инструментами.

При использовании `DENY` можно описывать исключения с помощью ключевого слова `EXCEPT`, которое действует на следующее непосредственно за ним ограничение, превращая его в исключение для чёрного списка:
```
CHECK_DEPENDENT_DIRS(DENY
    PEERDIRS
      some/bad/path
    EXCEPT some/bad/path/alot-of-good-libs
    EXCEPT GLOB some/bad/path/*/good
)
```

Для Java модулей ограничения, задаваемые данным макросом, проверяют classpath модуля после применения `DEPENDENCY_MANAGEMENT` и `EXCLUDE`.

### PROVIDES

```
PROVIDES(Feature...)
```

Данный макрос позволяет находить конфликты между реализациями одного и того же функционала разными библиотеками.

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

Хорошие примеры использования:
 * protobuf и protobuf_std помечены `PROVIDES(protobuf)`
 * llvm7, llvm8 и llvm11 помечены `PROVIDES(llvm)`


### REQUIRES

```
REQUIRES(Paths..)
```

Макрос контролирует, что среди всего набора зависимостей модуля (включая транзитивные), присутствуют заданные. `Paths...` указывает список директорий, в которых описана сборка модулей, наличие которых контролируется.

Макрос может быть нужен для дополнительного контроля в файлах для [`INCLUDE`](#include). Например, если файл описывает общую часть сборки некоторых плагинов к одной системе, то макрос `REQUIRES` может использоваться,
чтобы контролировать присутствие зависимости от общего модуля, фиксирующего интерфейс плагинов. Зависимость на такой модуль может быть у кода реализации (живущем, например, в `LIBRARY`), от которой в свою очередь зависит финальный модуль самого плагина (например, `DLL`).

{% cut  "To be documented" %}

{% note alert %}

Этот раздел пока не заполнен, но мы над этим работаем.

{% endnote %}

### Про .in-файлы и макросы подстановки переменных

```
CONFIGURE_FILE(File)
```

{% note alert %}

Этот раздел пока не заполнен, но мы над этим работаем.

{% endnote %}

{% endcut %}

## Управление сборкой мультимодуля

### EXCLUDE_TAGS
```EXCLUDE_TAGS(Tags)```

Вызов макроса `EXCLUDE_TAGS` позволяет отключить инстанциацию подмодулей МУЛЬТИМОДУЛЯ для указанных вариантов (тэгов, перечисленных в аргументах макроса). По умолчанию инстанциируются следующие варианты мультимодуля, кроме тех вариантов для которых свойство `.INCLUDE_TAG` имеет значение `no`.

## INCLUDE_TAGS
```INCLUDE_TAGS(Tags)```

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

### ONLY_TAGS
```ONLY_TAGS(Tags)```

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