Les commentaires multilignes n'existent pas. Donc à moins d'avoir un éditeur avancé qui sache gérer la mise (et retrait) en commentaire ligne par ligne (comme kate ou kwrite - Ctrl + d/Ctrl + shift + d), il suffit simplement d'entourer la partie à désactiver d'une condition toujours fausse :
if(0) # mon bloc de code à désactiver temporairement endif()
Pour réactiver temporairement cette portion de code, changer la valeur du if de 0 en 1. Et, bien sûr, retirer le if/endif quand il n'est plus nécessaire.
CMake 3.0 a, depuis, introduit les blocs de commentaire. Exemple :
#[[ # mon bloc de code à désactiver temporairement ]]
Avec if(DEFINED nom_variable) :
if(DEFINED x) message("x est définie") else(DEFINED x) message("x n'est pas définie") endif(DEFINED x)
L'opérateur NOT pourrait être envisageable mais considère également le contenu : outre l’inexistence, toute valeur fausse (0, vide, FALSE, OFF, etc) rendrait la condition vraie.
Une telle structure de données n'existe pas en CMake. À la place, on recourt généralement aux variables dynamiques de façon à créer une variable telle que <préfixe>_<clé> = <valeur> à l'usuel <nom>[<clé>] = <valeur>. Exemple :
set(cle "abc") set(hash_${cle} "def") message("hash_${cle} = ${hash_${cle}}")
Il existe cependant une exception : ENV.
En utilisant une liste, puis à la fin des opérations d'ajout utiliser l'option REMOVE_DUPLICATES de list. Exemple :
set(LISTE ) list(APPEND LISTE "a") list(APPEND LISTE "b") list(APPEND LISTE "a") message("[AVANT] LISTE = ${LISTE}") list(REMOVE_DUPLICATES LISTE) message("[APRES] LISTE = ${LISTE}")
Résultat :
[AVANT] LISTE = a;b;a [APRES] LISTE = a;b
La réponse est en partie dans la question : il faut passer par une liste. Pour la seconde partie, on passera par la fonction list et sa "commande" FIND qui renvoie l'index de l'élément sinon -1 :
set(CHOICES "poire" "pomme" "banane") set(VALUE "cerise") # Chercher si la valeur $VALUE est dans la liste CHOICES list(FIND CHOICES "${VALUE}" IN_CHOICES) if(IN_CHOICES EQUAL -1) # valeur interdite (elle n'est pas dans CHOICES) else(IN_CHOICES EQUAL -1) # valeur autorisée (elle est dans CHOICES) endif(IN_CHOICES EQUAL -1)
La liste des variables est une propriété de répertoire dans la mesure où une variable est associée à un répertoire donné. Sachant cela, on récupère cette liste par la commande get_directory_property et la propriété prédéterminée VARIABLES. Par défaut, le répertoire concerné est le répertoire de travail courant de CMake mais il peut être modifié par le paramètre DIRECTORY. Afficher ces variables et leur valeur respective :
get_directory_property(CURRENT_VARIABLES VARIABLES) foreach(CURRENT_VARNAME CURRENT_VARIABLES}) message("${CURRENT_VARNAME} = ${${CURRENT_VARNAME}}") endforeach(CURRENT_VARNAME)
Les différentes composantes du chemin d'un fichier peuvent être obtenues par la commande get_filename_component. Exemple :
get_filename_component(OUTPUT_DIRNAME "src/foo.c" PATH) # la partie répertoire du chemin original get_filename_component(OUTPUT_BASENAME "src/foo.c" NAME) # le nom du fichier, sans la partie répertoire get_filename_component(OUTPUT_EXTENSION "src/foo.c" EXT) # uniquement l'extension du fichier (point compris) get_filename_component(OUTPUT_NAME_WE "src/foo.c" NAME_WE) # le nom du fichier, sans celui du répertoire ni son extension get_filename_component(OUTPUT_ABSOLUTE "src/foo.c" ABSOLUTE) # le chemin complet/absolu du fichier message("OUTPUT_DIRNAME = ${OUTPUT_DIRNAME}") message("OUTPUT_BASENAME= ${OUTPUT_BASENAME}") message("OUTPUT_EXTENSION = ${OUTPUT_EXTENSION}") message("OUTPUT_NAME_WE = ${OUTPUT_NAME_WE}") message("OUTPUT_ABSOLUTE = ${OUTPUT_ABSOLUTE}")
Résultat :
OUTPUT_DIRNAME = src OUTPUT_BASENAME= foo.c OUTPUT_EXTENSION = .c OUTPUT_NAME_WE = foo OUTPUT_ABSOLUTE = C:/prog/cmake_tests/src/foo.c
Non. Démonstration :
function(foo) message("foo") endfunction(foo) function(FOO) message("FOO") endfunction(FOO) macro(bar) message("bar") endmacro(bar) macro(BAR) message("BAR") endmacro(BAR) foo() bar()
Résultat :
FOO BAR
Remarquer au passage que la dernière macro ou fonction déclarée remplace silencieusement la précédente.
Les variables créées au sein d'une fonction possèdent leur propre portée. Ainsi elles n'existent pas en dehors de la fonction. Une macro, similaire dans le principe à des langages comme le C (remplacement à la volée), récupère de fait la portée où elle figure (le scope global en somme). Exemple :
function(A) set(y 3) endfunction(A) macro(B) set(z 3) endmacro(B) A() B() message("y = ${y}") message("z = ${z}")
Résultat (en ajoutant l'option –warn-uninitialized à la commande cmake) :
CMake Warning (dev) at CMakeLists.txt:12: uninitialized variable 'y' This warning is for project developers. Use -Wno-dev to suppress it. y = z = 3
Notes :
CMake comprend un module standard appelé CMakeParseArguments qui fournit la fonction CMAKE_PARSE_ARGUMENTS. Son prototype est le suivant :
include(CMakeParseArguments) CMAKE_PARSE_ARGUMENTS(<prefix> <options> <one_value_keywords> <multi_value_keywords> args...)
Ses paramètres sont :
Notes :
Exemple, une fonction visant à packager des sources et éventuellement les installer :
include(CMakeParseArguments) function(create_jar) cmake_parse_arguments(PARSED_ARGS "" "PACKAGE;DESTINATION" "" ${ARGN}) if(PARSED_ARGS_PACKAGE) message("Nom de l'archive : ${PARSED_ARGS_PACKAGE}") #else(PARSED_ARGS_PACKAGE) # en définir un par défaut ? endif(PARSED_ARGS_PACKAGE) if(PARSED_ARGS_DESTINATION) message("Installation dans : ${PARSED_ARGS_DESTINATION}") #else(PARSED_ARGS_DESTINATION) # en définir un par défaut ? endif(PARSED_ARGS_DESTINATION) message("Sources devant composer cette archive :") foreach(SOURCE ${PARSED_ARGS_UNPARSED_ARGUMENTS}) message("- ${SOURCE}") endforeach(SOURCE) endfunction(create_jar) # Exemple d'utilisation create_jar(PACKAGE monAPI.jar DESTINATION share/monAPI classeA.class classeB.class classeC.class)
Très pratique. Ne pas hésiter à user et abuser de cette fonction que CMake met à notre disposition. Inutile de réinventer la roue !
Pour l'illustration nous supposerons qu'il s'agit d'un programme écrit en C (en pratique, ça pourrait être un peu n'importe quoi). Ainsi, nous définissons un modèle de fichier d'entête, nommé config.h.in ici, qui contient du code C/préprocesseur valide et des variables de la forme @VARIABLE@. Ces dernières seulement, seront remplacées par la valeur de la variable CMake éponyme lors de la création du fichier d'entête final, à utiliser dans notre code source, config.h. Voici un exemple sans grand intérêt de ce fichier config.h.in :
#define MON_APP_MAJOR_VERSION @MON_APP_MAJOR@ #define MON_APP_MINOR_VERSION @MON_APP_MINOR@ #define MON_APP_PATH_VERSION @MON_APP_PATH@
Du côté de CMake, c'est la commande configure_file qui, pour mon exemple, va créer config.h à partir de config.h.in après avoir substitué les variables (ne pas oublier de définir vos variables avant l'appel de configure_file) :
set(MON_APP_MAJOR 1) set(MON_APP_MINOR 2) set(MON_APP_PATH 3) configure_file( "config.h.in" "config.h" @ONLY )
Ne pas oublier d'inclure config.h (#include "config.h") dans les sources qui nécessite ces informations.
Deux étapes :
include(CheckIncludeFile) check_include_file("bidule.h" HAVE_BIDULE_H)
if(HAVE_BIDULE_H) add_definitions(-DHAVE_BIDULE_H=1) endif(HAVE_BIDULE_H)
#cmakedefine HAVE_BIDULE_H
configure_file( "config.h.in" "config.h" @ONLY )
La commande #cmakedefine VARIABLE (ne pas mettre d'arobases ici) sera remplacée par #define VARIABLE si du côté de CMake VARIABLE a une valeur vraie. Elle ne sera pas définie (#ifdef VARIABLE ne serait pas satisfait) si elle a une valeur fausse ou est inexistante. Pour que VARIABLE, en C, soit toujours définie et prenne une valeur 0 ou 1, remplacer #cmakedefine par #cmakedefine01.
Autres commandes utiles du même style :
check_struct_has_member("struct stat" st_blocks "sys/stat.h" HAVE_ST_BLOCKS)
Avant de commencer, il est bon de savoir que ce n'est possible que depuis CMake 2.8.8 (compris). En temps normal, si vous avez une source commune à plusieurs cibles, comme ci-dessous, celle-ci va être compilée autant de fois qu'elle est associée à de cibles différentes :
add_executable(exe1 common.c util.c exe1.c) add_executable(exe2 common.c util.c exe2.c)
Ici common.c serait compilée deux fois. Sur un projet plus important en cibles et/ou en sources partagées, on peut gagner un temps précieux en regroupant les sources communes sous la forme d'une bibliothèque dite objet (c'est ainsi que CMake le gère). On réinjecte ensuite cette "bibliothèque" dans les add_executable via une variable spéciale $<TARGET_OBJECTS:nom_bibliothèque_objet>. Mon exemple deviendrait alors :
add_library(common_util OBJECT EXCLUDE_FROM_ALL common.c util.c) add_executable(exe1 exe1.c $<TARGET_OBJECTS:common_util>) add_executable(exe1 exe1.c $<TARGET_OBJECTS:common_util>)
On peut utiliser autant de ces "bibliothèques objet" que l'on souhaite, ce qui permet de faire des petits groupes pour toujours s'assurer que tout n'est compilé qu'une seule fois. Et, bien sûr, ce système n'est pas limité à la création d'exécutables, il est tout aussi valable pour des bibliothèques (add_library). (voir le CMakeLists.txt de mon projet ugrep sur github - branche master - pour un exemple plus concret).
VC\bin\x86_amd64\vcvarsx86_amd64.bat
Notes :
dumpbin /headers <fichier.exe>