2 votes

CMake génère des dépendances circulaires pour le projet VS, mais pas les fichiers make. Comment l'éviter ?

Cet échantillon est tiré d'un projet sur lequel je travaille actuellement, une sorte de MWE (bien, no travailler serait plus approprié).

Considérez la structure de projet suivante :

.
 CMakeLists.txt
 build
 include
    hello.hpp
 src
     hello.cpp

Le site build est utilisé pour effectuer la construction et contient les artefacts de construction et les fichiers intermédiaires. Le contenu des autres fichiers sera indiqué au bas de cette question.

L'idée est de install() la bibliothèque construite ainsi que son en-tête (et d'autres choses dans le projet réel).

add_custom_command(
    TARGET ${PRJNAME}
    POST_BUILD
        COMMAND ${CMAKE_COMMAND} --build . --target install
    WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
    VERBATIM
)

... ainsi que le install() a pour but d'accomplir cela et le fait pour les générateurs Unix Makefiles y NMake Makefiles mais crée une dépendance circulaire pour Visual Studio 16 2019 (également pour Visual Studio 14 2015 mais je n'ai pas testé plus que cela).

Maintenant, ma réaction immédiate était que c'était une incohérence entre les générateurs, mais d'un autre côté, la génération de projets avec autre chose que des fichiers make a été - IMO - une des faiblesses de CMake depuis le début.

Pour l'instant, ma solution de contournement consiste simplement à utiliser la fonction NMake Makefiles mais l'inconvénient de cette approche est que je dois "détecter" Visual Studio avant de l'utiliser. vcvarsall.bat , vcvars32.bat et ses amis. Mais c'est une petite nuisance par rapport à la dépendance circulaire.

Comment puis-je utiliser l'un des générateurs de projet de Visual Studio pour obtenir ce que je veux et éviter cette dépendance circulaire ?

Sous Windows, j'utilise CMake 3.15.1 (les plus récentes au moment de la rédaction).

NB : Je réalise que je pourrais utiliser quelque chose comme ${CMAKE_COMMAND} --build . --target install en créant des commandes/cibles personnalisées saupoudrées de quelques file() les commandes. Mais ça ne l'empêcherait pas SEC maintenant, n'est-ce pas ?


CMakeLists.txt

set(CMAKE_RULE_MESSAGES OFF)
set(CMAKE_VERBOSE_MAKEFILE ON)

cmake_minimum_required(VERSION 3.12 FATAL_ERROR)
set(PRJNAME FOOBAR)

project (${PRJNAME})

set(SOURCE_DIR "src")
set(HEADER_DIR "include")
set(TARGET_DIR "${CMAKE_CURRENT_BINARY_DIR}/install-target")
set(PUBLIC_HEADER "${HEADER_DIR}/hello.hpp")
set(PROJ_SOURCES "${SOURCE_DIR}/hello.cpp")

add_library(${PRJNAME} SHARED ${PROJ_SOURCES})

list(TRANSFORM PUBLIC_HEADER PREPEND "${CMAKE_CURRENT_BINARY_DIR}/" OUTPUT_VARIABLE PUBLIC_HEADER_PP)
set_target_properties(
    ${PRJNAME}
    PROPERTIES
        CXX_STANDARD 11
        CXX_EXTENSIONS OFF
        CXX_STANDARD_REQUIRED ON
        PUBLIC_HEADER "${PUBLIC_HEADER_PP}"
        POSITION_INDEPENDENT_CODE 1
)

set(CMAKE_INSTALL_PREFIX ${TARGET_DIR})
set(CMAKE_INSTALL_LIBDIR lib)
set(CMAKE_INSTALL_INCLUDEDIR ${HEADER_DIR})

install(
    TARGETS ${PRJNAME}
    ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
    LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
    RUNTIME DESTINATION ${CMAKE_INSTALL_LIBDIR}
    PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
)

file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/${HEADER_DIR}")
configure_file("${PUBLIC_HEADER}" "${CMAKE_CURRENT_BINARY_DIR}/${HEADER_DIR}/" COPYONLY)
include_directories("${CMAKE_CURRENT_BINARY_DIR}/${HEADER_DIR}")

add_custom_command(
    TARGET ${PRJNAME}
    POST_BUILD
        COMMAND ${CMAKE_COMMAND} --build . --target install
    WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
    VERBATIM
)

set(PKGNAME "foobar-package.zip")

add_custom_command(
    OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${PKGNAME}
        COMMAND ${CMAKE_COMMAND} -E tar cv "${CMAKE_CURRENT_BINARY_DIR}/${PKGNAME}" -- .
    WORKING_DIRECTORY "${TARGET_DIR}"
    VERBATIM
)

set_property(DIRECTORY APPEND PROPERTY ADDITIONAL_MAKE_CLEAN_FILES ${CMAKE_CURRENT_BINARY_DIR}/${PKGNAME})

add_custom_target(
    lib
    DEPENDS ${PRJNAME}
)

src/hello.cpp

#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>

BOOL APIENTRY DllMain(HMODULE, DWORD, LPVOID) { return TRUE; }
#endif
#include "hello.hpp"

int hello() { return 42; }

include/hello.cpp

#pragma once

#ifdef _WIN32
#   ifdef FOOBAR_EXPORTS
#       define FOOBAR_API  __declspec(dllexport)
#   else
#       define FOOBAR_API  __declspec(dllimport)
#   endif
#else
#   define FOOBAR_API
#endif

FOOBAR_API int hello();

2voto

teivaz Points 147

Il me semble que votre cas d'utilisation est au-delà de ce pour quoi CMake a été conçu. Vous essayez de construire le INSTALL qui, à son tour, construit la cible FOOBAR qui, à son tour, appelle la cible INSTALL commandement.

Si nous brisons la chaîne ici en nous séparant en deux cibles différentes : FOOBAR y FOOBAR_INSTALL comme ça

add_custom_target(${PRJNAME}_INSTALL ALL
    ${CMAKE_COMMAND} --build . --target install
    WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
    VERBATIM
)

nous obtiendrions toujours la référence circulaire par le biais de la ALL_BUILD cible. Donc si on enlève le ALL la référence sera rompue mais la cible ne sera pas appelée par la commande de construction par défaut, ce qui n'est pas mieux que d'appeler make y make install .

L'endroit approprié pour rompre la dépendance serait entre les éléments suivants INSTALL et d'autres cibles. Au lieu de construire le INSTALL vous pouvez appeler la cible générée cmake_install.cmake script directement :

add_custom_command(
    TARGET ${PRJNAME}
    POST_BUILD
        COMMAND ${CMAKE_COMMAND} -D CMAKE_INSTALL_CONFIG_NAME=$<CONFIG> -P cmake_install.cmake
    WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
    VERBATIM
)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X