Что такое bazel linux

Обновлено: 04.07.2024

If you have questions or need help, see the Get Support page.

What is Bazel?

Bazel is a tool that automates software builds and tests. Supported build tasks include running compilers and linkers to produce executable programs and libraries, and assembling deployable packages for Android, iOS and other target environments. Bazel is similar to other tools like Make, Ant, Gradle, Buck, Pants and Maven.

What is special about Bazel?

Bazel was designed to fit the way software is developed at Google. It has the following features:

Multi-language support: Bazel supports many languages, and can be extended to support arbitrary programming languages.

High-level build language: Projects are described in the BUILD language, a concise text format that describes a project as sets of small interconnected libraries, binaries and tests. In contrast, with tools like Make, you have to describe individual files and compiler invocations.

Multi-platform support: The same tool and the same BUILD files can be used to build software for different architectures, and even different platforms. At Google, we use Bazel to build everything from server applications running on systems in our data centers to client apps running on mobile phones.

Reproducibility: In BUILD files, each library, test and binary must specify its direct dependencies completely. Bazel uses this dependency information to know what must be rebuilt when you make changes to a source file, and which tasks can run in parallel. This means that all builds are incremental and will always produce the same result.

Scalable: Bazel can handle large builds; at Google, it is common for a server binary to have 100k source files, and builds where no files were changed take about

Why doesn’t Google use …?

Make, Ninja: These tools give very exact control over what commands get invoked to build files, but it’s up to the user to write rules that are correct.

Users interact with Bazel on a higher level. For example, Bazel has built-in rules for “Java test”, “C++ binary”, and notions such as “target platform” and “host platform”. These rules have been battle tested to be foolproof.

Ant and Maven: Ant and Maven are primarily geared toward Java, while Bazel handles multiple languages. Bazel encourages subdividing codebases in smaller reusable units, and can rebuild only ones that need rebuilding. This speeds up development when working with larger codebases.

Gradle: Bazel configuration files are much more structured than Gradle’s, letting Bazel understand exactly what each action does. This allows for more parallelism and better reproducibility.

Pants, Buck: Both tools were created and developed by ex-Googlers at Twitter and Foursquare, and Facebook respectively. They have been modeled after Bazel, but their feature sets are different, so they aren’t viable alternatives for us.

Where did Bazel come from?

Bazel is a flavor of the tool that Google uses to build its server software internally. It has expanded to build other software as well, like mobile apps (iOS, Android) that connect to our servers.

Did you rewrite your internal tool as open-source? Is it a fork?

Bazel shares most of its code with the internal tool and its rules are used for millions of builds every day.

Why did Google build Bazel?

A long time ago, Google built its software using large, generated Makefiles. These led to slow and unreliable builds, which began to interfere with our developers’ productivity and the company’s agility. Bazel was a way to solve these problems.

Does Bazel require a build cluster?

Bazel runs build operations locally by default. However, Bazel can also connect to a build cluster for even faster builds and tests. See our documentation on remote execution and caching and remote caching for further details.

How does the Google development process work?

For our server code base, we use the following development workflow:

All our server code is in a single, gigantic version control system.

Everybody builds their software with Bazel.

Different teams own different parts of the source tree, and make their components available as BUILD targets.

Branching is primarily used for managing releases, so everybody develops their software at the head revision.

Bazel is a cornerstone of this philosophy: since Bazel requires all dependencies to be fully specified, we can predict which programs and tests are affected by a change, and vet them before submission.

More background on the development process at Google can be found on the eng tools blog.

Why did you open up Bazel?

Building software should be fun and easy. Slow and unpredictable builds take the fun out of programming.

Why would I want to use Bazel?

Bazel may give you faster build times because it can recompile only the files that need to be recompiled. Similarly, it can skip re-running tests that it knows haven’t changed.

Bazel produces deterministic results. This eliminates skew between incremental and clean builds, laptop and CI system, etc.

Bazel can build different client and server apps with the same tool from the same workspace. For example, you can change a client/server protocol in a single commit, and test that the updated mobile app works with the updated server, building both with the same tool, reaping all the aforementioned benefits of Bazel.

Can I see examples?

