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();