mardi 17 décembre 2013

Premake


Plan :

I- Introduction
II- C'est quoi Premake?
II- Comment ça marche?
IV- Télécharger Premake
V- Utiliser Premake
VI- Notions
VII- Cas pratique


I- Introduction :

Imaginez-vous un chef de projet d’un projet C++ Open Source, les développeurs qui ont accepté de contribuer au projet sont :

Amine un fervent utilisateur Linux, il programme à la old-school sans IDE, son éditeur de texte préféré est Emacs, il écrit ses propres fichiers Makefile, il compile et link ses projes manuellement, il adore  voir les lignes de log de la commande Make se défilent dans le shell décrivant les rouages de compilation de ses projets.

Ali préfère utiliser un IDE, selon lui l’IDE fait gagner beaucoup de temps, lui facilite le déboggage de ses programmes, la synchronisation avec les projets distants, etc. son IDE préféré est Visual Studio.

Zakaria un fan des Mac pro, il utilise Code::blocks de temps en temps, ça lui permet de travailler sur des projets Open Source vu qu’il est multi-platform, mais il préfére XCode.

Quelle sera votre environnement de développement du projet ?
allez-vous imposer un IDE aux programmeurs?

Premake vous offre la meilleure solution, laisser chaque développeur travailler avec son IDE préféré sans perdre le temps de configurer manuellement le projet pour chacun de leur IDE.

II - C'est quoi Premake :

Premake est un outil de génération de configuration projet, crée par Jason Perkins. Grâce à l’outil premake le projet est géré indépendemment sans se soucier de son environnement de développement, tout en supportant en même temps différents IDEs, Premake génère automatiquement une configuration du projet spécifique à un IDE donné.

Les IDEs supportés par Premake sont : 
  • Microsoft Visual Studio 2002-2010, y compris les éditions Express.
  • GNU Make, y compris Cygwin et MinGW
  • Apple XCode
  • Code::Blocks
  • CodeLite
  • MonoDevelop
  • SharpDevelop

III - Comment ça marche ?

Premake se base sur un fichier script Lua, qui est  le script de base qui décrit la configuration du projet, pour générer les configurations spécifiques à des environnements de développement spécifiques.

Figure 1 : Génération de configuration projet avec Premake

IV- Télécharger Premake :


L’outil Premake est très léger, vous pouvez le télécharger à partir du site officiel : http://industriousone.com/premake/download

V- Utiliser Premake:


Pour utiliser Premake il faut exécuter la commande “premake4” dans la ligne de commande. 

Syntaxe : premake4     action
action est un alias de l’environnement de développement spécifique pour lequel vous voulez générer le projet.

vs2010
Visual Studio 2010 (ou Express)
vs2008
Visual Studio 2008 (ou Express)
vs2005
Visual Studio 2005 (ou Express), SharpDevelop, ou MonoDevelop
vs2003
Visual Studio 2003
vs2002
Visual Studio 2002
gmake
GNU Make (y compris Cygwin et MinGW)
xcode3
Apple Xcode 3
codeblocks
Code::Blocks
codelite
CodeLite
Exemple : premake4  vs2008 

cela va générer une solution visual studio version 2008 du projet.
la commande premake4 cherche dans le dossier courant, ou la commande est exécutée, le fichier premake4.lua qui contient la configuration du projet. Si le fichier est nommé autrement il faut le mentionner à la commande :

Commande :

premake4  --file=maConfig.lua   action


Bonne pratique :

Ecrire un fichier .bat(pour les utilisateurs de windows) ou .sh (pour les utilisateurs de Linux) spécifique à l’IDE que vous utilisez, par exemple : premake_vs2008.bat(ou .sh) qui génère automatiquement en un seul click de souris le projet.pour en savoir plus sur la commande premake4, tapez : 


Commande : premake4  --help
  
ou consulter le site officiel : http://industriousone.com/

VI- Notions : 

1- Solution:


Cette notation est inspirée de la suite Visual studio de Microsoft, dans la plupart des IDE elle est connue sous le nom de workspace, dans le cadre de premake4, solution n’est pas seulement un conteneur de projets, mais rassemble aussi toutes les proprétés communes entre les projets de la même solution.

Exemple :
solution “test”
configuration { “debug”, “release”}

Par défaut, premake4 place les fichiers de la solution et ses projets dans le même dossier que le script premake, pour pouvoir les placer dans un autre emplacement on utilise la fonction location

Exemple :
solution “test”
configuration { “debug”, “release”}
location “build”

Figure 2 : arborescence

2- Project :


Le projet est un ensemble de paramètres et de fichiers sources pour faire un programme.
Un projet a plusieurs paramètres:

2-1- Kind:


désigne le type de fichier de sortie du projet, est ce que c’est fichier exécutable ou une librairie, …
les  4 types de projet premake sont :
ConsoleApp : application console
WindowApp : pour les applications desktop
SharedLib : librairie dynamique (DLL sous windows, so pour linux)

