Le BazelCon 2022 se déroulera du 16 au 17 novembre à New York et en ligne.
S'inscrire

Tutoriel Bazel: créer un projet C++

Restez organisé à l'aide des collections Enregistrez et classez les contenus selon vos préférences.

Présentation

Vous débutez avec Bazel ? Vous êtes au bon endroit. Suivez ce tutoriel de compilation initiale pour une présentation simplifiée de l'utilisation de Bazel. Ce tutoriel définit les termes clés tels qu'ils sont utilisés dans le contexte de Bazel et vous guide tout au long des bases du workflow Bazel. En commençant par les outils dont vous avez besoin, vous allez créer et exécuter trois projets avec une complexité croissante, et découvrir comment et pourquoi ils deviennent plus complexes.

Bien que Bazel soit un système de compilation compatible avec les builds multilingues, ce tutoriel utilise un projet C++ comme exemple et fournit les consignes et le flux généraux qui s'appliquent à la plupart des langages.

Durée estimée: 30 minutes.

Prerequisites

Commencez par installer Bazel, si ce n'est pas déjà fait. Ce tutoriel utilise Git pour le contrôle des sources. Par conséquent, pour de meilleurs résultats, installez Git.

Ensuite, récupérez l'exemple de projet à partir du dépôt GitHub de Bazel en exécutant la commande suivante dans l'outil de ligne de commande de votre choix:

git clone https://github.com/bazelbuild/examples

L'exemple de projet de ce tutoriel se trouve dans le répertoire examples/cpp-tutorial.

Découvrez ci-dessous comment elle est structurée:

examples
└── cpp-tutorial
    ├──stage1
    │  ├── main
    │  │   ├── BUILD
    │  │   └── hello-world.cc
    │  └── WORKSPACE
    ├──stage2
    │  ├── main
    │  │   ├── BUILD
    │  │   ├── hello-world.cc
    │  │   ├── hello-greet.cc
    │  │   └── hello-greet.h
    │  └── WORKSPACE
    └──stage3
       ├── main
       │   ├── BUILD
       │   ├── hello-world.cc
       │   ├── hello-greet.cc
       │   └── hello-greet.h
       ├── lib
       │   ├── BUILD
       │   ├── hello-time.cc
       │   └── hello-time.h
       └── WORKSPACE

Ce tutoriel comporte trois ensembles de fichiers, chacun représentant une étape. Dans la première étape, vous allez créer une seule cible résidant dans un seul package. Lors de la deuxième étape, vous allez créer un binaire et une bibliothèque à partir d'un seul package. Lors de la troisième et dernière étape, vous allez créer un projet avec plusieurs packages et le faire avec plusieurs cibles.

Résumé: introduction

En installant Bazel (et Git) et en clonant le dépôt pour ce tutoriel, vous avez posé les bases de votre première compilation avec Bazel. Passez à la section suivante pour définir certains termes et configurer votre espace de travail.

Premiers pas

Configurer l'espace de travail

Avant de pouvoir créer un projet, vous devez configurer son espace de travail. Un espace de travail est un répertoire qui contient les fichiers sources de votre projet et les sorties de build de Bazel. Il contient également les fichiers importants suivants:

  • Le fichier WORKSPACE file , qui identifie le répertoire et son contenu en tant qu'espace de travail Bazel et se trouve à la racine de la structure de répertoire du projet.
  • Un ou plusieurs BUILD files , qui indiquent à Bazel comment créer différentes parties du projet Un répertoire de l'espace de travail contenant un fichier BUILD est un package. (Nous aborderons les packages plus tard dans ce tutoriel.)

Dans les futurs projets, pour désigner un répertoire en tant qu'espace de travail Bazel, créez un fichier vide nommé WORKSPACE dans ce répertoire. Pour les besoins de ce tutoriel, un fichier WORKSPACE est déjà présent à chaque étape.

REMARQUE: Lorsque Bazel compile le projet, toutes les entrées doivent se trouver dans le même espace de travail. Les fichiers situés dans différents espaces de travail sont indépendants les uns des autres, sauf s'ils sont associés. Pour en savoir plus sur les règles d'espace de travail, consultez ce guide.

Comprendre le fichier BUILD

