# Локальная сборка ya make

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

Бóльшая часть опций сборки не зависит от того, где она исполняется и описана на [общей странице](./index.md)

Однако локальная сборка имеет ряд особенностей, к которым относятся:

- [Сборочные директории и файлы](#dirs)
- [Управление параллелизмом при исполнении сборки](#par)
- [Локальное кэширование](local_ya_cache.md#local-results)
- [Распределённое кэширование](yt_store.md)
- [Сборка с предпостроенными тулами](#prebuilt_tools)
- [Локальная работа с тестами](#tests)
- [Получение данных из SVN для сборки](#selective_co)

## Директории и файлы { #dirs }

Директории, которые использует локальная сборка настраиваются следующими ключами:

- `-S=CUSTOM_SOURCE_ROOT`, `--source-root=CUSTOM_SOURCE_ROOT` — путь до Аркадии из которой производить сборку. По умолчанию определяется автоматически по текущему пути, в котором запущена команда `ya make`.
- `-B=CUSTOM_BUILD_DIRECTORY`, `--build-dir=CUSTOM_BUILD_DIRECTORY` — корень сборочных директорий. Каждая сборочная команда исполняется в собственной директории, поэтому задаётся корень для всех таких директорий. По умолчанию эти директории размещаются внутри `~/.ya/build/`

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

- `-o=OUTPUT_ROOT`, `--output=OUTPUT_ROOT` — путь, куда будут сложены результаты сборки (на Linux и macOS будут использованы хардлинки). Результаты будут размещены по путям, соответствующим размещению описаний сборки в Аркадии.
- `I=INSTALL_DIR`, `--install=INSTALL_DIR` — **не рекомендуется** путь, куда будут сложены результаты сборки. Результаты будут сложены непосредственно в указанную директорию.

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

- `--log-file=LOG_FILE` — добавлять подробный лог к указанному файлу. По умолчанию на каждую сборку создаётся свой лог внутри `~/.ya/logs/`
- `--evlog-file=EVLOG_FILE` — записать лог событий в указанный файл. По умолчанию на каждую сборку создаётся свой лог внутри `~/.ya/evlogs/`

## Параллелизм в локальной сборке { #par }

Локальная сборка исполняет независимые сборочные команды параллельно. Для разных сборочных узлов доступен разный уровень параллелизма:

- Для обычной компиляции, генерации кода и скачивания ресурсов из Sandbox доступен самый высокий параллелизм. По умолчанию система сборки будет пытаться столько таких команд одновременно, 
  сколько есть ядер на сборочной машине. В большинстве случаев это разумное поведение. Однако, на системах с большим числом ядер и небольшим объёмам памяти при тяжёлых сборках это может
  приводить к ошибкам *Out of memory* (OOM). Кроме того, при сильной загрузке системы другой работой при таком параллелизме может снижаться отзывчивость. Для управления параллелизмом таких команд
  есть ключ `-j` (`--num-threads`) командной строки. Если хочется зафиксировать ограничение числа потоков на постоянной основе можно выставить параметр `num_threads` в файле `ya.conf`.

  {% note tip %}

  Поскольку ограничение разумнее иметь на систему, а не на пользователя параметр лучше писать в файл `$HOME/.ya/ya.conf`, а не коммитить в `junk/<user>/ya.conf`

  {% endnote %}

- Тесты тоже могу запускаться параллельно, однако предполагается, что они создают более высокую нагрузку и потому по умолчанию в параллель запускается не больше 2х тестов.
  Для управления параллелизмом запуска тестов служит параметр 	`--test-threads`.

- Аналогично тяжёлыми сборочными узлами считаются линковки. Их также запускают в параллель не больше 2х в обычном режиме и не больше одной в режимах `--lto`/`--thinlto`
  (Link-time optimization — режим, в котором вся компиляция происходит во время линковки для более глубокой оптимизации кода). Этот параллелизм настраивается ключом `--link-threads`.

- Отдельно настраивается параллелизм для скачивания с YT при использовании [распределённого кэша](./yt_store.md) параметром `--yt-store-threads`.

## Сборка с предпостроенными тулами { #prebuilt_tools }

По умолчанию локальная сборка для основных платформ (Linux, Darwin, Windows) в Аркадии выполняется с предпостроенными тулами. Это значит, что часть сборочных инструментов, которые собираются из Аркадии, приезжают в сборку в бинарном виде (как sandbox ресурсы). Список актуальных предпостроенных тулов можно найти [здесь](https://a.yandex-team.ru/arc/trunk/arcadia/build/prebuilt/ya.make). Такой механизм существенно сокращает время локальной сборки и снижает нагрузку на сборочные кэши, особенно после обновления рабочей копии. Дело в том, что сборочные тулы как правило зависят от util или стандартной библиотеки C++, которые, в свою очередь, обновляются достаточно часто. А это значит, что тулы будут перестраиваться тоже достаточно часто... И как следствие, будет перестраиваться всё что строится этими тулами в Аркадии - а это достаточное большое количество пересборок. 

{% note warning %}

Если по каким-то причинам хочется отключить использование предпостроенных тулов в локальной сборке, то в командной строке сборки необходимо добавить следующий набор флагов: `-DUSE_PREBUILT_TOOLS=no --host-platform-flag=USE_PREBUILT_TOOLS=no`

{% endnote %}

Дополнительные подробности о сборке с предпостроенными тулами можно прочитать [здесь](https://clubs.at.yandex-team.ru/arcadia/23540).

### Обновление предпостроенных тулов { #prebuilt_tools_update }

Для обновления предпостроенного тула предлагается использовать следующий алгоритм (на примере event2cpp):

1. Создайте ревью, закомментив там prebuild: [https://a.yandex-team.ru/review/2551740/details#comment-3520636](https://a.yandex-team.ru/review/2551740/details#comment-3520636)

2. Когда ревью финализируется, создайте тикет в DEVTOOLSSUPPORT с просьбой пересобрать нужный тул и приложите к нему pr

3. После получения id ресурсов (в саппортном тикете), раскомментриуйте prebuild и пропишите новые ресуры: [https://a.yandex-team.ru/review/2551740/files/3-4](https://a.yandex-team.ru/review/2551740/files/3-4)

{% note warning %}

В первом пункте рекомендуется прожать block merge, чтобы случайно не замержить.

{% endnote %}

## Локальная работа с тестами { #tests }

Локальный запуск тестов имеет ряд отличий от запуска в CI:

- По умолчанию локально результаты тестов не кэширутся. Т.е. независимо от изменений по команде `ya make -t` тесты будут запущены всегда.

- На данный момент локальный запуск тестов учитывает таймауты в соответствии с размером (1 минута для SMALL, 10 для MEDIUM, час для LARGE), но не ограничивает ресурсы в соответствии с `REQUIREMENTS`.

- По умолчанию локальная сборка производится в конфигурации `debug`, а в автосборке `relwithdebinfo` (релизная с ассёртами), поэтому поведение и производительность тестов может отличаться. Кроме этого, 
  в автосборке есть и другие отличия в конфигурации.

Результаты тестов локально складываются в директорию `test-results`, которая появляется символьной ссылкой в рабочей копии в директории теста (кроме Windows или флага `--no-src-links`).
Внутри находится директория `<suittype>/testing_out_stuff` в которой находятся логи тестов и прочие файлы, которые тесты порождают. 


{% note tip %}

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

{% endnote %}


## Селективный checkout из SVN { #selective_co }

{% note alert %}

Для разработки рекомендуется использовать систему контроля версий arc. Она предоставляет прозрачный доступ ко всем данным в репозитории, необходимым для сборки и лишена [проблем](#issues),
свойственных селективному чекауту из SVN.

{% endnote %}

Система сборки позволяет получить из SVN всё, что нужно для сборки конкретного проекта. Далее предполагается, что у вас уже есть утилита `ya`.

Порядок действий должен быть следующий:

1. Получаем каркас Аркадии (всё, что нужно системе сборки)
```
ya clone arcadia
```

2. В полученной рабочей копии получаем селективный чекаут вашего проекта

```
ya make --checkout <your/project>
```

Важные замечания:

- Вторая команда не только скачает, но и построит ваш проект. Чтобы отказаться от сборки используйте опцию `-j0`
- Указанная команда получит всё, что нужно для сборки (включая зависимости), но не то, что нужно для тестов. Если предполагается запускать тесты, добавьте опцию `-t`.
- Селективный чекаут фиксирует зависимости на конкретную ревизию. При изменении зависимостей может потребоваться снова запустить `ya make --checkout` после `svn up`.
- `ya make --checkout` можно запускать несколько раз для разных проектов или конфигураций. Она будет скачивать в рабочую копию только недостающее.
- `ya make --checkout` не обновляет ревизию. Она всегда работает в текущей ревизии Аркадии. Несколько её запусков для разных проектов в разные моменты без `svn up` создадут рабочую копию с зависимостями всех этих проектов на одной ревизии.

{% note alert %}

Всегда обновляйте (делайте `svn up`) в корне частичной Аркадии, при разных ревизиях в разных частях дерева сборка может работать неправильно.

{% endnote %}

### Как это работает

Селективный чекаут работает следующим образом:

1. Начиная от целей система сборки пытается построить сборочный граф в режиме `--keep-going`. Если какие-то файлы или зависимости недоступны, она выдаёт ошибки.
2. Селективный чекаут перехватывает эти ошибки и по эвристикам решает надо ли счекаутить только недостающий файл или файл вместе со всеми директориями
3. С помощью возможностей svn по ограничению чекаута (`--set-depth`) нужные пути скачиваются. Неудачи фиксируются, чтобы не пробовать снова.
4. Цикл повторяется с п.1. пока либо сборка не пройдёт без ошибок, либо не останется путей, которые не пробовали.
5. После этого запускается сборка в обычном режиме.

### Что может пойти не так { #issues }

1. Селективный чекаут не быстрый и итерационный. Чем глубже зависимости у проекта, тем больше итераций он сделает. 

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

3. Из-за эвристической природы и использования ошибок для работы при определённой структуре проектов селективный чекаут может ошибаться. Типичный пример — глубокий `PEERDIR`:

- Допустим у нас есть проект `program`, у которого есть в зависимостях библиотека, на которую есть такой `PEERDIR(lib/src)`
- Сама библиотека устроена следующим образом
  ```
  lib
  ├── src
  │    ├── impl.cpp
  │    └── ya.make
  ├── lib_h
  │    └── hdr.h
  └── ya.make
  ```
- `lib/src/ya.make` содержит `ADDINCL(lib)` 
- `lib/src/impl.cpp` содержит `#include <lib_h/hdr.h>`

При селективном чекауте `ya make --checkout program` случится следующее:

- По зависимости из program/ya.make будет получено дерево `lib/src`
- На следующей итерации ошибок не возникнет, поскольку `lib` из `ADDINCL` уже есть на файловой системе
- Однако при попытке собрать библиотеку возникнут проблемы — заголовочный файл `lib_h/hdr.h` не найдётся, потому что вся директория `lib_h` не будет скачана.

Чтобы починить эту проблему локально можно

- Дочекаутить недостающие файлы руками (например, `svn co lib --set-depth=infinity`)
- Изменить структуру проекта lib. Описание сборки вместе с `ADDINCL` надо перенести в `lib/ya.make`, использовав `SRCDIR(srс)` для более удобного перечисления исходных файлов.