Yes. For a simple example, see:

The Bazel source code itself provides a more complex example:

What is Bazel best at?

Bazel shines at building and testing projects with the following properties:

  • Projects with a large codebase
  • Projects written in (multiple) compiled languages
  • Projects that deploy on multiple platforms
  • Projects that have extensive tests

Where can I run Bazel?

Bazel runs on Linux, macOS (OS X), and Windows.

Porting to other UNIX platforms should be relatively easy, as long as a JDK is available for the platform.

What should I not use Bazel for?

Bazel tries to be smart about caching. This means that it is not good for running build operations whose outputs should not be cached. For example, the following steps should not be run from Bazel:

  • A compilation step that fetches data from the internet.
  • A test step that connects to the QA instance of your site.
  • A deployment step that changes your site’s cloud configuration.

If your build consists of a few long, sequential steps, Bazel may not be able to help much. You’ll get more speed by breaking long steps into smaller, discrete targets that Bazel can run in parallel.

How stable is Bazel’s feature set?

The core features (C++, Java, and shell rules) have extensive use inside Google, so they are thoroughly tested and have very little churn. Similarly, we test new versions of Bazel across hundreds of thousands of targets every day to find regressions, and we release new versions multiple times every month.

In short, except for features marked as experimental, Bazel should Just Work. Changes to non-experimental rules will be backward compatible. A more detailed list of feature support statuses can be found in our support document.

How stable is Bazel as a binary?

Inside Google, we make sure that Bazel crashes are very rare. This should also hold for our open source codebase.

How can I start using Bazel?

Doesn’t Docker solve the reproducibility problems?

With Docker you can easily create sandboxes with fixed OS releases, for example, Ubuntu 12.04, Fedora 21. This solves the problem of reproducibility for the system environment – that is, “which version of /usr/bin/c++ do I need?”

Docker does not address reproducibility with regard to changes in the source code. Running Make with an imperfectly written Makefile inside a Docker container can still yield unpredictable results.

Inside Google, we check tools into source control for reproducibility. In this way, we can vet changes to tools (“upgrade GCC to 4.6.1”) with the same mechanism as changes to base libraries (“fix bounds check in OpenSSL”).

Can I build binaries for deployment on Docker?

With Bazel, you can build standalone, statically linked binaries in C/C++, and self-contained jar files for Java. These run with few dependencies on normal UNIX systems, and as such should be simple to install inside a Docker container.

Bazel has conventions for structuring more complex programs, for example, a Java program that consumes a set of data files, or runs another program as subprocess. It is possible to package up such environments as standalone archives, so they can be deployed on different systems, including Docker images.

Can I build Docker images with Bazel?

Yes, you can use our Docker rules to build reproducible Docker images.

Will Bazel make my builds reproducible automatically?

Do not use dependencies that were not declared. Sandboxed execution (–spawn_strategy=sandboxed, only on Linux) can help find undeclared dependencies.

Avoid storing timestamps and user-IDs in generated files. ZIP files and other archives are especially prone to this.

Avoid connecting to the network. Sandboxed execution can help here too.

Avoid processes that use random numbers, in particular, dictionary traversal is randomized in many programming languages.

Do you have binary releases?

Yes, you can find the latest release binaries here. Our release policy is documented here.

I use Eclipse/IntelliJ/XCode. How does Bazel interoperate with IDEs?

For XCode, check out Tulsi.

For Eclipse, check out E4B plugin.

For other IDEs, check out the blog post on how these plugins work.

I use Jenkins/CircleCI/TravisCI. How does Bazel interoperate with CI systems?

Bazel returns a non-zero exit code if the build or test invocation fails, and this should be enough for basic CI integration. Since Bazel does not need clean builds for correctness, the CI system should not be configured to clean before starting a build/test run.

Further details on exit codes are in the User Manual.

What future features can we expect in Bazel?

Can I use Bazel for my [INSERT LANGUAGE HERE] project?

If you would like to develop extensions or learn how they work, see the documentation for extending Bazel.

Can I contribute to the Bazel code base?

Why isn’t all development done in the open?

We still have to refactor the interfaces between the public code in Bazel and our internal extensions frequently. This makes it hard to do much development in the open.