Un fichier BUILD contient plusieurs types d'instructions pour Bazel. Chaque fichier BUILD requiert au moins une règle sous forme d'instructions, qui indique à Bazel comment créer les sorties souhaitées, telles que des bibliothèques ou des fichiers binaires exécutables. Chaque instance d'une règle de compilation dans le fichier BUILD est appelée cible et pointe vers un ensemble spécifique de fichiers et de fichiers sources. Une cible peut également pointer vers d'autres cibles.

Examinez le fichier BUILD dans le répertoire cpp-tutorial/stage1/main:

cc_binary(
    name = "hello-world",
    srcs = ["hello-world.cc"],
)

Dans notre exemple, la cible hello-world instancie la fonction Bazel intégrée cc_binary rule. La règle indique à Bazel de créer un binaire exécutable autonome à partir du fichier source hello-world.cc, sans dépendances.

Récapitulatif: premiers pas

Vous connaissez maintenant certains termes clés, ainsi que leur signification dans le contexte de ce projet et de Bazel en général. Dans la section suivante, vous allez créer et tester l'étape 1 du projet.

Étape 1: cible unique, package unique

Il est temps de créer la première partie du projet. Pour référence visuelle, la structure de la section "Étape 1" du projet est la suivante:

examples
└── cpp-tutorial
    └──stage1
       ├── main
       │   ├── BUILD
       │   └── hello-world.cc
       └── WORKSPACE

Exécutez la commande suivante pour passer au répertoire cpp-tutorial/stage1:

cd cpp-tutorial/stage1

Exécutez la commande suivante :

bazel build //main:hello-world

Dans l'étiquette cible, la partie //main: correspond à l'emplacement du fichier BUILD par rapport à la racine de l'espace de travail, et hello-world au nom de la cible dans le fichier BUILD.

Bazel produit un résultat semblable à celui-ci:

INFO: Found 1 target...
Target //main:hello-world up-to-date:
  bazel-bin/main/hello-world
INFO: Elapsed time: 2.267s, Critical Path: 0.25s

Vous venez de créer votre première cible Bazel. Bazel place les sorties de compilation dans le répertoire bazel-bin à la racine de l'espace de travail.

Testez maintenant votre binaire récemment créé, à savoir:

bazel-bin/main/hello-world

Le message"Hello world"s'affiche.

Voici le graphique de dépendances de l'étape 1:

Le graphique de dépendance de hello-world affiche une seule cible avec un seul fichier source.

Résumé: étape 1

Maintenant que vous avez terminé votre première compilation, vous avez une idée générale de la structure d'une compilation. À l'étape suivante, vous allez ajouter de la complexité en ajoutant une autre cible.

Étape 2: cibles de compilation multiples

Bien qu'une seule cible soit suffisante pour les petits projets, vous pouvez diviser les projets plus importants en plusieurs cibles et packages. Cela permet d'accélérer les compilations, c'est-à-dire que Bazel ne recompile que les éléments modifiés, et accélère les compilations en créant plusieurs parties d'un projet à la fois. Cette étape du tutoriel permet d'ajouter une cible, et la suivante d'ajouter un package.

Voici le répertoire que vous utilisez pour l'étape 2:

    ├──stage2
    │  ├── main
    │  │   ├── BUILD
    │  │   ├── hello-world.cc
    │  │   ├── hello-greet.cc
    │  │   └── hello-greet.h
    │  └── WORKSPACE

Examinez le fichier BUILD dans le répertoire cpp-tutorial/stage2/main:

cc_library(
    name = "hello-greet",
    srcs = ["hello-greet.cc"],
    hdrs = ["hello-greet.h"],
)

cc_binary(
    name = "hello-world",
    srcs = ["hello-world.cc"],
    deps = [
        ":hello-greet",
    ],
)

Avec ce fichier BUILD, Bazel compile d'abord la bibliothèque hello-greet (à l'aide du fichier cc_library rule intégré de Bazel), puis le binaire hello-world. L'attribut deps de la cible hello-world indique à Bazel que la bibliothèque hello-greet est requise pour créer le binaire hello-world.

Avant de pouvoir créer cette nouvelle version du projet, vous devez changer de répertoire en passant au répertoire cpp-tutorial/stage2 en exécutant la commande suivante:

cd ../stage2

Vous pouvez maintenant créer le binaire à l'aide de la commande suivante:

