Qu’est-ce que je vais développer pour apprendre le Rust ?

 

Comme je l’écrivais dans mon précédent article : Rust, à l’assaut d’un nouveau langage de programmation !, je me suis motivé à enfin débuter à apprendre sérieusement le Rust.

Une des manières que je trouve la plus efficace pour apprendre un langage de programmation, est de développer un projet – bien entendu, sa taille peut varier, donc mieux vaut commencer petit.

Et surtout, de le terminer ! Ce qui, je vous l’accorde, est souvent difficile. Soit par contrainte de temps, de motivation…

Et comme je n’avais pas spécialement d’idée qui me venait à l’esprit, j’ai décidé de reprendre l’outils en ligne de commande (CLI) bscxx que j’ai développé il y a quelques mois, afin de simplement le réécrire en Rust.

Et c’est cet outils que je vais vous présenter dans cet article. Ne vous en faites pas, les « vrais » articles sur le développement en Rust arriveront dans la foulée.

C’est d’ailleurs une occasion géniale – de redévelopper un outils existant – pour voir la complexité de ce qu’on peut faire en Rust, en un temps donné, par rapport au C++ (le langage dans lequel a été codé bscxx). Et c’est d’autant plus intéressant quand on sait que le Rust tend justement à remplacer le C/C++ dans la catégorie des langages systèmes.

Bien entendu cela dit, je ne prendrai pas en compte le temps passé à réfléchir aux solutions que j’ai mis en place sur l’outils initial, le temps passé dans la documentation de CMake… mais je pourrai cela dit m’apercevoir du nombre de bugs rencontrés lors du développement par exemple (et encore que… comme je débute, il y a forcément des bugs dus à des erreurs de néophyte).

Bref, bref, bref… C’est bien tout ça, mais si on commençait les explications, non ?

 

Ton outils là, bscxx, qu’est-ce que c’est ?

Et surtout, à quoi ça sert ?

 

Bscxx (pour bootstrap C++… oui, il ne faut pas chercher bien loin…) est donc, à la base, un outils utilisable en ligne de commande (qu’on appelle communément CLI pour raccourcir, qui signifie tout simplement command line tools). C’est à dire qu’on s’en sert en tapant des commandes via un terminal classique comme l’invite de commandes windows ou le powershell par exemple.

Pour faire une comparaison rapide, ça s’utilise comme les commandes Unix telles que ls (permettant de lister les fichiers et dossiers), clear, mkdir (création d’un dossier), ou rm (suppression de fichier(s) / dossier(s))…

Bscxx permet donc d’automatiser entièrement la création d’un projet C++. Du moins c’était le concept de base.

Dorénavant, il fait un peu mieux que ça, puisqu’il peut s’apparenter à un package manager pour le C++, créé au dessus de l’utilitaire de build CMake – ce dernier sert à générer des fichiers permettant aux systèmes de build de compiler le projet, tels que Ninja, ou encore MSBuild (le système de build de visual studio), ou tout simplement Make.

Voyez tout simplement ça comme une sorte de NPM très simple pour le C++ (si vous avez déjà codé en JS pour node.js). Ou… de cargo pour le Rust.

Si vous connaissez déjà bien l’univers du C++, vous connaîtrez probablement d’autres packages managers connus, tels que Conan, ou Buck par exemple; et qui ont la même fonction. Et vous vous demandez alors peut-être pourquoi est-ce que j’ai décidé de refaire ce qui existe déjà ?

 

Et vous avez raison de vous poser cette question !

 

Tout simplement car je trouve que Conan est trop complexe à utiliser, et que Buck – qui lui est plutôt simple -, n’est pas assez malléable. Si je souhaite utiliser Ninja en tant que système de build par exemple, je ne peux tout simplement pas le faire via Buck.

Et puis c’était aussi l’occasion de faire un projet cool et amusant sur mon temps libre ! Alors pourquoi se priver ?

Juste pour donner une idée, il suffit de taper cette commande, pour automatiquement générer un projet C++ nommé « new_project » compilable (avec l’affichage d’un hello world par défaut) grâce à CMake. Cela initialise de plus un répertoire git, bref, tout est prêt !

bscxx create new_project

 

Et ce projet peut ensuite être utilisé comme module dans un autre projet, en utilisant la commande qui suit (par exemple, si on a push le projet sur github) :

bscxx add --github nom_utilisateur_github/nom_du_projet

 