Are you done open sourcing Bazel?

Open sourcing Bazel is a work-in-progress. In particular, we’re still working on open sourcing:

  • Many of our unit and integration tests (which should make contributing patches easier).
  • Full IDE integration.

Beyond code, we’d like to eventually have all code reviews, bug tracking, and design decisions happen publicly, with the Bazel community involved. We are not there yet, so some changes will simply appear in the Bazel repository without clear explanation. Despite this lack of transparency, we want to support external developers and collaborate. Thus, we are opening up the code, even though some of the development is still happening internal to Google. Please let us know if anything seems unclear or unjustified as we transition to an open model.

Are there parts of Bazel that will never be open sourced?

Yes, some of the code base either integrates with Google-specific technology or we have been looking for an excuse to get rid of (or is some combination of the two). These parts of the code base are not available on GitHub and probably never will be.

How do I contact the team?

Where do I report bugs?

What’s up with the word “Blaze” in the codebase?

This is an internal name for the tool. Please refer to Bazel as Bazel.

Why do other Google projects (Android, Chrome) use other build tools?

Until the first (Alpha) release, Bazel was not available externally, so open source projects such as Chromium, Android, etc. could not use it. In addition, the original lack of Windows support was a problem for building Windows applications, such as Chrome.

How do you pronounce “Bazel”?

The same way as “basil” (the herb) in US English: “BAY-zel”. It rhymes with “hazel”. IPA: /ˈbeɪzˌəl/

В последнее время на Хабре появляются посты про то, что cmake и c++ — друзья, приводятся примеры, как собирать header-only библиотеки и не только, но нет обзора хоть сколько-нибудь новых систем сборки — bazel, buck, gn и других. Если вы, как и я, пишете на C++ в 2к20, то предлагаю вам познакомиться с bazel как системой сборки c++ проекта.

Оставим вопросы, чем плохи cmake и другие существующие системы, и сконцентрируемся на том, что может сам bazel. Решать, что лучше конкретно для вас, я оставляю конкретно вам.

Начнем с определения и мотивации. Bazel это мультиязычная система сборки от гугла, которая умеет собирать c++ проекты. Почему мы вообще должны смотреть на еще одну систему сборки? Во первых, потому что ей уже собираются некоторые большие проекты, например Tensorflow, Kubernetes и Gtest, и соответственно чтобы интегрироваться с ними уже нужно уметь пользоваться bazel. Во вторых, кроме гугла bazel еще использует spaceX, nvidia и другие компании судя по их выступлениям на bazelcon. И наконец, bazel это довольно популярный open-source проект на github, так что он определенно стоит того чтобы на него взглянуть и попробовать.

Пример 1. Тривиальный

Есть main.cc и надо его скомпилировать:


Начинается все с объявления workspace. В терминах bazel workspace это директория в которой лежат все ваши исходные файлы. Чтобы этот workspace обозначить надо создать пустой файл с именем WORKSPACE в нужной нам директории, обычно это директория src.

Минимальной единицей организации кода в bazel является пакет. Пакет определяется директорией с исходниками и специальным BUILD файлом, который описывает как собираются эти исходники.

Добавим пакет main в наш проект:


В BUILD файле мы должны теперь описать что мы хотим собрать из нашего main. Мы, естественно, хотим собрать исполняемый бинарный файл, так что будем использовать правило cc_binary. Bazel уже из коробки поддерживает C++, так что уже есть определенный набор правил для сборки c++ целей, с остальными мы познакомимся дальше.

Добавляем правило cc_binary в BUILD файл, у него есть имя, которое будет иметь исполняемый файл и массив исходников которые будут переданы компилятору. Все это описывается на языке starlark, который является урезанным питоном.


Bazel, в отличие от cmake не основывается на командах, а позволяет декларативно описывать зависимости через правила. По сути, правила связывают несколько артефактов с определенной операцией. С помощью них bazel строит граф команд, который затем кэширует и исполняет. В нашем случае исходный файл main.cс связывается с операцией компиляциии, результатом которой будет артефакт hello_world — бинарный исполняемый файл.