StaticLib : librairie statique.


2-2- files :


les chemins des fichiers sources du projet, on peut utiliser des jokers comme ‘*’ pour désigner tous les fichiers d’un dossier, ‘**’ pour les fichiers d’un dossier et de ses sous-dossiers.

2-3- language :


le langage de programmation utilisé, les 3 langages de programmation supportés par premake sont : C, C++, C# .

2-4- links :


est l’ensemble de librairies ou des projets dont dépend le projet. Ne spécifier pas l’extension de la librairie, parce que premake la déduit automatiquement en se basant sur la plate-forme, sauf pour mac.
par exemple pour linker la librairie SDL avec le projet :

Exemple :
links { “SDL” }

si la plate-forme est win32 premake déduit alors qu’il faut chercher le fichier SDL.dll, si c’est le cas d’uneplate-forme linux : SDL.so .
pour mac :
links { ”cocoa.framework” }

Exemple :
solution “test”
configuration { “debug”, “release”}
location “build”

project “projetTest”
kind “ConsoleApp”
language “C++”
files { “src/projetTest/*.h”, “src/projetTest/*.cpp” }


3- Configuration:


la fonction configuration est une sorte de filtre, c’est un ensemble de paramètres qui seront appliqués au build, y compris les flags de build, les fichiers headers, les chemins de librairies lié à un projet ou à une solution.
Les configurations les plus utilisées dans la plupart des IDEs sont : Debug et Release, chaque configuration a ses propres paramètres par exemple dans la configuration Debug la directive (define) “_Debug” ou "Debug" est activée ce qui permet la collecte des informations concernant le debug de l’app à l”encontre de la configuration Release ou la directive “_Debug” est désactivée.

Utiliser les configurations :

Premake offre une grande flexibilité lorsqu’il s’agit de la configuration du build, vous pouvez définir la portée du paramétrage que vous spécifier : est ce que c’est pour toute la solution, ou bien pour certains projets de la solution, ou pour une combinaison de configurations,... cela dépendra de vos besoins.
On peut définir par exemple un symbole afin qu’il soit utilisé dans toute la solution pour toutes les configurations,

Exemple :
solution “mySol”
defines { “SYMBOLE”}
configurations { “Debug”, “Release” }

ou bien définir le même symbole mais seulement  dans la configuration Debug,
Exemple :
solution “mySol”
configurations { “Debug”, “Release” }

configuration “Debug”
defines { “SYMBOLE”}

N.B: pour faire ceci il faut que la configuration “Debug” soit au préalable définie dans la solution.

Exemple :
project “Mon_Proj”
defines {“MON_PROJ_SYMB”}
configuration “Debug”
defines {“MON_PROJ_DEBUG_SYMB”}

Dans ce cas le symbole “MON_PROJ_SYMB” est définie pour le projet “Mon_Proj” pour toutes les configurations spécifiées à ce projet, par contre le symbole “MON_PROJ_DEBUG_SYMB” est seulement définie pour la configuration Debug dudit projet.

Dans les exemples précédents je n’ai utilisé que le paramètre “defines” qui spécifie les symboles définies pour le build d’une configuration données, mais il y a pas mal de paramètres que vous pouvez définir dans le block configuration, consulter la documentation officielle de la fonction “configuration” de la commande premake. consulter : http://industriousone.com/configuration .

3-1- targetsuffix :


Si vous remarquez bien ce que le résultat de build produit comme fichiers de sorties pour certains IDEs le nom de l’exécutable et les fichiers qui vont avec sont parfois suffixés par “_r” ou “_d” selon la configuration utilisée : respectivement Release ou Debug, ceci pour ne pas écraser les fichiers de sortie de chacune des configurations.
grâce au paramètre targetprefix vous pouvez spécifier le suffixe de vos configurations, exemple :

Exemple :
configuration “Debug”
defines { “MY_DEBUG”}
targetsuffix“_d”

configuration “Release”
targetsuffix “_r”


3-2- targetname:


par défaut les IDEs nomment le fichier exécutable de chaque projet par le nom du projet, si vous désirez changer ce comportement par défaut c’est grâce à ce paramètre.

VII- Cas pratique :

Dans cette partie de l'article je vais mettre en pratique ce qui est cité dessus, c'est un simple projet C++

la photo dessous est l'arborescence de mon projet premake_test:
figure 3 : arborescence du projet

brievement la description de chaque dossier:

src : il contient les fichiers source (.cpp et .h) du projet.
premake : contient le fichier lua de la configuration du projet + fichiers .bat et .sh pour automatiser la génération du projet.
libs : contient les librairies (.lib pour les librairies windows VS , .so pour unix) utilisées dans le projet, dans le cadre de ce projet : SDL.
dll : spécifique à la plate-forme windows, contient les librairie dynamiques de windows.
core : contient l'exécutable de la commande premake, en effet 2 exécutables pour windows et linux. 
bin : lors de la compilation du projet tous les fichiers générés vont être placés dans ce dossier.
resources : des fichiers data, dans cet exemple il contient une image .bmp .

commençons par la configuration du projet :
Code : solution "solTest" configurations { "debug", "release" } language "C++" includedirs { "../src", "../libs/includes" } location "solution" libdirs { "../libs" }
comme je l'avais dit au debut de l'article dans le jargon de premake une "solution"  n'est pas seulement un workspace, mais de plus rassemble les paramètres communs entre les projets de la solution, dans ce cas : 
1) le langage de programmation C++, au lieu de le configurer pour chaque projet de la solution.
2) Les dossiers des fichiers entêtes(headers files), c'est très pratique lors du codage pour inclure un fichier header on n'est pas obligé de mentionner le chemin complet mais seulement le chemin à partir du dossier "src" du projet.
3) Le chemin indiquant l'emplacement des librairies des projets.
4) et enfin les configurations.

Code : configuration "debug" defines { "MY_DEBUG" } flags { "Symbols" } targetdir "../bin/debug" objdir ("../bin/debug") targetsuffix "_d" configuration "release" targetdir "../bin/release" targetsuffix "_r" objdir ("../bin/release")
Quant aux configurations des projets, je me contente des deux configurations les plus utilisées : debug et release. Pour chaque configuration je crée un dossier, comme ça lors de la compilation du projet les fichiers générés (l'exécutable, les fichiers objets .o) sont placés dans le dossier de la configueation adéquate dans le dossier bin, en dépit qu'ils(fichiers générés) soient suffixés "_r" ou "_d" mais pour ne pas y avoir plusieurs fichiers dans le dossier bin, c'est plus clean. Une petite particularité quant à la configuration "debug" c'est les defines, seulement en phase debug le code compris entre les directives #ifdef MY_DEBUG et #endif sera exécuté, 
Code : #include #include"SDL/SDL.h" 

 int main(int argc, char** argv) { 

 #ifdef MY_DEBUG
    int numdrivers = SDL_GetNumVideoDrivers(); 
     int working = 0;
     const char* drivername;
     for (int i = 0; i < numdrivers; ++i) 
    {
               drivername = SDL_GetVideoDriver(i); 
               if (SDL_VideoInit(drivername) == 0) 
              { 
                      SDL_VideoQuit();
                     ++working;
                      printf("Driver %s works.\n", drivername);
              } 
             else 
            {
                    printf("Driver %s doesn't work.\n", drivername);
             }
       }
        printf("\n%d video drivers total (%d work)\n", numdrivers, working);
 #endif

        if (SDL_Init(SDL_INIT_EVERYTHING) != 0)
       { 
               std::cout << "SDL_Init Error: " << SDL_GetError() << std::endl; return 1;
       }

ce code (compris entre les directives #ifdef et #endif) n'est généralement pas une fonctionnalité de l'application mais fait pour suivre de proche l'exécution de l'application, par exemple : l'affichage du FPS (Frame Per Second) d'un jeu en temps réel.
dans cet exemple le code debug compte le nombre de drivers graphiques de la machine, puis liste les drivers actifs.
Continuons avec la fonction project du framework Premake : 
Code :
 project "projTest"
  kind "ConsoleApp"
  files { "../src/*.h", "../src/*.cpp" }
  links { "SDL2" , "SDL2main" }
Le projet est un simple programme C++ de type console, les fichiers sont localisés dans le dossier src, vous pouvez remarquer que includedirs de solution et files du projet pointent vers le même dossier, pourquoi donc? Dans un grand projet, la solution contient plusieurs projets, chaque projet a son propre dossier dans le dossier src, alors files pointera dans ce cas sur le dossier du projet adéquat. et enfin les dépendances du projet, le link du projet avec la librairie SDL.


Bonne pratique :

Automatiser la génération de la solution est parmi les bonnes pratiques du développement, pour chaque IDE utilisé au projet développer un script (.bat ou .sh) pour faciliter la tâche au développeur.
Dans cet exemple j'ai fait 2 script : un .bat pour Visual Studio 2008 et un .sh pour GNU Make sous Linux.

Le fichier gmake_premake.sh :


Code : #!/bin/sh projectRoot=/home/itachi/DEV/Premake/Premake_test $projectRoot/core/premake4 --file=$projectRoot/Premake/maConfig.lua gmake
Le fichier premake_VS2008.bat :
Code : ..\core\premake4.exe --file=maConfig.lua vs2008 pause
pour les utilisateurs windows copier les DLLs du dossier dll dans le dossier de la configuration debug ou release du dossier bin.
clicker sur le fichier convenant à votre plate-forme, la solution sera générée dans le dossier solution du dossier Premake.
pour télécharger l'exemple voici le lien du projet : https://github.com/hlYassine/premake_test

Aucun commentaire:

Enregistrer un commentaire