Et sans aucun autre effort de notre part, on peut ensuite l’utiliser – il est maintenant devenu un packet – dans notre tout nouveau projet.

 

Okay, c’est bien joli tout ça, mais concrètement, ça donne quoi ?

 

D’accord, laissez moi vous faire une présentation un peu plus concrète de l’outils alors.

Mettons que nous souhaitons créer un packet qui a pour seul but d’écrire le classique Hello World !, que nous voulons utiliser dans n’importe quel autre projet.

Je commence donc tout d’abord par aller dans l’emplacement sur mon disque dur où je souhaite placer ce projet (dans mon cas, par exemple, dans D:\Projects\), puis je créé un nouveau dossier hello_world qui contiendra ce packet, à l’aide de la commande mkdir.

Ensuite, je me place à l’intérieur de ce dossier avec la commande cd, jusque là, rien d’exceptionnel.

mkdir hello_world
cd hello_world

 

Maintenant, je peux créer mon nouveau projet C++ à l’aide de bscxx.

bscxx create hello_world

 

Si je fait un ls ensuite, afin de lister les fichiers et dossiers du dossier courant hello_world, on peut voir que la base du projet C++ est générée, et qu’un repository git est initialisé.

ls
CMakeLists.txt  bscxx_modules/  dependencies.bscxx  include/  src/  test/ .git

 

Et je peux d’ailleurs déjà commencer à compiler ce projet à l’aide de CMake, en créant un dossier build (vous pouvez l’appeler comme vous le souhaitez, j’ai pour habitude de l’appeler build), en lançant la commande de génération, puis de compilation de CMake.

Dans mon cas, j’utiliserai le système de build Ninja (soit dit en passant, c’est un des système de build les plus rapides pour compiler votre code C/C++, je vous le recommande vivement pour développer).

D:\Projects\hello_world\(master -> origin)
mkdir build // création du dossier où vont être placés les fichiers de génération et de compilation du projet C++

D:\Projects\hello_world\(master -> origin)
cd build // on rentre dans le dossier build

D:\Projects\hello_world\build (master -> origin)
cmake .. -G Ninja // on utilise CMake pour générer les fichiers Ninja permettant de compiler le projet C++
-- The C compiler identification is GNU 7.3.0
-- The CXX compiler identification is GNU 7.3.0
-- Check for working C compiler: C:/tools/mingw64/bin/gcc.exe
-- Check for working C compiler: C:/tools/mingw64/bin/gcc.exe -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working CXX compiler: C:/tools/mingw64/bin/c++.exe
-- Check for working CXX compiler: C:/tools/mingw64/bin/c++.exe -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: D:/Projects/hello_world/build // la génération des fichiers Ninja s'est bien terminée correctement

D:\Projects\hello_world\build (master -> origin)
cmake --build . --config Release // on compile ensuite le projet dans une configuration de Release
[4/4] Linking CXX executable src\bin\hello_world.exe // le projet est bien compilé

 

Et voilà !

Si vous avez l’habitude de travailler avec CMake, cela devrait vous sembler intuitif.

Bref, dans tous les cas, mon projet C++ de base, généré par bscxx est bien compilé, et je peux le lancer avec la commande suivante :

D:\Projects\hello_world\build (master -> origin) 
"src/bin/hello_world.exe"
Running the app...

 

J’obtiens donc le résultat Running the app… puisque bscxx génère automatiquement le squelette de base et un bout de code pour chaque projet créé, afin de pouvoir commencer à programmer directement et rapidement.

On peut voir d’ailleurs ce code généré dans src/main.cc, le fichier C++ contenant la fonction main, qui est au passage l’entrée du programme.

#include <iostream>

int main(int argc, char** argv){
    std::cout << "Running the app..." << std::endl;
    return 0;
}

 

Ça n’explique toujours pas comment utiliser ton outils comme package manager, là, ton histoire !

 

Hmm, c’est pas faux ! Comme aurait sans doute répliqué notre bon seigneur Perceval !

 

En fait, comme je l’écrivais ci-dessus, tous les projets créés avec bscxx peuvent être à leur tour inclus dans un autre projet, et donc devenir des packets.

Pour continuer sur notre exemple, créons une méthode HelloWorld qui prend en paramètre une chaîne de caractère à afficher. Pour cela, je créé un fichier hello_world.h que je place dans le dossier include du projet.

Puis, je l’édite avec mon éditeur de code préféré (pour moi ça sera visual studio code), pour créer la fameuse fonction HelloWorld :

// Ce code est situé ici : include/hello_world.h

#include <iostream>

void HelloWorld(const std::string &text){
    std::cout << text << std::endl;
}

 

Maintenant, je rajoute simplement ce fichier header dans le fichier main.cc (celui dont j’ai parlé plus haut, qui est généré par bscxx), présent dans le dossier src, et j’appelle la fonction HelloWorld :

#include <iostream>
#include "hello_world.h"

int main(int argc, char** argv){
    std::cout << "Running the app..." << std::endl;
    HelloWorld("test");
    return 0;
}

 

Et on répète les étapes précédentes avec les commandes CMake (ici, il est nécessaire de régénérer les fichiers Ninja car j’ai ajouté un fichier au projet) :

D:\Projects\hello_world\build (master -> origin) // On veille bien à se replacer dans le dossier "build" si ce n'est pas déjà le cas
cmake .. -G Ninja // On regénère les fichiers Ninja
-- Configuring done
-- Generating done
-- Build files have been written to: D:/Projects/hello_world/build

D:\Projects\hello_world\build (master -> origin)
cmake --build . --config Release // On recompile le projet
[2/2] Linking CXX executable src\bin\hello_world.exe

D:\Projects\hello_world\build (master -> origin)
"src/bin/hello_world.exe" // Et on lance le programme pour vérifier que notre ajout de code a bien fonctionné
Running the app...
test // Tout fonctionne, impeccable !

 

Maintenant, je peux donc créer un nouveau projet, nommé simplement hello_world_2, dans un autre dossier, et utiliser la méthode HelloWorld du premier projet (hellow_world), qui deviendra donc un packet du nouveau projet (ou une dépendance).

Pour ce faire, je commence par créer le nouveau dossier hello_world_2 dans D:\Projects, et je réitère les premières étapes :

D:\Projects              
mkdir hello_world_2    
                         
D:\Projects              
cd hello_world_2       
                         
D:\Projects\hello_world_2
bscxx create hello_world_2

D:\Projects\hello_world_2 (master -> origin)

 

Je peux maintenant ajouter en dépendance de ce nouveau projet le projet précédent grâce à la commande add :

D:\Projects\hello_world_2 (master -> origin)
bscxx add --local ../hello_world
Adding hello_world module...
Module correclty added, project updated.

 

A partir de cette étape, ce qu’il faut noter, c’est que le dossier bscxx_modules contient dorénavant le projet initial hello_world.

D:\Projects\hello_world_2\bscxx_modules (master -> origin)// je me suis placé dans le dossier bscxx_modules
ls // et avec la commande ls, on peut voir le projet initial
hello_world/

 

Et en plus de ça, le fichier dependencies.bscxx s’est mis à jour.

BSCXX_PROJECT:
  [hello_world2]:^1.0.0	|	

BSCXX_DEPENDENCIES:
  [hello_world]:^1.0.0	|	../hello_world

 

Si je lance à ce moment la commande bscxx tree, je n’ai pas encore de résultat en terme de packets dont dépend le projet. Normal, puisque je n’utilise pas encore à proprement parlé le packet dans le code du nouveau projet :

D:\Projects\hello_world_2 (master -> origin)
bscxx tree

hello_world_2 // le nom du projet 
// On remarque qu'il n'y a rien de plus, donc pour l'instant, ce projet ne dépend d'aucun packet

 

Maintenant, je vais modifier le fichier main.cc de ce projet, comme tout à l’heure, mais en utilisant cette fois la fonction HelloWorld du packet hello_world, créé précédemment :

#include <iostream>
// j'inclu le fichier voulu, en spécifiant juste le packet dans lequel il se trouve 
#include <hello_world/hello_world.h> 

int main(int argc, char** argv){
    std::cout << "Running the app..." << std::endl;
    HelloWorld("test"); // et je peux maintenant appeler la méthode HelloWorld du projet précédent
    return 0;
}

 

Et si je relance à cette étape la commande bscxx tree, pour voir les dépendances du projet, cette fois, j’ai bien le packet hello_world qui s’affiche :

D:\Projects\hello_world_2 (master -> origin)
bscxx tree

hello_world_2 // le nom du projet
   |--hello_world // un des packets (pour l'instant l'unique) dont dépend ce projet

 

Petit aparté,  peut-on inclure des packets qui dépendent eux-même d’autres packets…

 

Bien sûr ! Et c’est d’ailleurs là toute la force des packages managers, et du découpage des projets en packets !

La modularité, c’est la vie !

Bref, pour tester que l’appelle à la fonction HelloWorld fonctionne bien, rien de plus simple. Je réitère simplement les étapes précédentes pour générer et compiler le projet C++ :

D:\Projects\hello_world_2 (master -> origin) // je créé le dossier build
mkdir build

D:\Projects\hello_world_2 (master -> origin) // je me place dedans
cd build

D:\Projects\hello_world_2\build (master -> origin)
cmake .. -G Ninja // je relance la commande pour générer les fichiers Ninja
-- The C compiler identification is GNU 7.3.0
-- The CXX compiler identification is GNU 7.3.0
-- Check for working C compiler: C:/tools/mingw64/bin/gcc.exe
-- Check for working C compiler: C:/tools/mingw64/bin/gcc.exe -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working CXX compiler: C:/tools/mingw64/bin/c++.exe
-- Check for working CXX compiler: C:/tools/mingw64/bin/c++.exe -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: D:/Projects/hello_world_2/build

D:\Projects\hello_world_2\build (master -> origin)
cmake --build . --config Release // et je compile
[3/3] Linking CXX executable src\bin\hello_world_2.exe // aucune erreur, tout semble correct !

 

La compilation s’est bien déroulée, il n’y a plus qu’à lancer le programme pour en être sûr :

D:\Projects\hello_world_2\build (master -> origin)
"./src/bin/hello_world_2.exe"
Running the app...
test // La fonction HelloWorld est bien appelée !

 

Et voilà ! Plutôt pratique non ?

Je sais que dans cet article, au final, j’aurais plus parlé de C++ que de Rust, mais je voulais vous présenter grosso-modo les fonctionnalités de l’outils, et surtout, à quoi il sert concrètement (il y a quelques fonctionnalités en plus, comme par exemple la génération du squelette pour les tests fonctionnels, mais cela ne me semblait pas très intéressant de surcharger cet article avec).

Mon objectif, en Rust, sera donc de refaire bscxx. Mais comme c’est dommage de refaire quelque chose qui existe, je ferai cette fois-ci une version pour le C, qui s’appellera donc tout simplement… bsc !

Le C++ et le C étant extrêmement proches, ça ne changera pas grand chose au fonctionnement de l’outils.

 

 

Au final, voilà donc la liste des choses que je vais devoir mettre en pratique, et qui devraient me permettre d’avoir des connaissances pratiques en Rust sur l’utilisation de base du langage :

  • Créer la base d’un programme CLI avec passage d’arguments via des commandes
  • Lire et écrire dans des fichiers
  • Lister les fichiers et dossiers présents dans un dossier spécifié
  • Utilisation de concepts classiques comme la récursivité, parcourir un arbre…
  • Utilisation des threads
  • Et… sûrement d’autres choses, mais je ne vois pas encore trop quoi 😉

Bref, merci d’avoir lu tout ce pavé jusqu’au bout, j’espère que ça vous intéresse.

La suite promet d’être vraiment chouette en tout cas, et je suis assez excité à l’idée de m’y mettre vraiment !

Et pour finir, ne vous en faites pas, le prochain article de la série sera entièrement consacré à Rust cette fois (si vous suivez cette série uniquement pour ça) !

 

 

Merci d’avoir pris le temps de lire cet article. Est-ce que cela vous a intéressé ? Est-ce que vous avez appris quelque chose ? N’hésitez pas à me partager votre opinion sur le sujet via les commentaires ci-dessous cet article.

Et si vous aimez ce contenu, vous pouvez vous abonnez à ma newsletter (si le coeur vous en dit), afin de rester informé des dernières nouveautés du blog, mais également pour recevoir (pas plus d’une fois par semaine), d’autres contenus que je juge intéressant sur tout ce qui touche la programmation ou le développement personnel.

Inscription à la newsletter :

[newsletter]

 

Victor Gallet

Victor Gallet

Étudiant programmeur jeu vidéo. J'aime par dessus tout apprendre, et je suis un éternel curieux de tout. Mon principal but dans la vie est d’être une meilleure personne, et de partager mes (faibles) connaissances avec les autres.

Leave a Reply

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.