Чтобы теперь получить наш исполняемый файл мы должны перейти в директорию с workspace и набрать:


Система сборки принимает команду build и путь до нашей цели, начинающийся от корня нашего проекта.

Итоговый бинарник будет расположен в bazel-bin/main/hello_world.

Пример 2. Сборка со своей библиотекой

К счастью, такие простые проекты особо никому не нужны, так что давайте посмотрим как добавить функциональности в наш проект. Добавим библиотеку, которая будет собираться отдельно и линковаться с нашим main.

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


Обратите внимание на подключение заголовочного файла, я делаю это через путь от workspace, даже несмотря на то, что файл лежит в той же самой директории. Такой подход принят в chromium code style guide, который наследуется от google c++ style guide. Такой способ позволяет сразу понимать откуда подключается заголовочный файл. Не волнуйтесь, файл найдется, bazel добавит пути для поиска заголовочных файлов, а вот если вы не будете следовать этому правилу, то заголовочные файлы могут и не найтись при сборки bazel’ом.

В BUILD файле нашей библиотеки описываем правило для сборки библиотек cc_library:


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

В main.cc мы используем нашу библиотеку:


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

И описываем зависимость в правиле сборки для main от библиотеки square.


Собирается вся наша программа точно также, как и без использования библиотеки, bazel сам поймет что от чего зависит, построит граф, закеширует результаты и пересоберет только то, что нужно пересобрать.

Пример 3. Подключение тестов

Как вообще жить без тестов? Никак! Чтобы подключить к bazel GTest, который к слову уже поддерживает сборку с bazel нужно добавить внешнюю зависимость. Делается это в WORKSPACE файле:


Все как у хипстеров, подключили правило git_repository и указали bazel’у какую версию скачать.

Дальше создаем отдельный пакет для тестов test и добавляем в него тесты на нашу библиотеку:


Теперь настал черед определение правила для тестов.


Добавили зависимости от нашей библиотеки и от gtest_main, чтобы библиотека gtest сама предоставила нам реализацию лаунчера.

Запускаются тесты командой:


Bazel сам скачает и построит GTest, слинкует все что надо для тестов и запустит сами тесты.

Упомяну, что bazel также умеет делать code coverage:


А если нужно отладить тесты, то собрать все в debug режиме с символами можно так:

Пример 4. Подключение других библиотек, которые не умеют в bazel

Конечно, мир не строится на одном лишь bazel, так что надо уметь подключать и другие библиотеки. Недавно в своем проекте мне понадобилась библиотека для разбора аргументов командной строки. Ну и не писать же мне в 2к20 свою такую библиотеку и отвлекаться от основной работы. Использовать какие-то полумеры, вроде getops тоже очень не хочется, как и тащить boost в свой проект.

Не для рекламы ради подключим библиотеку CLI11, которая не использует ничего лишнего кроме кроме stl стандарта C++11 и предоставляет более-менее удобный интерфейс.

Библиотека это header-only, что делает её подключение особенно простым.

Подключаем внешнюю зависимость в WORKSPACE:


Добавляем директорию third-party и добавляем пакет CLI11 для удобства построения зависимостей от этой библиотеки:


Bazel по умолчанию будет искать файл с библиотекой по пути /external/CLI11 так что мы немного поменяем пути чтобы подключать его по CLI11/.


В зависимости main добавляем "//third_party/CLI11:CLI11" и все начинает работать.
Не знаю как вам, но подключение какой-то незнакомой библиотеки и её использование в проекте на c++ в таком виде приводит меня в восторг.

Пример 5. Скрипты и автоматизация

Кому в 2к20 нужен проект на голом c++ без скриптов для автоматизации? Обычно такие скрипты нужны для запуска перф тестов или для деплоя ваших артефакты куда-нибудь на CI. Ну и обычно они пишутся на питоне.

Для этого bazel тоже годится, так как он умеет почти во все популярные языки и как раз и предназначен для того, чтобы собирать такие солянки из разных языков программирования, которые так часто встречаются в реальных проектах.

Давайте подключим питон скрипт, который будет запускать наш main.

Добавляем пакет perf-tests:


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


Для того чтобы запустить наши тесты просто пишем:

Заключение и то, чего не коснулись

Мы коротко посмотрели на bazel и его основные возможности для сборки исполняемых файлов и библиотек, как сторонних, так и своих. На мой вкус получается довольно лаконично и очень быстро. Не надо страдать и искать какой-нибудь туториал по cmake чтобы сделать какую-то тривиальную вещь и чистить CmakeCache.

Если вам будет интересно, то за бортом осталось еще много всего: подключение protocol buffers, санитайзеры, настройка тулчейна чтобы компилировать под разные платформы/архитектуры.


В этой статье я расскажу о практическом опыте работы с Bazel, утилитой для автоматизации сборки и тестирования софта от Google. Мы, компания NtechLab, разрабатываем платформу видеоаналитики FindFace. Продукт большой и сложный, используется много разных языков программирования и библиотек, соответственно процесс сборки у нас громоздкий. В поисках инструмента, способного упростить и ускорить сборку, мы остановились на Bazel.

Что такое Bazel

Bazel - это утилита для автоматизации сборки и тестирования софта от Google. Первый релиз случился в далеком 2015 году, как opensource часть Blaze - внутренней системы сборки Google. Акцент сделан на скорость, корректность и воспроизводимость всех процессов - для этого поддерживается централизованное кэширование, удаленная сборка и изолирование каждой части.

Для описания сборки используется Python-подобный язык skylark и Workspace правила, нативно поддерживаются C, C++, Go, Python, Java, Objective-C и Bourne-shell, но есть возможность сделать расширения и для других языков.

Как у нас было раньше

Чтобы понять, какие у нас были проблемы и как мы их решили с переходом на Bazel, я расскажу про наши основные сервисы. Упрощенная схема их зависимостей выглядит так:


extraction-api и video-worker - микросервисы, sdk - пользовательский продукт (shared library), facenkit - внутренняя библиотека для инференса нейронных сетей, opencv / openvino / tensorrt / cuda - внешние зависимости. На самом деле как микросервисов, так и их зависимостей намного больше, но в этой статье будем рассматривать упрощенный случай.

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

Для каждой из внешних зависимостей у нас был отдельный репозиторий во внутреннем gitlab: зеркало фиксированной версии апстрима, наши патчи и .gitlab-ci.yml для воспроизводимой сборки и упаковки в пакет. Библиотека facenkit тоже находилась в отдельном репозитории, перед сборкой она скачивались и устанавливались необходимые зависимости ( opencv , tensorrt ) фиксированной версии.

И, наконец, при сборке “финального” микросервиса, например video-worker, нужно было скачать не только артефакт библиотеки facenkit , но и артефакты сборок всех ее зависимостей.

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

Внести изменения в код, собрать библиотеку. Уже на этом этапе были трудности: размер некоторых зависимостей сотни мегабайт, и упаковка в пакет с последующей установкой занимают далеко не секунду; делать же костыли в следующих этапах сборки вида “бери сейчас зависимость из этой build-директории, а не из /usr/… ” или симлинкать /usr/… на артефакт из текущей сборки - во-первых, не очень удобно, а во-вторых, высок шанс что-то упустить.

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

Когда доволен результатом локально - обновить три репозитория (зависимость, facenkit , video-worker) , прописав везде нужные (и одинаковые) версии.

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

Как стало с Bazel

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

В файле WORKSPACE описываются внешние зависимости: указываются URL архива (можно использовать несколько зеркал) и его sha256 хэш-сумма, набор патчей и build_file, в котором описываются правила сборки:

В компонентах, для которых opencv является зависимостью, правило сборки может выглядеть так:

С какими сложностями столкнулись

Основной сложностью было разобраться с новым инструментом и написать BUILD-файлы для наших сервисов и их зависимостей. Рассмотрим это на примере простой библиотеки с нативной bazel-сборкой и более сложного примера, где проще использовать cmake-wrapper.

bazel-way сборка

WORKSPACE:

Указываем, откуда скачивать исходники (в т.ч. локальное зеркало), sha256hash и где брать правило для сборки.

third_party/com_github_fmtlib_fmt/BUILD.bazel:

Создаем библиотеку из двух .cc файлов и экспортируем наружу публичный интерфейс через заголовки hdrs .

cmake сборка libjpeg-turbo

Для libjpeg-turbo написать нативный BUILD-файл хоть и возможно, но вариант с cmake-сборкой выглядит проще. Для более сложных библиотек, типа OpenCV или OpenVino cmake-сборка практически не имеет альтернативы, если у вас нет нескольких свободных недель.

WORKSPACE:

Тут все аналогично примеру с библиотекой .

third_party/jpeg/BUILD.bazel:

Здесь используется rules_foreign_cc, который помогает делать сборку cmake и “configure && make” проектов. Для полного понимания всех параметров стоит обратиться к документации, но большинство из них понятны по имени.

Что мы получили

Пересборка только того, что нужно

Благодаря тому, что Bazel точно знает полный граф всех зависимостей, он пересобирает только то, что действительно нужно. Например, если A зависит от B и C, C зависит от E, то при изменении кода/параметров сборки E при сборке A будут пересобраны E, C, A, а B будет взят из кэша.

Быстрые тесты

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

Общие параметры сборки

Хотите debug-сборку или сборку с санитайзерами? Всего одним флагом можно пересобрать все с нужными вам параметрами.

Единая сборка под все системы

Не важно, под какой дистрибутив Linux или Windows мы делаем сборку, x86_64 это или arm64 - правила сборки под всё описаны в одном месте, а платформо-специфичные вещи удобно интегрируются с помощью select().

Единая версия зависимостей

Все зависимости всегда одной и той же версии - во всех компонентах и под все платформы, больше не нужно подстраиваться под libXXX-1.0 на старой LTS системе, когда нужные нам функции есть в свежем релизе libXXX-3.0.

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

tensorflow/third_party - Bazel-рецепты сборок зависимостей TensorFlow.

C++ Bazel Tutorial - раздел в официальной документации по Bazel с простым C++ туториалом.

DL4AGX - проект от NVIDIA, который собирается с помощью Bazel, и в котором им собираются CUDA-kernels.

Software Engineering at Google: Lessons Learned from Programming Over Time - как устроена разработка в Google, есть главы про Blaze (из которого родился Bazel) и монорепу - почему они к этому пришли, какие проблему решали и решили, кому это подходит, а кому нет.

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

Появление Maven-а породило волну различных систем сборок, каждая из которых в чем-то улучшала Maven или рассматривала процесс сборки немного под другим углом: Buildr, Gradle, Pants, Buck и некоторые другие.

И тут мы приходим к Bazel.
Итак, что же побудило Google создать свою собственную систему сборки при таком обилии уже существующих. На сайте Bazel на этот вопрос дается такой ответ:

Почему не Make?
Слишком низкоуровневый и подробный. Bazel позволяет высокоуровнево описывать сборку.

Почему не Ant или Maven?
Ant и Maven разработаны в основном для Java-проектов, а Bazel поддерживает многоязычность, что актуально для больших проектов.

Почему не Gradle?
Bazel имеет более структурированную систему конфигурации проектов, что позволяет добиться более высокой производительности за счет параллелизма.

Почему не Pants и Buck?
Pants и Buck были разработаны бывшими сотрудниками Google, которые спроектировали эти инструменты после Bazel, но имеют не так много возможностей, чтобы конкурировать с Bazel.
(довольно слабый аргумент, IMHO - прим. Toolkas)

  1. Google хочет собирать многоязычные проекты, что типично для компании, имеющей дело со сверхвысокой нагрузкой и гигантскими объемами данных;
  2. Google хочет собирать большие многоязычные проекты;
  3. Google хочет очень быстро собирать большие многоязычные проекты;
  4. Google хочет собирать проекты по-своему=).

Ну или как-то так.
Теперь, когда мы приблизительно в курсе причин, приведших к созданию Bazel, установим его на компьютер.

На официальном сайте Bazel подробно расписана процедура установки на компьютер в зависимости от операционной системы. По великой традиции, восходящей к Кернигану и Ричи, создадим программу, которая выводит "Hello, world!" на Java и соберем эту программу с помощью Bazel.
Для тех, кто видит Java впервые, приведу ее код:
Теперь создадим папку проекта helloworld с такой структурой:

