web-dev-qa-db-fra.com

add_custom_command ne génère pas de cible

C'est peut-être impossible et je lis mal la documentation cmake 3.2 , mais je pensais que la création d'une commande personnalisée créerait une "cible" personnalisée dans le Makefile afin que je puisse construire la cible en invoquant le nom de le fichier de sortie. Les documents CMake disent:

En termes de makefile, cela crée une nouvelle cible sous la forme suivante:

 OUTPUT: MAIN_DEPENDENCY DEPENDS
    COMMAND

donc j'ai pensé que je pourrais alors exécuter make OUTPUT. Peut-être que la documentation confond les cibles CMake avec les cibles Makefile?

Par exemple,

 add_custom_command(OUTPUT foo_out
    COMMAND post_process foo_in > foo_out
    DEPENDS foo_in
 )

Je voudrais faire

 make foo_out

et cela fera foo_out. Cependant, si je fais cela, je reçois

make: **** No rule to make target `foo_out`. Stop.

et bien sûr, le mot "foo_out" n'existe nulle part dans aucun fichier du répertoire de sortie binaire de cmake. Si je le change en ceci

add_custom_target(bar DEPENDS foo_out)
add_custom_command(OUTPUT foo_out COMMAND post_process foo_in > foo_out)

Alors je peux faire

make bar

et je peux faire

make foo_in

mais je ne peux toujours pas faire

make foo_out

Le problème avec make bar est qu'il n'est pas intuitif, car la sortie réelle du fichier est foo_out pas bar.

Comment puis-je faire cela?

Dans mon cas, je dois exécuter une étape de traitement spéciale vers la cible exécutable standard qui insère des ressources facultatives dans le fichier ELF. Je voudrais avoir la possibilité d'avoir les deux exécutables comme cibles Makefile, afin que je puisse construire l'exécutable ELF nu ainsi que l'exécutable ELF injecté de ressources.

Si j'écrivais un Makefile personnalisé, c'est trivial à faire!

foo_in: foo.c
    $(CC) $< -o $@

foo_out: foo_in
    post_process $< > $@   

Et je peux faire make foo_in et make foo_out.

17
Mark Lakata

add_custom_command ne pas crée une nouvelle cible. Vous devez définir explicitement les cibles par add_executable, add_library ou add_custom_target afin de les rendre visibles à faire.

Si vous devez arranger les choses pour le déploiement, vous pourriez

1. utilisez la commande install (quelque part dans votre CMakeLists.txt) comme ceci:

install(SCRIPT <dir>/post_install.cmake)

pour stocker des commandes qui ne sont exécutées que lorsque vous exécutez make install dans un fichier séparé . cmake . Ou si la cible d'installation est déjà réservée à d'autres choses ou si vous avez des choses plus complexes en cours:

2. définissez manuellement une cible de déploiement . Une fois que vous avez obtenu cela, vous pouvez créer une commande de post-construction personnalisée qui n'est exécutée que lorsque vous exécutez explicitement make sur votre déploiement cible. Cela vous permet d'exécuter des commandes via une cible distincte.

Dans votre CMakeLists.txt, cela pourrait ressembler à:

cmake_minimum_required(VERSION 3.0)

add_executable("App" <sources>)

# option 1: do deployment stuff only when installing
install(SCRIPT <dir>/post_install.cmake)

# option 2: define a deploy target and add post-build commands
add_custom_target("deploy")
add_custom_command(TARGET "deploy" POST_BUILD <some command>)

Les deux approches vous permettent de séparer les versions de développement des versions coûteuses prêtes à déployer (si je comprends bien, c'est le but ici). Je recommanderais l'option 1 car c'est juste plus propre.

J'espère que cela t'aides!

12
qCring

Documentation peu claire

La documentation de CMake n'est pas claire ici. Les générateurs Makefiles de CMake créent les règles de création du fichier source dans des sous Makefiles qui ne sont pas visibles dans le Makefile principal. Dans le Makefile principal, vous ne trouverez que les règles BIDON pour vos cibles CMake. La seule exception que je connaisse est le générateur Ninja Makefiles qui place toutes les règles de construction dans un seul fichier.

Traduire les étapes de post-traitement en CMake

D'après mon expérience - si post_process Est un script - vous devriez probablement penser à réécrire vos étapes de post-traitement avec/à l'intérieur des scripts CMake, car CMake devrait connaître toutes les dépendances de fichier et les variables utilisées pour le post-traitement ( il se chargera ensuite, par exemple, de toutes les étapes de reconstruction ou de nettoyage nécessaires pour vous).

Voici une version simplifiée/modifiée de ce que je fais:

function(my_add_elf _target)

    set(_source_list ${ARGN})
    add_executable(${_target}_in ${_source_list})

    set_target_properties(
        ${_target}_in
        PROPERTIES
            POSITION_INDEPENDENT_CODE   0
            SUFFIX                      .elf
    )

    add_custom_command(
        OUTPUT ${_target}_step1.elf
        COMMAND some_conversion_cmd $<TARGET_FILE:${_target}_in> > ${_target}_step1.elf
        DEPENDS ${_target}_in
    )

    add_custom_target(
        ${_target}_step1 
        DEPENDS 
            ${_target}_step1.elf
    )

    add_custom_command(
        OUTPUT ${_target}_out.elf
        COMMAND final_post_process_cmd ${_target}_step1.elf > ${_target}_out.elf
        DEPENDS ${_target}_step1
    )

    add_custom_target(
        ${_target}_out 
        DEPENDS 
            ${_target}_out.elf
    )

    # alias / PHONY target
    add_custom_target(${_target} DEPENDS ${_target}_out)

endfunction(my_add_elf)

puis appeler

my_add_elf(foo foo.c)

Ce n'est qu'un exemple, mais j'espère que cela donne l'idée: vous pourriez appeler make foo Pour la sortie ELF finale, make foo_in Ou make foo_step1 Pour l'une des autres étapes. Et je pense que toutes les étapes sont transparentes pour l'utilisateur et CMake.

Impossible de donner à votre cible le même nom que l'une des sorties

Lorsque vous essayez de donner à une cible personnalisée le même nom que l'une de ses sorties, par exemple comme ça:

add_executable(foo_in foo.c)
add_custom_command(
    OUTPUT foo_out
    COMMAND post_process foo_in > foo_out
    DEPENDS foo_in
)
add_custom_target(foo_out DEPENDS foo_out)

Vous vous retrouvez avec des fichiers make invalides. J'ai soulevé un problème à ce sujet dans l'espoir qu'il pourrait y avoir une solution possible en étendant CMake lui-même et obtenu la réponse suivante:

CMake n'est pas destiné à produire un contenu spécifique dans un Makefile. Les noms cibles de niveau supérieur créés par add_custom_target sont toujours des noms logiques (c'est-à-dire faux). Il n'est tout simplement pas autorisé d'avoir un fichier du même nom.

Solutions de contournement possibles

Il existe donc des solutions de contournement, mais elles présentent toutes l'un ou l'autre inconvénient.

1. Version la plus courte:

macro(my_add_elf_we _target)
    add_executable(${_target}_in ${ARGN})
    add_custom_target(
        ${_target}_out 
        COMMAND post_process $<TARGET_FILE:${_target}_in> > ${_target}_out
        DEPENDS ${_target}_in
    ) 
    set_property(DIRECTORY APPEND PROPERTY ADDITIONAL_MAKE_CLEAN_FILES ${_target}_out)
endmacro(my_add_elf_we)

Vous ne pouvez pas déclarer OUTPUTs dans la add_custom_target() elle-même, mais dans ce cas, vous ne voulez pas (parce que vous ne voulez pas avoir de confusion de noms). Mais si vous ne déclarez aucune sortie:

  • La cible sera toujours considérée comme obsolète
  • Vous devez ajouter les sorties "invisibles" à la règle de construction clean

2. Forcer la version du nom de sortie

Voici une version de la macro ci-dessus qui force les noms de cible et de sortie à des valeurs données:

macro(my_add_elf_in_out _target_in _target_out)
    add_executable(${_target_in} ${ARGN})
    set_target_properties(
        ${_target_in}
        PROPERTIES
            SUFFIX          ""
            OUTPUT_NAME     "${_target_in}"
    )
    add_custom_target(
        ${_target_out}
        COMMAND post_process ${_target_in} > ${_target_out}
        DEPENDS ${_target_in}
    ) 
    set_property(DIRECTORY APPEND PROPERTY ADDITIONAL_MAKE_CLEAN_FILES ${_target_out})
endmacro(my_add_elf_in_out)

Vous l'appelez avec:

my_add_elf_in_out(foo_in.elf foo_out.elf foo.c)

3. Version Libary de l'objet

La version suivante utilise des bibliothèques d'objets, mais le système ne réutilisera pas la liaison cible foo_in:

macro(my_add_elf_obj_in_out _target_in _target_out)

    add_library(${_target_in}_obj OBJECT ${ARGN})

    add_executable(${_target_in} $<TARGET_OBJECTS:${_target_in}_obj>)
    set_target_properties(
        ${_target_in}
        PROPERTIES
            SUFFIX          ""
            OUTPUT_NAME     "${_target_in}"
    )

    add_executable(${_target_out} $<TARGET_OBJECTS:${_target_in}_obj>)
    set_target_properties(
        ${_target_out}
        PROPERTIES
            SUFFIX              ""
            OUTPUT_NAME         "${_target_out}"
            EXCLUDE_FROM_ALL    1
    )
    add_custom_command(
        TARGET ${_target_out}
        POST_BUILD
        COMMAND post_process ${_target_in} > ${_target_out}
    )

endmacro(my_add_elf_obj_in_out)

4. Dernière et dernière version

Et une version finale qui définitivement ne fonctionne qu'avec les générateurs Makefile et qui m'a fait publier le problème sur le tracker de bogues de CMake:

macro(my_add_elf_ext_in_out _target_in _target_out)

    add_executable(${_target_in} ${ARGN})
    set_target_properties(
        ${_target_in}
        PROPERTIES
            SUFFIX          ""
            OUTPUT_NAME     "${_target_in}"
    )
    add_executable(${_target_out} NotExisting.c)
    set_source_files_properties(
        NotExisting.c
        PROPERTIES
            GENERATED           1
            HEADER_FILE_ONLY    1
    )
    set_target_properties(
        ${_target_out}
        PROPERTIES
            SUFFIX              ""
            OUTPUT_NAME         "${_target_out}"
            RULE_LAUNCH_LINK    "# "
    )
    add_custom_command(
        TARGET ${_target_out}
        POST_BUILD
        COMMAND post_process ${_target_in} > ${_target_out}
    )
    add_dependencies(${_target_out} ${_target_in})

endmacro(my_add_elf_ext_in_out)

Quelques références

7
Florian

Contourner les dépendances et utiliser la deuxième signature de add_custom_command , cela devrait fonctionner:

add_custom_target(foo_out DEPENDS foo_in)
add_custom_command(TARGET foo_out POST_BUILD COMMAND post_process foo_in > foo_out)

Remarque: Ajout de BYPRODUCTS foo_out fera (par exemple) ninja dire

plusieurs règles génèrent foo_out. les versions impliquant cette cible ne seront pas correctes; continue quand même

1
Antonio