bazel build //main:hello-world

Là encore, Bazel produit un résultat semblable à celui-ci:

INFO: Found 1 target...
Target //main:hello-world up-to-date:
  bazel-bin/main/hello-world
INFO: Elapsed time: 2.399s, Critical Path: 0.30s

Vous pouvez maintenant tester votre binaire récemment créé, qui renvoie un autre "Hello world" :

bazel-bin/main/hello-world

Si vous modifiez maintenant hello-greet.cc et recréez le projet, Bazel ne recompile que ce fichier.

En observant le graphique des dépendances, vous pouvez constater que hello-world dépend des mêmes entrées qu'auparavant, mais la structure du build est différente:

Le graphique de dépendance de "hello-world" affiche les modifications de structure après la modification du fichier.

Résumé: étape 2

Vous avez maintenant créé le projet avec deux cibles. La cible hello-world crée un fichier source et dépend d'une autre cible (//main:hello-greet), qui génère deux fichiers sources supplémentaires. Dans la section suivante, passez à l'étape suivante et ajoutez un autre package.

Étape 3: Plusieurs packages

L'étape suivante ajoute une couche de complication et crée un projet avec plusieurs packages. Examinez ci-dessous la structure et le contenu du répertoire cpp-tutorial/stage3:

└──stage3
   ├── main
   │   ├── BUILD
   │   ├── hello-world.cc
   │   ├── hello-greet.cc
   │   └── hello-greet.h
   ├── lib
   │   ├── BUILD
   │   ├── hello-time.cc
   │   └── hello-time.h
   └── WORKSPACE

Vous pouvez constater qu'il existe désormais deux sous-répertoires, contenant chacun un fichier BUILD. Par conséquent, pour Bazel, l'espace de travail contient désormais deux packages: lib et main.

Examinez le fichier lib/BUILD:

cc_library(
    name = "hello-time",
    srcs = ["hello-time.cc"],
    hdrs = ["hello-time.h"],
    visibility = ["//main:__pkg__"],
)

Et dans le fichier main/BUILD:

cc_library(
    name = "hello-greet",
    srcs = ["hello-greet.cc"],
    hdrs = ["hello-greet.h"],
)

cc_binary(
    name = "hello-world",
    srcs = ["hello-world.cc"],
    deps = [
        ":hello-greet",
        "//lib:hello-time",
    ],
)

La cible hello-world du package principal dépend de la cible hello-time du package lib (d'où l'étiquette cible //lib:hello-time). Cette information est fournie par Bazel via l'attribut deps. Vous pouvez le constater dans le graphique des dépendances:

Le graphique de dépendance pour "hello-world" montre comment la cible du package principal dépend de la cible du package "lib".

Pour que la compilation aboutisse, vous rendez explicitement la cible //lib:hello-time dans lib/BUILD visible par les cibles de main/BUILD à l'aide de l'attribut de visibilité. En effet, par défaut, les cibles ne sont visibles que par les autres cibles du même fichier BUILD. Bazel utilise la visibilité cible pour éviter les problèmes tels que les bibliothèques contenant des détails de mise en œuvre qui peuvent être divulguées via des API publiques.

Maintenant, compilez cette version finale du projet. Passez au répertoire cpp-tutorial/stage3 en exécutant la commande suivante:

cd  ../stage3

Exécutez à nouveau la commande suivante:

bazel build //main:hello-world

Bazel produit un résultat semblable à celui-ci:

INFO: Found 1 target...
Target //main:hello-world up-to-date:
  bazel-bin/main/hello-world
INFO: Elapsed time: 0.167s, Critical Path: 0.00s

Testez maintenant le dernier binaire de ce tutoriel pour obtenir le dernier message Hello world:

bazel-bin/main/hello-world

Résumé: étape 3

Vous avez maintenant créé le projet sous la forme de deux packages avec trois cibles et vous comprenez les dépendances entre elles, ce qui vous permet de vous lancer et de construire de futurs projets avec Bazel. Dans la section suivante, découvrez comment poursuivre votre parcours Bazel.

Étapes suivantes

Vous avez terminé votre première compilation de base avec Bazel, mais ce n'est qu'un début. Voici d'autres ressources pour continuer à vous former avec Bazel:

Bon bâtiment !