Папка src содержит исходники и ее структура полностью идентична таковой в Maven.
Теперь разберемся с новыми файлами в структуре проекта: WORKSPACE и BUILD.

Файл WORKSPACE должен находиться в корне файловой структуры проекта, помечая его, таким образом, как проект Bazel. Обычно этот файл содержит глобальные настройки проекта, ссылки на репозитории внешних артефактов и тому подобное.

Наш проект очень простой, никакие внешние артефакты ему не нужны, поэтому оставим файл WORKSPACE пока пустым.

Файл BUILD (их может быть несколько) содержит информацию, как собирать различные части проекта. Папку, в которой лежит файл BUILD называют пакетом (package). Файл BUILD использует правила сборки (build rule), которые описывают, как именно собирать отдельные части проекта. Конкретное именованное использование правила сборки называется цель (target), ее можно вызвать отдельно. Содержимое файла BUILD пишется на языке программирования Starlark, специально разработанном для Bazel. Starlark можно считать диалектом Python, но он не является языком общего назначения. Файл BUILD нашего простого проекта будет иметь вид:

Файл BUILD использует два встроенных правила: java_library и java_binary.
java_library компилирует исходники и упаковывает классы в jar-файл.
Для того, чтобы использовать правило java_library, нужно вызвать по имени цель, которая использует это правило.
Выглядит это так:

Эту команду нужно выполнить в корне проекта.
В результате Bazel соберет jar-файл:
Обратите внимание, что при вызове цели мы используем имя, которое прописано в атрибуте name правила java_library.

Именно по имени мы можем вызывать цели Bazel, поэтому имена целей в файле BUILD должны быть уникальны.

Теперь рассмотрим цель helloworld. Она использует правило java_binary, которое предназначено для того, чтобы создать запускаемый модуль.
Если вы используете Bazel в операционной системе Windows, то результатом работы правила java_binary станет запускаемый jar-файл и exe-файл. Удобно, не правда ли?=)

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

Чтобы собрать запускаемый модуль, нужно выполнить команду:
в результате чего Bazel соберет jar и exe файлы:
Запустить программу на выполнение можно командой:
Красным цветом выделена строчка, которую напечатал класс HelloWorld.

Тестовые примеры, вроде нашего HelloWorld, хорошо демонстрируют принцип, но совершенно оторваны от реальности. Хочется попробовать Bazel на немного более сложном проекте. Допустим, у нас есть интерфейс кода приветствия:

И два варианта реализации приветствия, на английском:
и на русском:
Все три класса мы хотим организовать в виде отдельных jar-файлов. Есть много способов получить такой результат, рассмотрим один из них:
Поскольку нам не требуется никаких глобальных настроек, то и в этом случае файл WORKSPACE пустой.

Ни один из трех классов не является запускаемым, значит нам не нужно использовать правило java_binary. Все три класса должны просто собираться в jar-файлы, поэтому мы используем правило java_library.
Поскольку greeting-api должно быть доступно во время сборки проектам greeting-en и greeting-ru, для проекта greeting-api необходимо указать видимость. Таким образом, файл greeting-api/BUILD будет выглядеть так:
greeting-ru и greeting-en - это пакеты, которые содержат конкретные реализации, поэтому этим пакетам не нужна публичная видимость. Единственное, что им нужно - это greeting-api. Поэтому файл greeting-en/BUILD будет выглядеть так:
а файл greeting-ru/BUILD так:
Из этих примеров видно, как корректно делать ссылки на правила: //[относительный путь от корня проекта]:[имя правила в BUILD]. Теперь для сборки всех модулей достаточно выполнить команду:

Bazel хорош тем, что жестко контролирует зависимости пакетов. Это сделано для воспроизводимости результатов сборки в любом окружении. Чтобы сгенерировать дерево зависимостей greeting-en, нужно в корне проекта выполнить команду:

Если мы хотим представить дерево зависимостей более наглядно, необходимо скопировать код графа
и вставить его в GraphViz, который покажет нам вот такую симпатичную картинку:

Читайте также: