# Описание сборки в ya.make

## Что такое описание сборки

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

Поэтому для системы сборки:

- Должны быть описаны правила, по которым исходные файлы превращаются в результаты.
- Так или иначе должна быть предоставлена информация о *зависимостях* разного рода, чтобы система сборки могла обеспечить минимальность.
- Описаны сборочные свойства для тех, кто от цели зависит, с тем, чтобы сборка цели и её зависимостей были согласованы.
- Могут быть указаны дополнительные описательные и/или контролируемые свойства. Примером описательного свойства может быть владелец кода, а контролируемого — лицензия библиотеки.

## Как описывается сборка для ya make { #how }

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

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

- В ya.make существуют переменные, однако (в отличие от cmake) они должны быть заданы до использования в конструкциях вроде [`IF()/ELSEIF()/ELSE()/ENDIF()`](./syntax.md#if). Использование неинициализированных переменных запрещено.

- Многие факты система сборки умеет выводить из расширений файлов (как исходных, так и промежуточных результатов). Подробнее об этом [ниже](#ext)

Описание сборки для ya make делается в файлах `ya.make`. Поскольку имя файла фиксировано, то в каждой директории может быть только один такой файл.
В качестве цели в команде сборки указывается имя директории с `ya.make`-файлом, и этот файл определяет, какие модули будут собраны в результате выполнения команды.

Все конструкции, которые можно написать в файлах `ya.make`, называются **макросами**. В тексте они выглядят как вызовы функций с параметрами, разделёнными пробелом.
Исторически для их именования используется [SCREAMING_SNAKE_CASE](https://ru.wikipedia.org/wiki/Snake_case).  Кроме макросов есть только комментарии.
В ya.make символом начала комментария является `#`. Комментарий длится до конца строки.

- [`Подробнее про синтаксис ya.make`](./syntax.md)
- [`Подробнее о том, как интерпретируется ya.make`](#interp)

Каждый `ya.make`-файл синтаксически содержит описание сборки не более чем одного **модуля**. Модуль — это библиотека/модуль/пакет, программа, тест или группа сборочных целей
(модули [`PACKAGE`](./package/modules.md#package) и [`UNION`](./package/modules.md#union)). Поскольку один `ya.make` содержит описание сборки не более чем одного модуля, для указания любых зависимостей между модулями,
как и для ссылок между `ya.make` достаточно указывать имя директории.

{% note info %}

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

Примеры мультимодулей — это [`PY23_LIBRARY`](./python/modules.md#py23_library) и [`PROTO_LIBRARY`](./proto/modules.md#proto_library)

{% endnote %}

Файлы `ya.make `могут содержать описание сборки *модуля* в данной директории и/или *связи* с другими файлами. Сборка модуля описывается макросами, которые надо размещать между
макросом типа модуля или мультимодуля и специальным макросом `END()`. Макросы, не описывающие сборку модуля, пишутся за его пределами. Там, в частности, надо писать
макросы [`RECURSE`](./common/macros.md#recurse) и подобные, которые выражают **связи** между сборками. Они позволяют при указании сборки для директории собрать несколько целей. Так можно указать,
что при сборке библиотеки надо собрать и тесты для неё (и запустить их, если запрошен запуск тестов).


### Самые первые ya.make

ya.make для списка проектов содержит только список директорий в одном из макросов [`RECURSE`](./common/macros.md#recurse)

```
RECURSE(
    commie
    core
    lib
    solver
    ut
)
```

При сборке такого `ya.make` будут собраны все цели, указанные в нём. Цели сами тоже могут содержать [`RECURSE`](./common/macros.md#recurse), что приведёт к сборке вложенных целей и т.д.

Система сборки поддерживает сборку разнообразных модулей на различных языках. Ниже приведены примеры для основных языков, но кроме них есть ещё модули для Protocol Buffers и Flatbuffers,
модули для агрегации результатов, модули специфичные для проектов и т.п.

Описание модуля обычно содержит:

- Указание на владельца модуля — макрос [`OWNER`](./common/macros.md#owner). Он может быть как в [глобальном скоупе](./syntax.md#global_scope), так и в [скоупе модуля](./syntax.md#mod_scope) (после макроса типа модуля).
- Макрос типа модуля: что именно будет строиться.
- Возможно, макрос для указания зависимостей модуля от других модулей — [`PEERDIR`](./common/macros.md#peerdir).
- Макрос с перечислением исходных файлов модуля [`SRCS`](./common/macros.md#srcs)/[`PY_SRCS`](./python/macros.md#py_srcs)/[`JAVA_SRCS`](./java/macros.md#java_srcs).
- Макрос `END`, завершающий [скоуп модуля](./syntax.md#mod_scope).
- Возможно, связанные сборочные цели (чаще всего тесты), указанные макросом [`RECURSE`](./common/macros.md#recurse) или [`RECURSE_FOR_TESTS`](./common/macros.md#recurse)

Для разных языков описание сборки *программы* будет выглядеть так:

{% list tabs %}

- C++

  __Структура директорий:__

  ```
  project/program
  ├── test
  │   ├── test.cpp
  │   └── ya.make
  ├── main.cpp
  └── ya.make
  ```

  __project/program/ya.make:__

  ```
  OWNER(username)  # Информационный (не сборочный) макрос, описывающий владельцев модуля

  PROGRAM()         # Макрос типа модуля: программа (на C++)

  PEERDIR(          # Макрос зависимостей модуля
      util/draft    # Это зависимость на модуль, описанный в util/draft/ya.make
  )

  SRCS(             # Макрос исходных файлов модуля
      main.cpp
  )

  END()             # Закрывающий макрос модуля

  RECURSE_FOR_TESTS(test) # Макрос связи - при тестировании вместе с программой
                          # строить и исполнять тест в поддиректории test
  ```

  * [Пошаговое руководство по описанию сборки на C++](../tutorials/cpp.md)

- Python

  __Структура директорий:__

  ```
  project/program
  ├── test
  │   ├── test.py
  │   └── ya.make
  ├── main.py
  └── ya.make
  ```

  __project/program/ya.make:__

  ```
  OWNER(username)  # Информационный (не сборочный) макрос, описывающий владельцев модуля

  PY3_PROGRAM()                # Макрос типа модуля: программа на 3м питоне

  PEERDIR(                     # Макрос зависимостей модуля
      contrib/python/requests  # Это зависимость на модуль, описанный в contrib/python/requests/ya.make
  )

  PY_SRCS(                     # Макрос исходных файлов модуля
     MAIN main.py              # Дополнительно указана, что в файле точка входа в программу
  )

  END()                        # Закрывающий макрос модуля

  RECURSE_FOR_TESTS(test) # Макрос связи - при тестировании вместе с программой
                          # строить и исполнять тест в поддиректории test
  ```

  * [Немного о сборке Python](#python)
  * [Пошаговое руководство по описанию сборки на Python](../tutorials/python.md)


- Java

  __Структура директорий:__

  ```
  project/program
  ├── src
  │   ├── main
  │   │   └── java
  │   │       └── ru
  │   │           └── yandex
  │   │               └── example
  │   │                   └── lib
  │   │                       └── Greet.java
  │   └── test
  │       ├── java
  │       │   └── ru
  │       │       └── yandex
  │       │           └── example
  │       │               └── lib
  │       │                   └── test
  │       │                       └── GreetTest.java
  │       └── ya.make
  └── ya.make
  ```

  __project/program/ya.make:__

  ```
  OWNER(username)  # Информационный (не сборочный) макрос, описывающий владельцев модуля

  JAVA_PROGRAM()               # Макрос типа модуля: программа на Java

  PEERDIR(                     # Макрос зависимостей модуля
      contrib/java/org/yaml/snakeyaml   # Это зависимость на общую библиотеку, описанную в contrib/python/requests/ya.make
  )

  DEPENDENCY_MANAGEMENT(       # Макрос выбора версионированной зависимости
      contrib/java/org/yaml/snakeyaml/1.27
  )

  JAVA_SRCS(                   # Макрос исходных файлов модуля
      SRCDIR src/main/java **/*.java    # Все .java файлы в указанной директории рекурсивно
  )

  END()                        # Закрывающий макрос модуля

  RECURSE_FOR_TESTS(test) # Макрос связи - при тестировании вместе с программой
                          # строить и исполнять тест в поддиректории src/test
  ```

  * [Немного о сборке Java](#java)
  * [Пошаговое руководство по описанию сборки на Java](../tutorials/java.md)

- Go

  __Структура директорий:__

  ```
  project/library
  ├── gotest
  │   └── ya.make
  ├── greeting.go
  ├── greeting_test.go
  └── ya.make
  ```

  __project/library/ya.make:__

  ```
  OWNER(username)  # Информационный (не сборочный) макрос, описывающий владельцев модуля

  GO_LIBRARY()     # Макрос типа модуля: библиотека (пакет) на Go

  SRCS(            # Макрос исходных файлов модуля
      greeting.go
  )

  GO_TEST_SRCS(    # Макрос исходных файлов внутренних тестов модуля
      greeting_test.go
  )

  END()            # Закрывающий макрос модуля

  RECURSE_FOR_TESTS(gotest) # Макрос связи - при тестировании вместе с программой
                            # строить и исполнять тест в поддиректории gotest
  ```

  * В описании сборки на Go обычно не указываются зависимости: система сборки находит их сама в исходном коде
  * Для Go поддерживаются внутренние (internal) и внешние (external) тесты. Первые попадают при запуске с тестированием в сам пакет,
    вторые — это отдельная тестовая программа с зависимостью на пакет
  * [Пошаговое руководство по описанию сборки на Go](../tutorials/java.md)

{% endlist %}

{% note tip %}

*Связь* [`RECURSE`](./common/macros.md#recurse) часто противоположна по направлению *зависимости* [`PEERDIR`](./common/macros.md#peerdir): рядом с библиотекой часто написана
*связь* на тесты, в то время как тесты *зависят* от библиотеки.

{% endnote %}

Описание библиотеки будет очень похоже, поменяется только макрос типа модуля (LIBRARY вместо PROGRAM).

Более сложное описание сборки может включать:

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




**Для справки**

- [Откуда берутся модули и макросы](./syntax.md#conf)
- [Полный автогенерируемый набор модулей в Аркадии](https://a.yandex-team.ru/arc_vcs/build/docs/readme.md)


### Правила при написании ya.make

{% note alert %}

Это важные правила, которое нужно соблюдать.

{% endnote %}

- Любое включение заголовка из другой библиотеки должно сопровождаться соответствующим [`PEERDIR`](./common/macros.md#peerdir) в `ya.make`. То есть `#include bla/blabla/blah.h` (или `import bla.blabla`) должно вести к `PEERDIR(bla/blabla)`.

- Если директория состоит из одних заголовков, нужно написать в ней `ya.make`, включающий строчки
```
LIBRARY()
    PEERDIR(
        # директории, заголовки из которых есть
        # в заголовках данной директории
    )
END()
  ```
  и ставить [`PEERDIR`](./common/macros.md#peerdir) на эту директорию в местах, где включаются заголовки данной директории.

- Библиотекам и программам нельзя включать заголовки или как-то ещё зависеть от программ без использования [`PEERDIR`](./common/macros.md#peerdir). Исключение сделано только для тестов во всех языках кроме C++). 
   Если часть кода программы нужно использовать повторно, выделите его в отдельную библиотеку.

- Архитектура интерфейс/реализация: если библиотека IR содержит две части (интерфейс I и реализацию R), и не хочется, чтобы части кода, включающие только I,
   зависели от R, нужно разбить библиотеку на две части. Конечно, такое разбиение делать разумно,
   если библиотека используется более чем в одном месте и есть несколько разных реализаций интерфейса.

- Ложные зависимости: если библиотека AB состоит из двух частей - A и B, и существует существенная часть другого кода, зависящая (включающая заголовки из) A, но не зависящая от B,
   то библиотеку AB нужно разбить на две части - A и B.

- Макрос [`SRCDIR`](./common/macros.md#srcdir) разрешается использовать **только** для сборки внешнего кода, структуру директорий которого менять нежелательно.
     Если одни и те же файлы надо включить в разные проекты, необходимо выделить их в отдельную библиотеку. Если файлы нужно собрать с разными [`CFLAGS`](cpp/macros.md#cflags), 
     следует перечислить [`CFLAGS`](cpp/macros.md#cflags) в отдельном файле и включить его в `ya.make`.
     Этим правилом следует руководствоваться не только в С++, а во всех разрешённых языках.

- Поддиректории с названиями `/common`, `/util` и им подобными заводить не следует - такие названия приводят к тому, что в этом месте со временем образуется мусорная свалка.
     Называйте свои поддиректории осмысленными именами.

- Все генерируемые в процессе сборки файлы должны находиться в каталоге сборки ([`BINDIR`, `ARCADIA_BUILD_ROOT`](common/vars.md#dir_vars)). Запрещено генерировать файлы в каталоге с исходниками 
    ([ARCADIA_ROOT SRCDIR](common/vars.md#dir_vars)).

- В `ya.make` проекта запрещается устанавливать флаги сборки (компиляции, дефайны), влияющие на сборку других проектов. Тем более запрещается менять глобальные флаги, влияющие на 
     конфигурацию сборки (вызывающие включение / исключение компонент и файлов в компоненты). Такие флаги разрешается устанавливать только глобально, для сборки в целом.

- Все проекты должны  быть достижимы от корня по цепочке `RECURSE() + PEERDIR()` от директории `/autocheck` для нужд автосборки и тестирования.

### Описание сборки модуля { #mod }

Описание сборки модуля содержит все необходимые для него исходные файлы, включая команды генерации исходных файлов, свойства и опции его сборки, а также необходимые
*зависимости* как ссылки на другие модули. Тестовые модули могут также содержать данные, зависимости и свойства, необходимые для запуска теста.

Начинается описания модуля **макросом типа модуля**, в котором опционально могут быть указаны имя его результата (может быть важно для программ и динамических библиотек) и иногда
другая необходимая для описания самого модуля информация. Заканчивается описание модуля макросом `END`.


**Модули** условно делятся на *финальные* и *промежуточные*.

- Промежуточные модули — это модули, родственные [`LIBRARY`](./cpp/modules.md#library) и [`UNION`](./package/modules.md#union). На них можно ставить зависимость ([`PEERDIR`](./common/macros.md#peerdir)) как из аналогичных модулей,
  так и из соответствующих финальных модулей (для [`LIBRARY`](./cpp/modules.md#library) это [`PROGRAM`](./cpp/modules.md#program), `DLL`, `UNITTEST` и т.п.). При сборке промежуточных модулей как целей их зависимости
  **не строятся**. Не имеет смысл указывать промежуточные модули в макросах [`BUNDLE`](./common/macros.md#bundle) и [`DEPENDS`](./tests/common.md#depends) поскольку они не замкнуты по зависимостям.
  Их также нельзя использовать как инструменты в макросах типа [`RUN_PROGRAM`](./common/macros.md#run_program). Единственное исключение - [`DEPENDS`](./tests/common.md#depends) на [`UNION`](./package/modules.md#union), см. ниже.

- Финальные модули — это программы, тесты и [`PACKAGE`](./package/modules.md#package). При сборке они замыкают транзитивно все свои зависимости. На них нельзя ставить зависимость [`PEERDIR`](./common/macros.md#peerdir) в других модулях кроме [`PACKAGE`](./package/modules.md#package).
  Их можно использовать как артефакты в макросах [`DEPENDS`](./tests/common.md#depends) и [`BUNDLE`](./common/macros.md#bundle). Исполняемые финальные модули могут выступать инструментами в макросе [`RUN_PROGRAM`](./common/macros.md#run_program) и аналогичных

- Если сослаться на [`UNION`](./package/modules.md#union) в макросе [`DEPENDS`](./tests/common.md#depends), то все зависимости [`UNION`](./package/modules.md#union) транзитивно построятся и будут доступны в build-директории по путям, соответствующим своему размещению в репозитории.

{% note tip %}

Для упрощения написания тестов для программ и реализации традиционных возможностей для отдельных языков, программы могут быть *мультимодулями*, состоящими из *промежуточного* модуля-библиотеки и
*финального* модуля программы. Такая организация позволяет иметь [`PEERDIR`](./common/macros.md#peerdir)-зависимость из теста на программу, чтобы протестировать функции в программах непосредственными вызовами,
а не запуском программы (как при использовании [`DEPENDS`](./tests/common.md#depends)). Такая реализация сейчас у [`JAVA_PROGRAM`](./java/modules.md#java_program) и [`PY3_PROGRAM`](./python/modules.md#py3_program).

{% endnote %}

Система сборки ya make предоставляет развитую систему модулей для различных языков и применений. Они описаны в соответствующих разделах:

- [Более подробно о синтаксисте ya.make](./syntax.md)
- [Модули для C++](cpp/modules.md)
- [Модули для Python](python/modules.md)
- [Модули для Java](java/modules.md)
- [Модули для Go](go/modules.md)
- [Модули для Protocol Buffers](proto/modules.md)
- [Модули для Flat Buffers](flatbuf/modules.md)
- [Модули для агрегации](package/modules.md)
- Модули для тестов [на С++](tests/cpp.md), [на Python](tests/cpp.md), [для Java](tests/cpp.md), [для Go](tests/cpp.md).
- [Полный автогенерируемый набор модулей в Аркадии](https://a.yandex-team.ru/arc_vcs/build/docs/readme.md)


### Макросы в модуле { #module_macros }

Условно макросы в модуле можно разделить на три группы:

1. Управляющие макросы.

   Это [условные макросы](./syntax.md#if), макрос [`INCLUDE`](./syntax.md#include), макросы [`EXCLUDE_TAGS`/`ONLY_TAGS`/`ONLY_TAGS`](./syntax.md#exclude_tag)
   Они влияют на то, как интерпретируется `ya.make`.
   
   Условными макросами `IF()/ELSEIF()/ELSE()/ENDIF()` можно настроить разную сборку на основе значений переменных как заданных локально (макросом [`SET()`](./syntax.md#var_macros)), 
   так и глобальных, определяющих конфигурацию сборки для данного модуля (например, `OS_WINDOWS` при сборке под Windows или `PYTHON3` в соответствующем варианте мультимодуля).

2. Макросы, задающие свойства.

   Такие макросы могут влиять на встроенные свойства непосредственно, например, [`PEERDIR`](./common/macros.md#peerdir) задаёт зависимости для модуля,
   [`SRCDIR`](./common/macros.md#srcdir) или [`ADDINCL`](./common/macros.md#addincl) задают пути поиска для [разрешения имён](../general/how_it_works.md#resolving).
   Макрос [`PY_NAMESPACE`](python/macros.md#py_namespace) задаёт пространства имён для импорта описанных в модуле файлов и т.п.

   Также они могут формировать значения [переменных](./syntax.md#vars), которые будут использованы в командах других макросов или модуля. Например, `СFLAGS` задаёт
   флаги для команд компиляции C/C++ файлов в модуле. Макросом [`SET(VAR VALUE)`](./syntax.md#var_macros) можно задать значение переменной. Если переменная присутствует в
   глобальной конфигурации, её значение изменится для данного модуля, если нет - переменная появится и будет доступна в контексте данного модуля в макросах, идущих ниже по тексту.

   Макрос [`OWNER`](./common/macros.md#owner) можно писать за пределами модуля и даже в файлах `ya.make` не описывающих никаких модулей. Этот макрос позволяет внешним системам 
   (Arcanum и CI) находить ответственных за код. Поскольку эта информация не имеет отношения к системе сборки, то в скором времени вместо этого макроса владельцев кода надо писать 
   в конфигурации проектов в репозитории (файле `a.yaml`).

3. Макросы, описывающие команды.

   Такие макросы описывают команды, которые должны быть исполнены в процессе сборки. Макрос формирует команду, которая встраивается в сборочный граф своего модуля за счёт
   [связывания](../general/how_it_works.md#resolving) своих результатов с входами других команд (потребителей) и, возможно, своих входов с результатами других команд (источников).

   Так, например, макрос `COMPILE_LUA` создаёт команду, компилирующую код на Lua в представление, пригодное для LuaJit. Его результат будет связан с командой модуля
   (например, упаковкой в библиотеку), а библиотеку по зависимостям влинкует в себя программа.


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

- Ссылки на файлы могут относиться как к файлам в репозитории, так и к генерированным файлам. Для первых проверяется наличие в файловой системе, для вторых - доступность
  в соответствующем модуле по графу сборки. Для точного указания о каком файле идёт речь можно использовать пути в формате `${ARCADIA_ROOT}/project/path/file.name` для
  файлов в репозитории и `${ARCADIA_BUILD_ROOT}/project/path/file.name` для генерированных файлов. [Подробнее о разрешении имён](../general/how_it_works.md#resolving).

- Ссылки на директории практически всегда фиксируют только имя директории, но не её содержимое. Единственное исключение - макрос `DATA()`, задающий данные для тестов.
  При указании в нём директории тест будет зависеть от всех файлов в указанной директории.

- Ссылки на модули всегда описываются указанием директории, но в такой директории должен быть `ya.make` с описанием сборки модуля или мультимодуля.
  Ссылки на директории с `ya.make` без модулей запрещены. Это относится к любым макросам, задающим межмодульные зависимости: [`PEERDIR`](./common/macros.md#peerdir), [`DEPENDS`](./tests/common.md#depends), [`BUNDLE`](./common/macros.md#bundle), а также к ссылкам на инструменты в
  макросах запуска инструментов (`RUN_PROGRAM`) и подобным. Это не относится к макросам, задающим *связи* — [`RECURSE`](./common/macros.md#recurse) и подобным. Такие макросы могут ссылаться на `ya.make`
  только с [`RECURSE`](./common/macros.md#recurse) для рекурсивного задания связей.


Система сборки ya make предоставляет огромное количество макросов для различных языков и применений. Они описаны в соответствующих разделах:

- [Более подробно о синтаксисте ya.make](./syntax.md)
- [Макросы для C++](cpp/macros.md)
- [Макросы для Python](python/macros.md)
- [Макросы для Java](java/macros.md)
- [Макросы для Go](go/macros.md)
- [Макросы для Protocol Buffers](proto/macros.md)
- [Макросы для Flat Buffers](flatbuf/macros.md)
- [Макросы для агрегации](package/macros.md)
- [Макросы для тестов](tests/common.md)
- [Полный автогенерируемый набор макросов в Аркадии](https://a.yandex-team.ru/arc_vcs/build/docs/readme.md)

### Мультимодули { #multi }

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

- Если мультимодуль используют по зависимости [`PEERDIR`](./common/macros.md#peerdir), то вариант выбирается по *входящей зависимости* - модуль,
  который зависит от мультимодуля определяет какой вариант ему нужен. Каждый отдельный модуль может зависеть только от одного варианта, но в рамках
  одной сборки может быть собрано несколько вариантов для разных зависимостей.
- Если мультимодуль является непосредственной целью собираются все его варианты (более точно см. документацию по соответствующему мультимодулю).
- Если мультимодуль указан в [`DEPENDS`](./tests/common.md#depends) или [`BUNDLE`](./common/macros.md#bundle) будет выбран единственный вариант со свойством `FINAL_TARGET`. Если такой вариант не единственный
  или его нет вовсе - будет выдана ошибка конфигурации.

**Пример**

__Структура директорий:__

```
project/interface
├── cpp
│   ├── iface.cpp
│   └── ya.make
├── go
│   ├── iface.go
│   └── ya.make
├── java
│   ├── iface.java
│   └── ya.make
├── proto
│   ├── iface.proto
│   └── ya.make
├── python
│   ├── iface.py
│   └── ya.make
└── ya.make
```

__project/interface/ya.make:__

```
OWNER(username)  # Информационный (не сборочный) макрос, описывающий владельцев модуля

RECURSE(
    proto

    cpp
    go
    java
    python
)
```

__project/interface/proto/ya.make:__

```
OWNER(username)  # Информационный (не сборочный) макрос, описывающий владельцев модуля

PROTO_LIBRARY()  # Макрос типа модуля: мультимодуль библиотеки protobuf (для разных языков)

SRCS(            # Макрос исходных файлов модуля. Интерпретируется по-разному в разных вариантах
    iface.proto
)

END()            # Закрывающий макрос модуля
```

__Зависимости на эту библиотеку в разных языках:__

{% list tabs %}

- C++

  __project/interface/cpp/ya.make:__

  ```
  OWNER(username)  # Информационный (не сборочный) макрос, описывающий владельцев модуля

  LIBRARY()         # Макрос типа модуля: библиотека (на C++)

  PEERDIR(          # Макрос зависимостей модуля
      project/interface/proto    # Это зависимость на модуль, описанный в project/interface/proto/ya.make
  )

  SRCS(             # Макрос исходных файлов модуля
      iface.cpp
  )

  END()
  ```

  * Такая библиотека получит зависимость на `project/interface/proto/libproto.a`
  * Библиотеке будет доступен для `#include` файл `project/interface/proto/iface.pb.h`
  * И библиотека и файл будут результатами сборки варианта [`PROTO_LIBRARY`](./proto/modules.md#proto_library) для C++ по зависимости из этой библиотеки

- Python

  __project/interface/python/ya.make:__

  ```
  OWNER(username)  # Информационный (не сборочный) макрос, описывающий владельцев модуля

  PY3_LIBRARY()    # Макрос типа модуля: библиотека (на Python 3)

  PEERDIR(         # Макрос зависимостей модуля
      project/interface/proto    # Это зависимость на модуль, описанный в project/interface/proto/ya.make
  )

  PY_SRCS(         # Макрос исходных файлов модуля
      iface.py
  )

  END()
  ```

  * Библиотеке будет доступен для `import` модуль `project.interface.proto.iface_pb2`
  * При сборке по зависимости из этой библиотеки будет построит Python 3 вариант [`PROTO_LIBRARY`](./proto/modules.md#proto_library).
    Это будет сгенерированный и прекомпилированный Python-код, доступный для импорта. Немножко о сборке Python [ниже](#python).

- Java

  __project/interface/java/ya.make:__

  ```
  OWNER(username)  # Информационный (не сборочный) макрос, описывающий владельцев модуля

  JAVA_LIBRARY()   # Макрос типа модуля: библиотека (на Python 3)

  PEERDIR(         # Макрос зависимостей модуля
      project/interface/proto    # Это зависимость на модуль, описанный в project/interface/proto/ya.make
  )

  JAVA_SRCS(       # Макрос исходных файлов модуля
      iface.java
  )

  END()
  ```

  * Библиотеке будут доступен для `import` класс `project.interface.proto.iface.Message` (в предположении, что в `iface.proto` описано сообщение `Message`.
  * При сборке по зависимости из этой библиотеки будет собран Java-вариант [`PROTO_LIBRARY`](./proto/modules.md#proto_library). В результате будет сгенерирован набор .java-файлов и все они
    будут откомпилированы в `project/interface/proto/iface.jar`, который и станет зависимостью для этой библиотеки.


- Go

  __project/interface/java/ya.make:__

  ```
  OWNER(username)  # Информационный (не сборочный) макрос, описывающий владельцев модуля

  GO_LIBRARY()   # Макрос типа модуля: библиотека (на Python 3)

  SRCS(          # Макрос исходных файлов модуля
      iface.go
  )

  END()
  ```

  * В коде на Go [`PEERDIR`](./common/macros.md#peerdir) указывать не надо, он будет получен из исходного файла.
  * import будет выглядеть как `"a.yandex-team.ru/project/interface/proto/iface"`. Для правильной его работы в `iface.proto` должно быть указано
    `option go_package = "a.yandex-team.ru/project/interface/proto/iface";`
  * При сборке по зависимости из этой библиотеки будет собран Go-вариант [`PROTO_LIBRARY`](./proto/modules.md#proto_library). Для этого будет сгенерирован и скомпилирован go-код.
    Получившаяся библиотеке (пакет) станут зависимостью этой библиотеки.

{% endlist %}

Как несложно заметить [`PEERDIR`](./common/macros.md#peerdir) выглядит одинаково для всех языков, однако в зависимости от потребителя будет предоставлен
разный *вариант* [`PROTO_LIBRARY`](./proto/modules.md#proto_library): для go — это будет собранный пакет, для C++ библиотека, для java - .jar, для Python - набор
сгенерированных и прекомпилированных Python-модулей. Такое поведение определяется двумя встроенными *переменными*, поддержанными в коде системы сборки:

- `MODULE_TAG` позволяет различать варианты и говорит о назначении модуля. У разных вариантов мультимодуля он разный.
- `PEERDIR_TAGS` говорит какой вариант надо выбирать из мультимодуля по [`PEERDIR`](./common/macros.md#peerdir). На данный момент из каждого мультимодуля можно выбирать только
  один вариант, список нужен для выбора вариантов в разных видах мультимодулей. Кроме выбора вариантов этот же механизм используется для контроля
  совместимости обычных модулей по зависимостям.

{% note tip %}

Если необходима разная сборка в зависимости от выбранного варианта, то надо использовать макрос [`IF()/ELSEIF()/ELSE()/ENDIF()`](./syntax.md#if) с условием на переменную
`MODULE_TAG` или другие переменные специфичные для интересующего варианта (например, `PYTHON2`/`PYTHON3`).

{% endnote %}


### Как интерпретируется ya.make { #interp }

К моменту начала зачитывания и интерпретации ya.make уже:

- Зачитана конфигурация с описанием всех макросов, глобальных правил и переменных
- Переменные, относящиеся к платформе уже означены

ya.make файл интерпретируется следующим образом:

**Чтение ya.make**

- Макросы читаются в том порядке, в котором написаны, но интерпретируются сразу далеко не все.
- В интерпретируемые макросы известные переменные подставляются, условные выражения отрабатывают по сложившимся к моменту их вызова значениям.
- Изменения переменных, для которых есть правила в описании модуля, приводят к вызову этих правил и соответствующему изменению значений внутри правил.
- Макросы, меняющие переменные, срабатывают сразу. Если в макросах есть вызовы других макросов, они тоже срабатывают в этом смысле. Если в макросах есть использования переменных -- они подставятся.
- Все макросы кроме изменений переменных помещаются в приоритизированную очередь, где [`PEERDIR`](./common/macros.md#peerdir) имеет самый высокий приоритет, а [`SRCS`](./common/macros.md#srcs) один из самых низких.
  Также очень низкий приоритет имеют макросы, реализованные в [плагинах](#plugins).
- Для мультимодулей это делается для каждого варианта отдельно.

**Построение графа зависимостей**

- Отдельным проходом очередь разбирается
- [`PEERDIR`](./common/macros.md#peerdir) вызывает переход к обработке `ya.make` по зависимостям. Это необходимо для корректного распространения [глобальных свойств](#global).
- Выясняются входы и выходы команд, происходит их [связывание](../general/how_it_works.md#resolving) в соответствии с [`SRCDIR`](./common/macros.md#srcdir) этого модуля.
- Команды макросов помещаются в [граф зависимостей](../general/how_it_works.md) в соответствии с зависимостями. [Глобальные переменные](#global) остаются не подставленными. Отдельно формируются [глобальные команды](#global), чтобы их мог получить модуль, который их агрегирует.
- Все входы всех команд транзитивно парсятся на предмет зависимостей [в соответствии с их расширениями](./extensions.md#parsing), напарсенные имена [превращаются в зависимости на файлы](../general/how_it_works.md#resolving) в соответствии с [`ADDINCL`](./common/macros.md#addincl) применимыми в этом модуле.
- После всех команд из макросов в модуле добавляется команда модуля, она получает входы от других команд как обычными связыванием, так и специальным, [модульным](../general/how_it_works.md#mod_resolve).
- Когда граф полностью построен, на нём отрабатывает код для макросов, анализирующих граф целиком ([`DEPENDENCY_MANAGEMENT`](./java/macros.md#dependency_namagement), [`CHECK_DEPENDENT_DIRS`](./common/macros.md#check_dependent_dirs), [`RESTRICT_LICENSES`](./common/macros.md#restrict_licenses) и т.п.).
- После этого формируется [граф команд](../general/how_it_works.md). В него записываются тексты команд с учётом подстановки всех переменных, включая [глобальные](#global)
- Отдельно формируется информация, которую будет использовать [обогащение графа](../general/how_it_works.md), например, для формирования команд запуска тестов. Туда попадает часть переменных и свойств модуля.


#### Глобальные свойства, переменные и команды { #global }

В системе сборки ya make есть понятие *локальных* и *глобальных* свойств, переменных и команд.

- Локальные свойства, переменные и команды относятся к тому модулю, в котором описаны. Локальные свойства — это свойства только этого модуля; локальные переменные — переменные доступные только в нём; локальные команды — это команды,
  результаты которых являются (возможно, транзитивно) входами команды модуля.

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

**Глобальными свойствами** могут являться:

- [`PEERDIR`](./common/macros.md#peerdir), в C++, Python и Go: если программа `P` зависит от библиотеки `A`, а библиотека `A` зависит от библиотеки `B`, то программа `P` тоже зависит от библиотеки `P`. В частности эта
  библиотека должна быть передана в команду линковки программы.

- [`ADDINCL`](./common/macros.md#addincl) может быть глобальным, если указан параметр `GLOBAL`. В это случае он станет свойством как самой библиотеки, так и всех модулей (библиотек и программ), зависящих от этой библиотеки.
  Это нужно в том случае, если в заголовочных файлах библиотеки в `#include` указан относительный путь. Поскольку такой заголовочный файл может быть включён в файлах из других модулей, они должны
  знать где файл искать.

Есть и другие глобальные свойства.

**Глобальные переменные**

Некоторые переменные могут распространять свои значения по зависимостям. Примерами могут служить `LDFLAGS` — если библиотека требует указания каких-то флагов при линковке, она макросом
`LDFLAGS` может добавить их в список одноимённой переменной `LDFLAGS`, которая будет использована в команде линковки программы. Аналогично работает `CFLAGS(GLOBAL ...)` но в этом случае
переменная `CFLAGS` будет использована во всех командах компиляции во всех модулях, зависящих от библиотеки.

Глобальные переменные конфигурируются для каждого типа модуля и новые нельзя создать синтаксисом в `ya.make`. Если вам кажется, что для решения вашей сборочной задачи необходима глобальная переменная,
[обращайтесь в devtools](https://st.yandex-team.ru/createTicket?queue=DEVTOOLSSUPPORT).

{% note alert %}

Распространять свойства и переменные в сторону зависимостей нельзя. Иначе разные цели могли бы для одного и того же
модуля запросить разные свойства, и его конфигурация бы размножилась. Для изменения свойств зависимостей надо использовать [установку переменной на всю сборку `ya make -DVAR`](../usage/ya_make/index.md#D).

У этого правила есть три исключения:
- Транзитивные межмодульные инварианты проверяются на всём подграфе и могут запрещать транзитивную зависимость.
- dependency management для java формирует граф зависимостей глобально для узла учитывая, но не полностью следуя зависимостям своих зависимостей
- Связывание по PEERDIR варианта [мультимодуля](#multi) — это выбор конфигурации зависимости, но из ограниченного набора вариантов.

{% endnote %}


**Глобальные команды**

Иногда возникает необходимость сделать команду зависимостью не самого модуля (библиотеки), а финального артефакта (программы). Например, при линковке С++ кода объектные файлы из библиотек
могут быть полностью удалены, если у них нет использований. Если код регистрирует себя при статической инициализации он может быть исключён из программы. Чтобы этого не произошло, такой код надо
подать на линковку непосредственно программе. Этой цели служат *глобальные команды*. Чаще всего они возникают из вызова макроса [`SRCS`](./common/macros.md#srcs) c параметром `GLOBAL`, но есть и другие макросы, порождающие
глобальные команды.

{% note tip %}

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

{% endnote %}

#### Автоматическая обработка по расширениям { #ext }

Макросы, задающие исходный код для модуля, такие как [`SRC`](./common/macros.md#srcs)/[`SRCS`](./common/macros.md#srcs)/[`PY_SRCS`](./python/macros.md#py_srcs)/[`JAVA_SRCS`](./java/macros.md#java_srcs) полиморфны. Они задают обработку файла в соответствии с расширением.
Поддерживается достаточно большой [список расширений](./extensions.md#srcs). Если файл, с поддерживаемым расширением является результатом работы команды в каком-то макросе,
например, перечислен в параметре `OUT` (есть у многих генерирующих макросов), такой файл будет обработан **автоматически** так, как будто он указан в [`SRCS`](./common/macros.md#srcs).
Дополнительно указывать его там не нужно. Если такая обработка является нежелательной или обработка по расширению не поддерживается - используйте параметр `OUT_NOAUTO` вместо `OUT`.
Конкретные команды обработки по расширениям могут зависеть от модуля, для которого обрабатываются файлы. Так в контексте [`LIBRARY`](./cpp/modules.md#library) (библиотеки для C++), файл `.proto` будет обработан по цепочке `.proto -> .pb.cc/pb.h -> .pb.cc.o`, после чего все `.o` файлы
соберутся в библиотеку. В контексте `GO_LIBRARY` цепочка получится уже `.proto -> .pb.go -> .pb.go.o`. Таким образом, команда для `.proto` будет разной в зависимости от типа модуля, а
команды для компиляции `.cc` и `.go` будут предоставлены неявно по факту появления промежуточного результата с таким расширением.

Описание сборки в `ya make` очень высокоуровневое, она не требует описания зависимостей файлов от других файлов (например, `#include` в C++), если исходный файл доступен на файловой системе.
Система сборки сама парсит такие файлы, основываясь на расширениях и находит зависимости. Более того, система сборки может выводить парсингом межмодульные зависимости (в go), а также [наведённые](#induced)
зависимости генерируемых файлов от других генерируемых. Так из `import` в .proto система сборки делает вывод не только о зависимости между .proto-файлами, но и о
зависимости .pb.cc файла от импортируемого .pb.h. Какие файлы и как парсятся можно посмотреть в [списке расширений поддержанных парсингом и командами](./extensions.md#parsing)


## Зависимости в сборке { #deps }

### Зависимости между модулями { #mod_deps }

Основной зависимостью между модулями является [`PEERDIR`](./common/macros.md#peerdir) — она примерно эквивалентна import-зависимости между пакетами/модулями в модульных языках.
[`PEERDIR`](./common/macros.md#peerdir)-зависимости между модулями могут быть *логическими* или *сборочными* в зависимости от языка и типа модуля.

- В C++ зависимость одной библиотеки ([`LIBRARY`](./cpp/modules.md#library)) от другой **логическая**: для сборки зависящей библиотеки, зависимую библиотеку собирать не нужно. Зависимость между библиотеками в данном случае означает что при сборке программы вместе
с зависящей библиотекой надо линковать и зависимую. Кроме этого, по такой зависимости могут распространяться свойства. Например, зависимая библиотека может добавить путь поиска
`#include` всем, кто от неё зависит или отправить во все программы по зависимостям флаги линковки и т.п.

- Зависимость программы ([`PROGRAM`](./cpp/modules.md#program)) от библиотек ([`LIBRARY`](./cpp/modules.md#library)) является **сборочной** — все библиотеки по зависимостям будут собраны и предоставлены программе для линковки.

Сборка в Python устроена аналогично С++ с логическими связями между библиотеками. В отличие от C++, Java и Go — модульные языки, сборка пакета/модуля требует наличия всех предсобранных зависимостей,
поэтому зависимости `JAVA_LIBRARY` и `GO_LIBRARY` от себе подобных **сборочные** и пересборка любой такой библиотеки вызывает пересборку всех зависимостей.

Кроме [`PEERDIR`](./common/macros.md#peerdir) между модулями могут быть следующие зависимости:

- **инструментальная зависимость** — зависимость команды модуля от другого (*финального*) модуля, как инструмента. Описывается такая зависимость через упоминание в макросе
  [`RUN_PROGRAM`](./common/macros.md#run_program) или в параметре `TOOL` многих генерирующих макросов.

  {% note warning %}

  Такая зависимость является кроссплатформенной. В то время как модуль, в котором написан [`RUN_PROGRAM`](./common/macros.md#run_program) будет собираться под [*целевую платформу*](../general/base_concepts.md#configs),
  модуль, от которого есть *инструментальная зависимость* будет собираться под [*сборочную платформу*](../general/base_concepts.md#configs), поскольку он будет исполняться в процессе сборки.

  {% endnote %}

- **[`BUNDLE`](./common/macros.md#bundle) — зависимость от модуля как от файла**. Результат сборки модуля становится входом команды в другом модуле. Например, его можно сложить внутрь модуля макросом [`RESOURCE`](./common/macros.md#resource)

- **[`DEPENDS`](./tests/common.md#depends) — нужно для исполнения**. Результат сборки модуля (обычно программы) нужен в запуске теста. Такая программа будет собрана независимо и доступна тесту по пути, предоставляемому тестовым фреймворком.

#### Какие модульные зависимости не поддержаны

Есть несколько разумных зависимостей, которые, однако, у нас сейчас не поддержаны.

- **Дополнительный результат. ([`DEPENDS`](./tests/common.md#depends) для программ)** Зависимость такого рода, например, могла бы возникать у программы на динамическую библиотеку и приводить к тому,
  что результатом сборки программы была бы ещё и динамическая библиотека. На данный момент такая возможность не поддержана, мы рекомендуем использовать [`UNION`](./package/modules.md#union) + 
  [`BUNDLE`](./common/macros.md#bundle) или [`PACKAGE`](./package/modules.md#package) + [`PEERDIR`](./common/macros.md#peerdir) для сборки программы с динамической библиотекой. Однако мы думаем о том, чтобы добавить такую возможность.

- **Наведённый [`PEERDIR`](./common/macros.md#peerdir)**. Таким образом можно было бы описать, что для работы порождённого генератором кода нужна библиотека. Это не поддержано в силу рядя причин:
  - Такая возможность сделала бы описание PEERDIR-ов нелокальным и усложнила бы конфигурирование.
  - Технически генератор конфигурируется под сборочную платформу, а его рантайм-библиотека нужна в целевой, поэтому такая зависимость требовала бы распространения свойств между разными платформами

### Зависимости модулей и команд от файлов { #mod_to_file }

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

- Такие макросы как [`SRCS`](./common/macros.md#srcs), `FILES`, [`RESOURCE`](./common/macros.md#resource) и многие другие порождают команды, которые работают над файлами, перечисленными в макросе, и порождают файлы результата. Последние могут быть [обработаны по расширению](#ext) и так несколько раз,
  пока в итоге не станут входами модуля. При пересборке изменение такого файла приведёт к перезапуску команды из макроса дале по цепочке вплоть до модульной команды и далее по модульным зависимостям.
  Про то как выходы одних команд становятся входами других, можно почитать в разделе про [связывание имён входов с файлами и результатами](../general/how_it_works.md#resolving).

- Модули для языков, не поддерживающих раздельную компиляцию (например, Java и go) собирают все исходные файлы прямо в модульной команде. Их макросы [`SRCS`](./common/macros.md#srcs) и [`JAVA_SRCS`](./java/macros.md#java_srcs) формируют только список
  исходных файлов, который подаётся на вход модульной команде.

Как описано [выше](#ext), не все зависимости и даже команды нужно указывать явно:

- Модульная команда [собирает все результаты сборки и даже исходные файлы](../general/how_it_works.md#mod_resolve), перечисленные в `SRCS` по расширениям [зафиксированным для модуля](./extensions.md#auto)
- Файлы, возникающие как результаты команд (кроме тех, где это явно отменено и тех, которые попали под правило выше) будут обработаны так, словно написаны в `SRCS`. Сам макрос `SRCS` 
  [обрабатывает файлы в соответствии с расширением](./extensions.md#srcs). Причём, если обработка в `SRCS` порождает файл с расширением, поддержанный в `SRCS`, он будет снова обработан.


### Зависимости файлов от файлов { #file_to_file }

Единицей сборки является команда, поэтому зависимость команд от файлов, описанная выше, достаточно естественна и понятна. Однако, в системе сборки ya make одни файлы могут зависеть от других.
В реальности такие зависимости превращаются в зависимость команды обработки файла от него и всех его зависимостей, но, если делать эти зависимости явными получится очень много дуг. Например,
в C++ каждая команда компиляции зависит вообще от всех заголовочных файлов, транзитивно включённых в исходный. А ведь заголовочные файлы могут включать другие заголовочные файлы и т.д. создавая
огромные повторяющиеся наборы зависимостей. В системе сборки ya make такие `#include` (а также `import`-ы в `.proto` и многие другие подобные связи) представлены непосредственно.

Это позволяет заметно сэкономить память и время обработки графа: в большинстве внутренних обходов каждая дуга посещается не более одного раза.

Зависимости файлов от файлов по своему происхождению бывают двух видов:

- Непосредственные зависимости, выясняются [парсингом](#parsing) на основе [расширений](./extensions.md#parsing). Это зависимости непосредственно написанные исходном в файле.
- Наведённые зависимости — это зависимости генерированных файлов. Они могут быть [предсказаны парсером или указаны явно](#induced)

#### Парсинг { #parsing }

Сборочные инструменты, например компилятор, обрабатывают все инклуды рекурсивно во время компиляции. В частности, это означает, что каждый хедер файл будет прочитан за время сборки столько раз,
сколько .cpp файлов его используют (транзитивно). Повторять такую логику при анализе зависимостей крайне дорого, поэтому система сборки ya make читает каждый файл ровно один раз. В неё встроено
достаточно большое количество быстрых парсеров для файлов разных типов, [определяемых расширением](./extensions.md#parsing). Эти парсеры не разбирают язык полностью, а лишь ищут межфайловые
зависимости (инклуды, импорты и т.п.). В файлах могут быть условные инклуды и даже в одной сборке они могут отработать по-разному в контексте разных компиляций.
Поскольку система сборки парсит файл ровно один раз она игнорирует условия и использует все include как источники зависимостей. Это избыточно, но лишние зависимости лучше, чем неучтённые.

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


#### Наведённые зависимости { #induced }

Не все зависимости система сборки может получить парсингом: ya make анализирует зависимости до исполнения сборки, поэтому зависимости генерируемых файлов она получить не может.
Такие зависимости называются **наведёнными** и могут быть получены тремя способами:

- Зависимости, приносимые инструментами. Если какой-то генератор порождает фиксированные инклуды, то их можно указать в модуле самого генератора макросом [`INDUCED_DEPS`](./common/macros.md#induced_deps). Поскольку такие инклуды будут
  использованы в генерированном коде, который даже относится к другой платформе (инструмент относится к сборочной, а хедеры будут использованы в контексте сборке для целевой), то мы не делаем для них
  полноценный [резолвинг](../general/how_it_works.md#resolving). В этом макросе должны быть указаны [*полные имена*](../general/how_it_works.md#resolving). Поскольку один генератор может генерировать код на разных языках в макросе можно указать разные наведённые инклуды для
  разных языков.

- Зависимости, приносимые вызовом инструментов. Конкретный вызов инструмента в макросе [`RUN_PROGRAM`](./common/macros.md#run_program) может определять набор зависимостей, которые будут у результата. Такие зависимости должны быть
  указаны в параметре `OUTPUT_INCLUDES` самого макроса. Поскольку в данном случае результат макроса принадлежит тому же модулю, что и вызов макроса, то имена, указанные в параметре будут предметом
  резолвинга, как и обычные инклуды. Для вызова одного и того же инструмента могут применяться оба вида зависимостей.

- Наведённые зависимости, получаемые [парсингом](#parsing). Код обработки импортов в отдельных случаях может делать выводы о том, какие зависимости получатся у генерированного кода. Так из импорта
  файла `imp.proto` в файле `use.proto` система сборки делает вывод о наличии include `imp.pb.h` в `use.pb.h` n `use.pb.cc`.

## Исполнение сборки по описанию

Более подробно о том, как работает сборка, написано в [соответствующем разделе](../general/how_it_works.md#build)

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

1. Зачитывается глобальная конфигурация, из неё ya make узнаёт о доступных макросах и модулях.
2. Инициализируются переменные связанные с платформой и конфигурацией.
3. Зачитывается и интерпретируется ya.make файл в директории сборочной цели.
4. Все связанные ya.make добавляются в очередь.
5. Если ya.make содержит [описание сборки модуля](#mod), то выясняются свойства его сборки и зависимости.
6. Зачитываются и интерпретируются ya.make зависимостей, [связи](./common/macros.md#recurse) от них игнорируются.
7. Процесс 5–6 идёт рекурсивно пока не достигнет модуля без зависимостей, пройденные модули запоминаются в стек.
8. Если есть зависимые модули - их [глобальные свойства](#global) применяются. Анализируются [зависимости модуля от файлов](#mod_to_file), формируются команды модуля.
9. [Глобальные свойства](#global) модуля фиксируются для распространения.
10. Повторяются шаги 5,6 пока не пройдены все зависимости, потом шаги 8,9.
11. При достижении дна стека берётся следующий ya.make из очереди. который ещё не обрабатывали (в обходе по зависимостям). Начальный модуль и модули из очереди называются *стартовыми*.
12. Когда очередь исчерпана - построен *Граф зависимостей*.
13. На построенном графе **анализируются глобальные свойства**, делается [dependency management](./java/dependencies.md) для Java, проверяются транзитивные инварианты (например, [`CHECK_DEPENDENT_DIRS`](./common/macros.md#check_dependent_dirs)).
14. *Граф зависимостей* трансформируется в *граф сборочных команд* для конкретной платформы, в котором каждый промежуточный или финальный результат формирует узел с ассоциированной командой,
    входами, выходами и зависимостями от таких же узлов, а также **UID** - хэш от всех собственных зависимостей команды (входов, зависимых файлов, текста команды,
    необходимого окружения, а также *UID*-ов зависимых команд). Команды сборки стартовых модулей становятся в этом графе **результатами**.
15. **Граф сборочных команд сохраняется.**
16. К данному моменту у нас есть граф сборочных команд для одной целевой платформы. От платформы зависят как минимум шаги 1, 3 (ya.make может содержать условные конструкции) и шаг 6.
    (формирование свойств и команд зависит от платформы). Однако, граф сборочных команд может содержать команды сборки сборочных инструментов, а их надо сформировать для сборочной платформы.
17. Находим все сборочные инструменты в графе команд.
18. Для инструментов запускаем шаги 1–16.
19. Заменяем части графа с шага 14, относящиеся к инструментам, на соответствующие части графа с шага 16.
20. Если запрошен запуск тестов - обогащаем граф узлами запуска тестов (это делается отдельным проходом по историческим причинам). Запуск тестов и получение результатов — это обычные узлы в графе команд.
    Чтобы тесты были исполнены, они добавляются к результатам графа. Если запрошена опция `— add-result`, то к результатам добавляются запрошенные промежуточные результаты.
21. Недостижимые от результатов узлы удаляются из графа.
22. *Граф сборочных команд* готов и его можно исполнять.
23. **Исполнение** начинается от результатов.
24. Если *UID* команды есть в кэше, то из кэша поднимается результат.
25. Если нет - пытается построить зависимости по шагам 24.-25.
26. Когда все зависимости построены, исполняем команду в узле.
27. Шаги 24.-25. могут делаться параллельно для независимых команд.
28. Процесс заканчивается, когда результаты построены.

### Немного про сборку Python { #python }

Обычно для Python нет понятия *сборки*, однако ya make собирает Python-код в исполняемые программы (описывается как `PY3_PROGRAM`). В такую программу попадает пре-компилированный код всех
зависимых пакетов, описанных как `PY3_LIBRARY`, код генерированных пакетов, например [`PROTO_LIBRARY`](./proto/modules.md#proto_library) а также, возможно бинарно откомпилированный C/C++ код доступный через биндинги на Cython.
Программа содержит интерпретатор, который и исполняет Python-код начиная с main-функции или модуля, описанных для этой программы.

Подробнее об этом можно прочитать в [разделе о сборке Python](./python)

## Полезные ссылки

- [Начало работы с системой сборки](../general/getting_started.md)
- [Базовые понятия системы сборки ya make](../general/base_concepts.md)
- [Как работает система сборки](../general/how_it_works.md)
- [Использование системы сборки](../usage/ya_make/index.md)
- Описание сборки:
  - [Описание синтаксиса ya.make](./syntax.md)
  - [Общие модули и макросы](./common/index.md)
  - С++: [пошаговое руководство](../tutorials/cpp.md) и [подробная инструкция](./cpp/index.md)
  - Python: [пошаговое руководство](../tutorials/python.md) и [подробная инструкция](./python/index.md)
  - Java: [пошаговое руководство](../tutorials/java.md) и [подробная инструкция](./java/index.md)
  - Go: [пошаговое руководство](../tutorials/go.md) и [подробная инструкция](./go/index.md)
  - Protobuf: [пошаговое руководство](../tutorials/protobuf.md) и [подробная инструкция](./proto/index.md)
  - Flatbuf: [пошаговое руководство](../tutorials/flatbuf.md) и [подробная инструкция](./flatbuf/index.md)
  - [Описание сборки и исполнения тестов](./tests/index.md)
- [Запуск сборки](../usage/ya_make/index.md)
- [Запуск тестов](../usage/ya_make/tests.md)
